Profile picture

[PySide6] 다중 윈도우(서브창) 기능 구현해보기!

JaehyoJJAng2024년 09월 25일

개요

Image

이번 게시글에서는 PySide6와 Qt Designer를 이용하여

여러 개의 윈도우(메인 창, 서브 창1, 서브 창2)를 단계적으로 띄우는 프로그램을 구현해보려고 한다!


목표

  • 1. 메인 윈도우가 프로그램 실행 시, 모니터 왼쪽, 수직 중앙에 위치하도록 함.
  • 2. 서브 창1(SubWindow1)을 메인 윈도우 오른쪽에 열기
  • 3. 서브 창2(SubWindow2)는 서브 창1의 오른쪽에 열기
  • 4. 프로그램 종료 시(메인 윈도우 닫을 때), 열려 있는 모든 서브 창도 자동으로 닫혀야 함.

개발 환경 준비

1. Qt Designer에서 .ui 파일을 만든 뒤 pyside6-uic 또는 pyuic을 사용하여 .py 파일로 변환시키기


2. 디렉터리 구조 예시는 다음과 같다.

my_project/
├─ main.py
├─ main_layout/
│   ├─ main_ui.py           # QtDesigner로부터 변환된 메인 UI
├─ sub1_layout/
│   ├─ sub_window1_ui.py    # QtDesigner로부터 변환된 창1 UI
├─ sub2_layout/
│   ├─ sub_window2_ui.py    # QtDesigner로부터 변환된 창2 UI

코드 구현 예시

핵심 포인트!

  • 메인 윈도우, 서브 창1, 서브 창2 모두 QWidget 상속
  • 서브 창을 열 때, parent=상위객체로 설정하여 부모-자식 관계 를 만들도록 함.
    • 이렇게 해야 메인 창이 닫힐 때 서브 창들이 자동으로 정리됨.
  • 각 창의 위치는 move(x,y) 메소드를 통해 자유롭게 조정하도록 함.

import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtGui import QScreen

# QtDesigner로부터 변환된 UI 모듈 임포트
from main_layout.main_ui import Ui_Form as MainUI
from sub1_layout.sub_window1_ui import Ui_Form as Sub1UI
from sub2_layout.sub_window2_ui import Ui_Form as Sub2UI


class MainAPP(QWidget, MainUI):
    """메인 윈도우 클래스"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        # 1) 메인 윈도우 크기 지정
        self.resize(400, 300)

        # 2) 메인 윈도우 위치를 모니터 '왼쪽 중앙'에 배치
        #    (참고) Qt에서 스크린 정보를 가져오려면 QScreen 사용
        screen_geo = QApplication.primaryScreen().availableGeometry()
        screen_width = screen_geo.width()
        screen_height = screen_geo.height()

        # x 좌표: 왼쪽에서 10px 정도 띄운다
        main_x = 10
        # y 좌표: 모니터 중앙 (화면 높이 - 윈도우 높이) / 2
        main_y = (screen_height - self.height()) // 2
        self.move(main_x, main_y)

        # 3) 창1에 대한 참조 변수
        self.sub_window1 = None

        # 4) 메인 UI의 QToolButton 클릭 시 창1 열기
        self.openSub1.clicked.connect(self.show_subwindow1)

    def show_subwindow1(self):
        """메인 윈도우 오른쪽에 창1을 띄운다."""
        # 창1이 없거나, 이미 닫혀있다면 새로 생성
        if self.sub_window1 is None or not self.sub_window1.isVisible():
            self.sub_window1 = SubWindow1(self)  # parent=self
            self.sub_window1.show()

            # 메인 윈도우 오른쪽 + 10px 정도 띄운 위치
            main_geo = self.geometry()
            sub1_x = main_geo.x() + main_geo.width() + 10
            sub1_y = main_geo.y()
            self.sub_window1.move(sub1_x, sub1_y)
        else:
            # 이미 창1이 떠 있다면 최상위로 올려주기
            self.sub_window1.raise_()
            self.sub_window1.activateWindow()


class SubWindow1(QWidget, Sub1UI):
    """서브 창1 클래스: 여러 버튼이 있고, 클릭 시 창2를 띄움"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        # 창2를 가리킬 참조 변수
        self.sub_window2 = None

        # 예시: sub_window1_ui.py 안에 buttonA, buttonB 버튼이 있다고 가정
        self.buttonA.clicked.connect(lambda: self.show_subwindow2("A"))
        self.buttonB.clicked.connect(lambda: self.show_subwindow2("B"))

    def show_subwindow2(self, button_type):
        """창1의 특정 버튼 클릭 시 창2 띄우기
           이미 창2가 열려 있다면 닫고 새로 연다.
        """
        # 기존 창2가 열려 있다면 닫는다
        if self.sub_window2 is not None:
            self.sub_window2.close()
            self.sub_window2 = None

        # 창2 생성 (부모 = self)
        self.sub_window2 = SubWindow2(button_type, self)
        self.sub_window2.show()

        # 창1 오른쪽에 창2 배치
        geo = self.geometry()
        sub2_x = geo.x() + geo.width() + 10
        sub2_y = geo.y()
        self.sub_window2.move(sub2_x, sub2_y)


class SubWindow2(QWidget, Sub2UI):
    """서브 창2 클래스: 창1에서 클릭된 버튼에 따라 동작 달라짐"""
    def __init__(self, button_type, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        # button_type 이 A냐 B냐에 따라 타이틀(or 기능) 분기 처리
        if button_type == "A":
            self.update_button_type_A()
        elif button_type == "B":
            self.update_button_type_B()
    
    def update_button_type_A(self) -> None:
        self.setWindowTitle("창2 - A 버전")

    def update_button_type_B(self) -> None:
        self.setWindowTitle("창2 - B 버전")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainAPP()
    window.show()
    sys.exit(app.exec())

Loading script...