PySide Qt for Python Qt Designer 프로모트 위젯 커스텀 위젯 등록 디자이너 플러그인 완벽 가이드
🚀 실무 예제로 배우는 디자이너·코드생성·프로모트·플러그인까지 한 번에 정리
UI를 빠르게 만들고 유지보수까지 수월하게 하려면 도구의 흐름을 통째로 이해하는 편이 훨씬 효율적입니다.
프로젝트가 커질수록 .ui 파일로 설계를 관리하고, 필요할 때 코드로 변환하며, 공통 기능은 커스텀 위젯으로 재사용하는 방식이 체감 성능을 높여 줍니다.
특히 PySide(Qt for Python) 환경에서는 Qt Designer로 드래그 앤 드롭으로 화면을 만들고, 프로모트 위젯으로 사용자 정의 클래스를 설계 단계에서 바로 대체하며, 디자이너 플러그인으로 팀 전반의 위젯 자산을 표준화할 수 있습니다.
이 글은 초보와 숙련 모두를 염두에 두고, 헷갈리기 쉬운 개념을 명확한 용어와 사례 중심으로 풀어갑니다.
복잡한 UI를 다루다 보면 동일한 패턴이 반복되고, 디자이너에서 보이지 않는 커스텀 요소를 코드로만 붙이느라 시간이 새기 쉽습니다.
프로모트 위젯을 활용하면 설계 화면에서 곧바로 자신의 위젯으로 치환해 미리 배치·레이아웃·속성까지 확인할 수 있고, 필요 시 디자이너 플러그인으로 아예 위젯 박스에 등록해 협업 품질을 일정하게 유지할 수 있습니다.
또한 코드생성 흐름을 이해해 둬야 런타임 로딩과 정적 변환 사이에서 적합한 전략을 선택할 수 있습니다.
이 글의 목차를 따라가며 개념, 작업 순서, 주의점, 검증 체크리스트까지 차근차근 정리해 보겠습니다.
📋 목차
🔗 PySide Qt Designer와 코드생성 개요
PySide(Qt for Python)에서 UI 개발은 Qt Designer로 시각 설계를 하고, .ui 파일을 그대로 로드하거나 Python 코드로 변환해 사용하는 방식으로 진행합니다.
팀 협업에서는 시각 파일을 버전 관리하기 쉽고, 유지보수에서는 위젯 속성의 변경을 빠르게 반영할 수 있다는 장점이 있습니다.
여기에 프로모트 위젯(커스텀 위젯 등록)과 디자이너 플러그인을 더하면 재사용성과 설계 일관성이 커집니다.
개발 흐름의 큰 줄기는 두 가지이며, 프로젝트 성격에 따라 선택하거나 혼합합니다.
🧭 런타임 로드 방식과 정적 코드생성 방식
| 방식 | 핵심 아이디어 |
|---|---|
| 런타임 로드 | .ui 파일을 실행 중에 로드해 위젯 트리를 구성합니다. 설계 변경이 잦을 때 유리합니다. |
| 정적 코드생성 | pyside6-uic로 .ui를 .py로 변환합니다. 런타임 의존이 줄고 로딩이 단순해집니다. |
⚙️ 런타임 로드 예시
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtUiTools import QUiLoader
from PySide6.QtCore import QFile
app = QApplication([])
ui_file = QFile("forms/main_window.ui")
ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()
window.show()
app.exec()
💡 TIP: 리소스(.qrc)는 pyside6-rcc로 .py로 변환해 import하면 배포 시 파일 경로 문제를 줄일 수 있습니다.
🛠️ 정적 코드생성 예시
# .ui -> .py 변환
# 터미널
pyside6-uic forms/main_window.ui -o ui_main_window.py --from-imports
# 사용 예
from PySide6.QtWidgets import QApplication, QMainWindow
from ui_main_window import Ui_MainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
app = QApplication([])
w = MainWindow()
w.show()
app.exec()
⚠️ 주의: 생성된 ui_*.py 파일은 수정하지 않는 것이 원칙입니다.
수정이 필요하면 상속으로 확장하거나 .ui를 업데이트해 재생성합니다.
PySide에서 프로모트 위젯을 사용하면 Designer 단계에서 기본 QWidget 자리를 자신이 만든 파이썬 클래스명으로 치환해 배치와 속성을 즉시 확인할 수 있습니다.
정적 코드생성 시에는 해당 커스텀 클래스를 import 가능한 경로에 두고, 런타임 로드 시에는 로더가 참조할 수 있도록 모듈을 준비합니다.
반복적으로 쓰는 공통 위젯은 디자이너 플러그인으로 등록해 위젯 박스에서 바로 끌어다 쓰면 설계 일관성과 생산성이 크게 올라갑니다.
- 🧭프로젝트에 적합한 방식 선택: 런타임 로드 또는 코드생성
- 🗂️.ui, 리소스(qrc), 커스텀 위젯 모듈의 모듈 경로와 import 정합성 점검
- 🧱반복되는 UI는 프로모트 위젯으로 설계 단계에서부터 재사용
- 🔌팀 표준 위젯은 디자이너 플러그인으로 등록해 일관성 확보
💬 규모가 커질수록 .ui 유지보수 용이성과 런타임 의존성 사이의 균형이 중요합니다.
초기에는 런타임 로드로 빠르게 반복하고, 안정화 단계에서 코드생성으로 고정하는 전략이 실무에서 자주 쓰입니다.
🧱 프로모트 위젯 개념과 필요성
PySide나 PyQt에서 프로모트 위젯(Promote Widget)은 디자이너에서 기본 QWidget을 자신이 만든 커스텀 클래스와 연결해주는 기능입니다.
예를 들어 CustomButton, ChartWidget, LogViewer 등 특별한 기능을 가진 위젯을 매번 코드로 추가하지 않고, 디자이너 단계에서 직접 배치할 수 있도록 해줍니다.
이 방식은 설계와 코드 간의 연결을 한층 자연스럽게 만들어주며, UI 수정 시 코드 구조를 건드리지 않아도 되기 때문에 유지보수성이 매우 높습니다.
🎯 프로모트 위젯의 작동 원리
Qt Designer는 자체적으로 Custom Widget 클래스의 존재를 모르기 때문에, 기본 위젯(QFrame, QWidget 등)을 배치해 둔 후 Promote to… 기능으로 커스텀 클래스로 치환합니다.
이때 설정해야 할 항목은 두 가지입니다.
첫째, Promoted class name에는 커스텀 클래스명을 입력합니다.
둘째, Header file에는 파이썬 기준으로 해당 모듈의 경로를 작성해야 합니다.
예를 들어 widgets/custom_button.py에 정의되어 있다면 Header file에는 widgets.custom_button이라고 적습니다.
🧩 예시: CustomButton을 프로모트하는 절차
- 🧱디자이너에서 QPushButton을 배치합니다.
- 🔧오른쪽 클릭 → Promote to… 선택합니다.
- 🪄Promoted class name: CustomButton
- 📂Header file: widgets.custom_button
- ✅“Add” 클릭 후 “Promote” 적용
이제 디자이너에서 버튼을 클릭하면 객체 속성에 CustomButton이 표시되며, UI에서 해당 위젯이 실제 클래스와 연결된 상태로 코드가 생성됩니다.
런타임 시에도 CustomButton 클래스가 import 가능한 경로에 있다면 정상적으로 인스턴스화됩니다.
# widgets/custom_button.py
from PySide6.QtWidgets import QPushButton
class CustomButton(QPushButton):
def __init__(self, text="", parent=None):
super().__init__(text, parent)
self.setStyleSheet("background-color: #8B6E63; color: white; border-radius: 6px;")
self.clicked.connect(self.on_click)
def on_click(self):
print("Custom button clicked!")
💎 핵심 포인트:
프로모트 위젯은 UI 설계와 코드 간의 간극을 없애줍니다.
UI 파일에서 직접 커스텀 위젯을 배치할 수 있게 만들어, 디자이너 미리보기와 코드 실행 결과의 차이를 최소화합니다.
💬 Qt Designer는 프로모트된 클래스의 속성을 미리 읽지 않기 때문에, 시각적으로 완벽히 표시되지 않을 수도 있습니다.
그러나 런타임에서는 올바르게 동작합니다.
디자이너 플러그인을 제작하면 이 미리보기 한계를 해결할 수 있습니다.
🧩 커스텀 위젯 등록 절차와 주의점
PySide에서 커스텀 위젯 등록은 디자이너의 프로모트 기능과 코드 import 경로의 일치가 핵심입니다.
위젯의 Python 모듈 위치, 클래스 이름, 디자이너의 설정값이 모두 일관되어야 런타임 에러 없이 UI가 올바르게 렌더링됩니다.
특히 모듈 경로가 잘못되면 “module not found” 오류가 발생하거나, 디자이너 미리보기에서 위젯이 표시되지 않는 문제가 생깁니다.
📁 올바른 디렉터리 구조 예시
project_root/
├── forms/
│ └── main_window.ui
├── ui/
│ └── ui_main_window.py
├── widgets/
│ ├── __init__.py
│ └── custom_button.py
└── main.py
이 구조에서는 main.py가 프로젝트의 실행 진입점이며, widgets 폴더가 커스텀 위젯 모듈을 담는 공간입니다.
Promote 설정 시 Header file에는 widgets.custom_button을, 클래스명에는 CustomButton을 지정해야 합니다.
⚠️ 프로모트 후 발생하는 일반 오류
| 오류 메시지 | 원인 및 해결 방법 |
|---|---|
| ImportError: No module named ‘widgets.custom_button’ | 프로젝트 루트가 잘못 설정되었거나 PYTHONPATH에 누락. 실행 파일의 위치를 루트로 지정하거나 sys.path 수정 필요. |
| AttributeError: type object ‘CustomButton’ has no attribute ‘__init__’ | 클래스 선언 오류 또는 import 순환 문제. 모듈을 분리하거나 클래스명을 점검. |
| 디자이너 미리보기에서 흰 사각형만 표시됨 | PySide는 기본적으로 커스텀 위젯을 렌더링하지 않습니다. 디자이너 플러그인 제작으로 해결 가능합니다. |
🧠 PYTHONPATH 설정 팁
PySide 프로젝트에서 import 오류가 자주 나는 이유는 모듈 경로 기준이 명확하지 않기 때문입니다.
Windows에서는 실행 시 다음과 같이 루트 경로를 추가할 수 있습니다.
import sys, os
sys.path.append(os.path.dirname(__file__)) # 현재 디렉터리를 루트로 추가
💡 TIP: IDE 환경 설정에서도 프로젝트 루트를 PYTHONPATH에 등록해 두면, 디자이너 미리보기와 실행 환경의 경로가 달라져 생기는 문제를 예방할 수 있습니다.
💎 핵심 포인트:
커스텀 위젯은 단순히 모듈을 추가하는 것 이상의 관리가 필요합니다.
Promote 설정값, 클래스 정의, import 경로, PYTHONPATH 구조까지 모두 맞아야 정상 작동합니다.
💬 PySide 프로젝트는 구조가 깔끔할수록 유지보수가 쉬워집니다.
ui, widgets, resources 폴더를 분리해 관리하면 이후 디자이너 플러그인 제작 및 배포에도 그대로 활용할 수 있습니다.
🔌 디자이너 플러그인 제작과 설치
PySide(Qt for Python)에서 디자이너 플러그인(Designer Plugin)은 커스텀 위젯을 Qt Designer의 위젯 박스에 직접 등록하여 시각적으로 사용할 수 있게 만드는 확장 모듈입니다.
기존에는 단순히 Promote Widget으로만 커스텀 클래스를 연결했지만, 플러그인을 제작하면 디자이너 미리보기에서도 해당 위젯의 스타일과 동작을 바로 확인할 수 있습니다.
팀 단위 개발이나 공용 컴포넌트 라이브러리를 만들 때 매우 유용한 기능입니다.
🧠 플러그인의 기본 구조
Qt Designer 플러그인은 일반적으로 QPyDesignerCustomWidgetPlugin 클래스를 상속하여 제작합니다.
이 클래스는 위젯의 이름, 모듈 경로, 위젯 인스턴스를 반환하는 createWidget() 메서드를 포함해야 하며, icon(), group() 등을 오버라이드하여 Designer 내에서의 표시 형태를 정의합니다.
# widgets/custom_button_plugin.py
from PySide6.QtDesigner import QPyDesignerCustomWidgetPlugin
from PySide6.QtGui import QIcon
from widgets.custom_button import CustomButton
class CustomButtonPlugin(QPyDesignerCustomWidgetPlugin):
def __init__(self, parent=None):
super().__init__(parent)
self.initialized = False
def initialize(self, core):
if self.initialized:
return
self.initialized = True
def isInitialized(self):
return self.initialized
def createWidget(self, parent):
return CustomButton("플러그인 버튼", parent)
def name(self):
return "CustomButton"
def group(self):
return "Custom Widgets"
def icon(self):
return QIcon()
def toolTip(self):
return "디자이너에서 사용할 수 있는 사용자 정의 버튼"
def whatsThis(self):
return "CustomButton Plugin"
def isContainer(self):
return False
def includeFile(self):
return "widgets.custom_button"
위 코드에서 includeFile() 메서드는 PySide가 위젯을 로드할 때 참조할 Python 모듈을 지정합니다.
Designer는 이 정보를 사용하여 위젯을 생성하고 속성 편집창에 표시합니다.
🧩 설치 경로 및 로드 설정
Qt Designer는 플러그인을 다음 경로에서 자동으로 검색합니다.
Windows: C:\Users\<사용자명>\AppData\Roaming\Python\QtDesigner\plugins\python
macOS/Linux: ~/.local/share/QtDesigner/plugins/python
해당 폴더에 플러그인 파일(*.py)을 복사하면 Qt Designer 실행 시 자동으로 인식합니다.
플러그인 그룹은 group() 메서드에서 지정한 이름으로 위젯 박스에 표시됩니다.
💡 TIP: 여러 커스텀 위젯을 한 플러그인 파일에 등록할 수도 있습니다.
각 위젯마다 QPyDesignerCustomWidgetPlugin을 상속한 클래스를 개별로 정의하면 됩니다.
⚠️ 주의: Qt Designer는 Python 버전과 PySide 빌드 버전에 매우 민감합니다.
Designer 실행 환경과 플러그인 제작 환경이 일치하지 않으면 위젯 로드 실패가 발생할 수 있습니다.
💬 플러그인을 제작하면 디자이너의 생산성이 크게 향상됩니다.
커스텀 위젯을 끌어다 놓기만 해도 속성과 미리보기가 바로 적용되므로, UI 설계 품질이 한층 올라갑니다.
🧪 시그널 슬롯 통합과 배포 팁
PySide(Qt for Python)의 강력한 기능 중 하나는 시그널(Signal)과 슬롯(Slot) 메커니즘입니다.
커스텀 위젯과 디자이너를 함께 사용할 때 이 두 개념을 제대로 연동해야만 이벤트가 정상적으로 작동합니다.
특히 프로모트 위젯이나 디자이너 플러그인으로 등록된 클래스는 @Slot() 데코레이터를 붙이지 않으면 디자이너가 해당 함수를 시그널로 인식하지 못합니다.
🔗 커스텀 시그널과 슬롯 예시
from PySide6.QtCore import Signal, Slot
from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout
class LogViewer(QWidget):
newLog = Signal(str) # 새로운 로그가 추가될 때 발생하는 시그널
def __init__(self, parent=None):
super().__init__(parent)
self.label = QLabel("Log initialized")
layout = QVBoxLayout()
layout.addWidget(self.label)
self.setLayout(layout)
@Slot(str)
def append_log(self, message):
self.label.setText(f"📜 {message}")
print("New log:", message)
위 코드는 newLog라는 커스텀 시그널을 만들어 다른 위젯이 메시지를 보낼 때 자동으로 화면에 출력되도록 합니다.
디자이너 상에서는 시그널-슬롯 편집기(Signal/Slot Editor)를 통해 연결할 수 있으며, 런타임에서도 connect() 메서드로 직접 바인딩할 수 있습니다.
⚙️ 연결 예시 (MainWindow와 LogViewer)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# 커스텀 위젯 인스턴스 가져오기
self.log_viewer = self.ui.logViewer
self.ui.pushButton.clicked.connect(self.handle_click)
def handle_click(self):
self.log_viewer.newLog.emit("버튼 클릭 이벤트 발생")
이처럼 커스텀 위젯을 프로모트로 등록하면 디자이너에서도 자동으로 객체 참조가 가능하므로, 별도의 findChild() 호출 없이 바로 접근할 수 있습니다.
커스텀 시그널을 활용하면 UI 요소 간의 의존성을 줄이고, 보다 느슨한 구조로 유지보수가 쉬운 코드를 구성할 수 있습니다.
📦 배포 및 실행 환경 팁
- 📁.ui 파일은 소스와 함께 배포하거나, pyside6-uic로 변환한 .py를 포함합니다.
- 🧩리소스 파일은 pyside6-rcc로 변환해 단일 패키지로 묶으면 안전합니다.
- 🧰PyInstaller를 이용할 때는 –collect-all PySide6 옵션으로 누락된 리소스를 포함시킵니다.
- 🧠프로모트 위젯 경로는 상대경로보다 패키지 경로로 설정해야 합니다.
💎 핵심 포인트:
PySide 디자이너 개발의 완성은 시그널-슬롯 통합입니다.
프로모트 위젯과 플러그인으로 UI 설계 효율을 높였다면, 커스텀 시그널을 통해 동작까지 유기적으로 연결해 실무 수준의 애플리케이션 구조를 완성할 수 있습니다.
💬 디자이너·코드생성·프로모트·플러그인·시그널까지 연결되면, PySide는 GUI 개발 생산성을 극대화합니다.
이 구조는 Qt 기반 크로스플랫폼 앱의 표준적인 패턴으로 자리 잡고 있습니다.
❓ 자주 묻는 질문 FAQ
Qt Designer에서 프로모트 위젯이 보이지 않아요. 왜 그런가요?
그러나 실제 실행 시에는 정상적으로 표시되므로 걱정하지 않아도 됩니다.
미리보기를 보고 싶다면 디자이너 플러그인을 제작하면 됩니다.
프로모트 위젯의 Header file 경로는 어떻게 지정해야 하나요?
예를 들어 커스텀 위젯이 widgets/custom_button.py에 있다면 Header file은 widgets.custom_button으로 입력합니다.
디자이너 플러그인을 설치했는데 위젯이 안 나타납니다.
세 버전을 동일한 환경에서 빌드 및 실행해야 플러그인이 인식됩니다.
프로모트 위젯을 여러 개 동시에 등록할 수 있나요?
Qt Designer의 Promote 대화상자에서 여러 클래스를 추가하고 필요할 때마다 선택하여 사용할 수 있습니다.
또한 디자이너 플러그인으로 묶으면 여러 위젯을 한 번에 관리할 수 있습니다.
PySide6-uic로 생성된 코드(ui_*.py)는 직접 수정해도 되나요?
이 파일은 자동 생성되므로, UI를 수정할 경우 이전 내용이 모두 덮어씌워집니다.
필요한 코드는 별도의 클래스에서 상속하여 오버라이드하는 방식으로 확장해야 합니다.
디자이너에서 커스텀 위젯의 속성이 보이지 않습니다.
커스텀 속성을 표시하려면 플러그인 클래스의 domXml() 메서드를 재정의해 속성을 등록해야 합니다.
PySide 프로젝트를 실행할 때 UI 파일을 동적으로 로드해도 괜찮나요?
QUiLoader를 이용하면 런타임에서 .ui 파일을 읽어와 위젯 트리를 생성할 수 있습니다.
UI를 자주 수정하는 초기 개발 단계에서 특히 유용합니다.
PySide에서 Qt Designer 없이도 UI를 만들 수 있나요?
모든 UI는 코드로 직접 구성할 수 있으며, 필요에 따라 디자이너와 병행하는 것이 좋습니다.
그러나 시각적인 배치와 신속한 프로토타이핑에는 디자이너가 훨씬 효율적입니다.
🧭 PySide 프로모트 위젯과 디자이너 플러그인 완성 가이드
PySide(Qt for Python)의 강점은 Qt Designer와의 통합입니다.
이 글에서는 디자이너 설계 → 코드생성 → 프로모트 위젯 등록 → 디자이너 플러그인 제작 → 시그널·슬롯 통합까지의 전체 과정을 살펴봤습니다.
핵심은 설계와 실행 코드가 서로 일관성을 유지하도록 구조를 정리하는 것입니다.
특히 커스텀 위젯을 자주 사용하는 프로젝트라면 프로모트와 플러그인 활용은 필수입니다.
프로모트 위젯은 UI 설계 단계에서 자신이 만든 클래스를 바로 사용할 수 있게 하며, 플러그인은 디자이너 내에서 미리보기가 가능한 완전한 개발 환경을 제공합니다.
여기에 시그널-슬롯 구조를 추가하면 UI와 로직이 명확하게 분리되어 유지보수가 쉬워집니다.
코드생성은 프로젝트 규모에 따라 정적 변환 또는 런타임 로드 방식을 적절히 선택하는 것이 좋습니다.
PySide는 Python 기반이지만 C++ Qt의 생태계를 거의 그대로 사용할 수 있기 때문에, 크로스플랫폼 애플리케이션을 빠르게 개발하려는 개발자에게 최적의 선택입니다.
UI 설계와 코드 로직의 조화를 이루는 이 구조를 익히면, 실무에서도 효율적인 생산성과 유지보수를 모두 만족시킬 수 있습니다.
🏷️ 관련 태그 : PySide6, QtforPython, QtDesigner, 프로모트위젯, 디자이너플러그인, 커스텀위젯, 코드생성, 시그널슬롯, GUI프로그래밍, 파이썬UI