본문 바로가기
목차
Python/PySide PyQt

[PySide6] QtUiTools.QUiLoader 를 Qt Designer의 .ui 사용하기.

by ds31x 2024. 5. 6.
728x90
반응형

QtUiTools.QUiLoader : 

QUiLoader
Qt 프레임워크에서 Qt Designer 로 생성된 .ui 파일을
runtime(런타임)에서 로딩하여 widget으로 생성하는 역할을 수행함.

  • .ui 파일들은 Qt Designer라는 도구를 사용하여 XML 형식으로 만들어졌으며,
  • 이 파일들은 사용자 인터페이스의 레이아웃과 속성을 정의하고 있음.

QUiLoader는 PySide와 PyQt 모두 QtUiTools 모듈에서 지원하고 있음.

  • 참고로, PyQt에서는 uic 모듈의 loadUiType 또는 loadUi 함수를 이용하는 방법이 보다 많이 사용됨
  • PySide에 비해 PyQt가 이전에는 훨씬 많이 사용되었기 때문에, uic.loadUi 또는 uic.loadUiType를 이용한 예제나 구현 코드가 보다 많음.
  • 하지만, 이는 PySide에서 지원하지 않는 방법임에 주의할 것.

 

QUiLoader의 사용은 "code를 통한 직접적인 인터페이스 구현 방식"에 비해 다음의  다음의 장점을 가짐.

  1.  Qt의 Designer를 사용하여 WYSIWYG 로 보다 직관적인 개발이 가능함.
  2. 이는 개발 과정을 간소화시킴.
  3. 인터페이스의 변경이 프로그램 코드를 수정하지 않고도 가능하게 함 (Desinger 를 이용한.ui 파일만 재작성)
  4. 이는 전체적인 유지 보수의 편리성을 제공할 수 있음.

정적으로 ui 파일 사용하기

참고로, QUiLoader를 통해 runtime에 .ui를 로드하여 사용하는 방법 외에도 "정적으로 Qt GUI Application에서 .ui 파일을 적용하는 방법"도 있음.

 

uic를 통한 정적으로 적용하는 방법은 다음과 같음: 

  • pyside6-uic command line program을 이용하여
  • .ui 파일을 Python code로 변환하고,
  • 해당 code에 정의된 widget 클래스를 상속하는 방식을 통해
  • Qt GUI Application 에서 정적으로 사용하는 방법도 있음.
pyside6-uic design.ui -o ui_design.py

 

2024.05.07 - [Python/PySide PyQt] - [PySide6] pyside6-uic 사용하기

 

[PySide6] pyside6-uic 사용하기

pyside6-uicPySide6 프레임워크에서 지원하는 도구로 .ui 파일을 Python code 파일로 변환하는 컴파일러임 (User Interface Compiler, uic) 사용법터미널로 해당 directory로 이동terminal에서 변환하고자 하는 .ui 파

ds31x.tistory.com

 


동적으로 ui 파일 사용법

  1. QUiLoader 객체를 만들고,
  2. load() 메소드를 호출하여 .ui 파일에 해당하는 QFile 객체를 넘겨주면,
  3. 이 메소드는 해당하는 .ui 파일을 읽고 이를 통해 widget을 동적으로 생성하여 반환함.
  4. 동적으로 생성된 widget은 바로 애플리케이션에 통합되어 사용될 수 있음.

주의할 점:

Python의 open()으로 연 파일 객체
QUiLoader.load()의 argument로 직접 사용할 수 없음.

 

왜냐하면 QUiLoader.load()는 첫 번째 인자로 Qt의 QIODevice 타입을 요구하고,

Python의 open() 함수가 반환하는 객체는 QIODevice를 상속받지 않기 때문임.

 

아래 Example의 예제 코드에서 Case 3를 사용하여 에러를 확인할 수 있음.


Example

다음의 Python code snippet은

  • PySide6 라이브러리를 사용하여
  • Qt Designer를 통해 생성된 .ui파일을 이용하여
  • 간단한 GUI(Graphical User Interface) 어플리케이션을 생성하는 방법을 보여줌.

아래의 QLineEdit 객체에서 문자열을 입력하고 엔터를 누르면, 위의 QLabel 객체의 text이 Hello <입력된 문자열>로 갱신됨.

Ex1_QtDesigner.ui
0.00MB


아래 이미지 참고.

이는

  • QtUiTools.QUiLoader를 사용하여 Qt Designer가 생성한 .ui파일에 해당하는 widget을 만들고
  • 이를 이용하여 GUI Application을 만드는 방법을 보여줌.
  • uic를 이용하는 경우엔 상속이 이용되는 것과 달리  자신의 attribute로 사용하는 차이점을 가짐 (has-a relation을 이용).
import os
import sys

# PySide6에서 필요한 클래스들 import
from PySide6.QtWidgets import (
    QApplication,      # 어플리케이션 객체
    QMainWindow,       # 메인 윈도우 프레임
    QLineEdit,         # 한 줄 텍스트 입력 위젯
    QLabel             # 텍스트 표시 위젯
)
from PySide6.QtUiTools import QUiLoader     # .ui 파일을 런타임에 로드하는 도구
from PySide6.QtCore import QFile            # Qt 파일 입출력을 위한 클래스


# 메인 윈도우 클래스 정의
class MainWindow(QMainWindow):
    def __init__(self, ui_fstr):
        super().__init__()

        # UI 파일(.ui)로부터 위젯 트리를 생성하여 중앙 위젯으로 설정
        self.wnd = self.ds_get_wnd_from_ui(ui_fstr)

        # 위젯 참조를 찾아서 시그널-슬롯 연결
        self.ds_setup()

        # 생성된 위젯을 QMainWindow의 중앙에 배치
        self.setCentralWidget(self.wnd)
        self.show()

    # UI 내부 위젯에 접근하고 이벤트를 연결하는 함수
    def ds_setup(self):
        # objectName으로 QLineEdit와 QLabel 찾기
        self.lineEdit = self.wnd.findChild(QLineEdit, "lineEdit")
        self.label = self.wnd.findChild(QLabel, "label")

        # UI에 필수 위젯이 없다면 오류 발생
        # 누락된 위젯들 확인
        missing_widgets = []
        if not self.lineEdit:
            missing_widgets.append("lineEdit")
        if not self.label:
            missing_widgets.append("label")
        
        if missing_widgets:
            raise AttributeError(f"UI에 다음 위젯들이 없습니다: {', '.join(missing_widgets)}")
        

        # returnPressed 시 라벨 업데이트
        self.lineEdit.returnPressed.connect(self.ds_update_label)

    # 입력된 텍스트를 라벨에 표시하는 슬롯
    def ds_update_label(self):
        self.label.setText(f'Hello, {self.lineEdit.text()}')

    # .ui 파일을 열어서 QWidget으로 로딩하는 함수
    def ds_get_wnd_from_ui(self, ui_fstr):
        ui_loader = QUiLoader()

        # 현재 파일의 디렉토리 기준으로 .ui 파일 경로 구성
        root_dir = os.path.dirname(os.path.abspath(__file__))
        ui_path = os.path.join(root_dir, ui_fstr)

        # QFile 객체로 .ui 파일 준비
        ui_file = QFile(ui_path)
        if not ui_file.exists():
            raise FileNotFoundError(f"UI 파일이 존재하지 않습니다: {ui_path}")

        # 파일 열고 로드
        if not ui_file.open(QFile.ReadOnly):
            raise IOError(f"UI 파일을 열 수 없습니다: {ui_path}")

        try:
            wnd = ui_loader.load(ui_file, self)  # parent를 self로 설정
            if wnd is None:
                raise RuntimeError(f"UI 파일 로딩 실패: {ui_path}")
            return wnd
        finally:
            ui_file.close()  # 예외 발생해도 반드시 닫힘


# 애플리케이션 진입점
if __name__ == '__main__':
    app = QApplication(sys.argv)                 # Qt 애플리케이션 생성
    window = MainWindow('Ex1_QtDesigner.ui')     # 메인 윈도우 클래스 인스턴스 생성
    sys.exit(app.exec())                         # 이벤트 루프 실행
728x90