Introduction to PySide
QT가 무엇인가?
Qt는 원래 C++을 위한 cross-platform GUI 라이브러리로 시작했으나 현재는 범용 어플리케이션 프레임워크로 기능이 확정되어었고 Android, IOS까지 확장되었다. 개발 환경인 QtCreator, GUI 디자인 툴인 Qt Designer 등도 제공하여 왠만한 어플리케이션을 Qt 안에서 개발할 수 있다.
그래도 핵심은 여전히 cross-platform GUI다. Cross-platform이란 하나의 코드를 여러 운영체제에서 쓸수 있다는 뜻이다. C++에서 GUI를 만들땐느 운영체제마다 상이한 API를 써야하는데 그러한 API를 내부적으로는 사용하되 사용자에게는 통일된 API를 제공하여 사용자는 어느 운영체제건 상관없이 Qt 라이브러리로 동일한 코드를 컴파일하여 동일한 GUI를 만들 수 있다.
Install, Simple Example
터미널에서 다음 명령어를 통해 pyside6를 설치해주자
pip install pyside6
다음으로 간단한 코드로 정상 설치되었는지 확인해보자. PySide의 내부 모듈은 Pyside6 패키지에 들어있고 그 안의 QtWidget에 GUI관련 모듈들이 들어있다.
import sys
from PySide6.QtWidgets import QApplication, QLabel
app = QApplication(sys.argv)
label = QLabel("Hello PySide6")
label.show()
app.exec()
app = QApplication(sys.argv)은 전체 GUI를 관리하는 객체다.
label은 간단한 텍스트를 보여주는 GUI객체다. label.show()를하면 객체를 화면에 나타낸다.
app.exec()가 실행되어야 이벤트 루프가 시작되면서 입력을 받을 수 있는 상태가 된다. 파이썬은 GUI가 실행되는 동안 이 줄에 멈춰있다. 다음과 같이 표시되면 정상 작동중이다.

Design Methods: Writing code vs Qt Designer
PySide를 이용한 GUI 설계 방식은 두 가지가 있다. 위와 같이 직접 코드로 GUI 객체를 생성하고 속성을 지정할 수도 있고 Qt Designer를 통해 GUI를 모두 그린 뒤 이를 불러오는 방식이 있다. 두 방법은 장단점이 있다. Qt Designer를 이용하면 그래픽 툴을 통해 쉽게 만들 수 있고 실행 전 미리 시각적으로 볼 수 있기 때문에 시행 착오가 적다. 하지만 GUI에 어떤 요소가 있는지 파이썬 코드에 드러나지 않기에 GUI설계가 끝나도 Qt Designer를 항상 함께 실행해야하는 불편함이 있다. Qt Designer를 이용하더라도 어차피 세부 설정이나 이벤트 처리는 파이썬 코드로 만들어야하기 때문에 두 방식을 모두 알아두는 것이 좋다.
우선 코드 기반으로
Signal & Slot
우리가 GUI 프로그램에서 주로 하는 일은 사용자로부터 입력을 받으면 이를 처리하는 루틴을 실행하는 것이다. app.exec()를 실행해 이벤트 루프를 돌며 이벤트가 발생하기를 기다린다. 기존 GUI 라이브러리에서 이벤트를 처리하는 방법은 callback 함수를 쓰는 것이엇다. GUI 객체에서 특정 이벤트시 실행할 함수 객체를 가지고 있다가 이벤트 발생시 함수를 실행시키는 것이다. Qt에서는 Signal & Slot이란 방식을 쓰는데 이벤트 발생시 이벤트 Signal을 보내고 이를 연결된 Slot 함수에서 처리하는 방시이다.
callback 함수와 가장 큰 차이는 하나의 Signal을 여러개의 Slot과 연결하면 하나의 이벤트로 여러 함수를 동시에 실행시킬 수 있다는 것이다. 다음 그리모가 같이 여러 개의 GUI 객체가 서로 연동될 때 유용한 기능이다.

MainWindow and Button
간단히 창에 버튼을 추가해 버튼을 누르는 이벤트를 처리하는 코드를 만들어보자. 윈도우에 버튼을 추가하기 위해서는 QMainWindow 클래스를 상속받은 새로운 클래스를 만들고 그 안에 QPushButton 객체를 생성해야 한다.
윈도우에 버튼 두 개를 추가하여 하나를 누르면 hello Pyside를 출력하게 하고 다른 하나를 누르면 GUI를 종료하도록 하자.
import sys
from PySide6.QtWidgets import *
from PySide6.QtCore import QCoreApplication
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setupUI()
self.count = 0
def setupUI(self):
self.setWindowTitle("PySide6: Basic button event")
self.setGeometry(500, 200, 300, 150)
print("window geometry:", self.geometry())
print("window geometry:", self.geometry().x(), self.geometry().y(), self.geometry().width(), self.geometry().height())
# window geometry: PySide6.QtCore.QRect(500, 200, 300, 150)
# window geometry: 500 200 300 150
# print button
btn_print = QPushButton("Hello", self)
btn_print.move(20, 20)
btn_print.resize(100, 50)
print("btn_print position:", btn_print.pos(), btn_print.pos().x(), btn_print.pos().y())
print("btn_print size:", btn_print.size(), btn_print.size().width(), btn_print.size().height())
btn_print.clicked.connect(self.hello_slot)
# => btn_print position: PyQt5.QtCore.QPoint(20, 20) 20 20
# => btn_print size: PyQt5.QtCore.QSize(100, 50) 100 50
# close button
btn_close = QPushButton("닫기", self)
btn_close.move(20, 100)
btn_close.clicked.connect(QCoreApplication.instance().quit)
# print label
self.label_print = QLabel("Hello PySide6", self)
self.label_print.move(150, 30)
def hello_slot(self):
self.count += 1
self.label_print.setText(f"Hello PySide6 {self.count}")
def main():
app = QApplication(sys.argv)
my_wnd = MyWindow()
my_wnd.show()
app.exec()
if __name__ == "__main__":
main()

기본 윈도우를 만들기 위해 QMainWindow를 상속받은 MyWindow 클래스 객체를 생성했다. 그리고 생성자 내부의 setupUI()에서 윈도우를 디자인하였다. setWindowTitle()은 윈도우 제목을 지정하는 함수다. setGeometry(x, y, width, height)함수를 이용해 윈도우의 위치와 크기를 지정할 수 있다. geometry 함수는 현재 GUI 객체의 위치와 크기를 나타내는 QRect(x, y, width, height) 형식의 구조체를 반환한다.
MainWindow 내부에 버튼을 추가할 때는 반드시 생성자의 두 번째 인자로 self가 들어가야만 윈도우에 버튼이 추가된다. setGeometry 함수로 위치와 크기를 한번에 지정했지만 move(), resize() 함수로 위치와 함수를 따로 지정하고 현재 설정된 값을 pos(), size()를 통해 읽을 수 있다. 중요한 것은 마지막의 connect함수다. btn_print 객체의 clicked라는 Signal을 self.hello_slot이라는 Slot 함수와 연결한 것이다. 이제 btn_print를 클릭하면 hello_slot이라는 함수가 실행된다.
두 번째 버튼은 닫기 버튼이므로 종료 함수와 연결하였다. QCoreApplication.instance().quit은 GUI 이벤트 루프를 종료시켜 app.exec() 다음으로 넘어가게 하는 함수다.
QLabel은 화면상에 간단한 텍스트를 띄우는 요소다. setText() 함수를 통해 텍스트 내용을 바꿀 수 있다. setUI()에서 "Hello PyQt"로 문구를 초기화 한 다음 "Hello" 버튼을 누를때마다 hello_slut 함수에서 숫자를 카운트하여 보여준다.
* 참고문서 찾기
처음보는 함수들이 나오는데 PyQt가 파이썬 스크립트가 아닌 바이너리 라이브러리 형태로 제공되기에 내부 변수나 함수를 볼 수 없다. 그렇기에 다른 클래스와 함수에 관한 정보를 얻고 싶으면 다음 링크를 통해 찾아보면 참고하기 좋다.
Simple QtDesigner Example
코드를 통해 GUI를 만들어 보았으니 이번에는 QtDesigner를 이용해 동일한 GUI를 만들어보겠다.
우선 다음 링크를 통해 다운받아주자.
https://build-system.fman.io/qt-designer-download
1. File - New - MainWindow - Creater
2. MainWindow 설정 : 우측 Proprty Editor에서 geometry 항목을 펼쳐서 Width, Height 항목 숫자 조절 (300, 200), window Title은 "PySide: Basic button event"

3. GUI 객체 추가: PushButton 2개와 Label 1개 추가
4. 기본 텍스트 수정: 각 객체를 더블 클릭하여 다음 그림과 같이 기본 텍스트 입력

5. 객체 이름 수정: 우측 Object Inspector 창에서 세 객체의 이름을 각각 다음 그림과 같이 수정

6. 파이썬 프로젝트 폴더에 hellopyside.ui로 저장
저장된 파일을 열어보면 XML형식으로 저장된것을 볼 수 있다.
UI 파일을 스크립트로 변환
UI 파일을 파이썬 스크립트에서 활용하기 위해 UI 파일을 변환해줘야한다. 코드로 직접 버튼을 추가하는 과정들을 자동화하는 것이다. 터미널을 열어 다음 명령어를 실행해보자. 파이참에서 터미널을 열면 프로젝트 폴더가 기본 경로로 잡히고 그 아래 가상환경 폴더인 venv가 있다고 가정한다. ui 파일은 프로젝트 폴더 내에 있어야 한다.
.\.venv\Scripts\pyside6-uic.exe hellopyside.ui -o hellopyside.py
그러면 다음과 같이 hellopyside.py가 만들어진다.
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'hellopyside.ui'
##
## Created by: Qt User Interface Compiler version 6.11.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QMenuBar,
QPushButton, QSizePolicy, QStatusBar, QWidget)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(300, 200)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.btn_print = QPushButton(self.centralwidget)
self.btn_print.setObjectName(u"btn_print")
self.btn_print.setGeometry(QRect(50, 30, 91, 31))
self.btn_close = QPushButton(self.centralwidget)
self.btn_close.setObjectName(u"btn_close")
self.btn_close.setGeometry(QRect(50, 80, 91, 31))
self.label_print = QLabel(self.centralwidget)
self.label_print.setObjectName(u"label_print")
self.label_print.setGeometry(QRect(160, 30, 101, 31))
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 300, 21))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"PySide: Basic button event", None))
self.btn_print.setText(QCoreApplication.translate("MainWindow", u"Hello", None))
self.btn_close.setText(QCoreApplication.translate("MainWindow", u"\ub2eb\uae30", None))
self.label_print.setText(QCoreApplication.translate("MainWindow", u"Hello Pyside", None))
# retranslateUi
이 스크립트에서 바로 코드를 작성해도는 되지만 그렇게할 경우 디자인 수정이 어려워진다. 기능을 추가하고 작업을 하다 디자인을 바꾸기 위해 ui파일을 수정하고 다시 스크립트 파일로 변환하면 다시 처음부터 작업해야하기에 여기서 바로 작업하는 것은 비효율적이다. 변환된 파일을 그대로 두고 UI_MainWindow 클래스를 상속 받은 새로운 클래스를 만들어 그곳에서 기능을 추가하면 디자인을 바꾸더라도 계속 이어서 작업할 수 있다.
GUI 기능 추가
위 클래스를 상속받아 버튼에 기능을 추가해보자.
import sys
from PySide6.QtWidgets import QMainWindow, QApplication
from PySide6.QtCore import QCoreApplication
from hellopyside import Ui_MainWindow # pyside6-uic로 변환된 파일
class MyWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self) # UI 초기화
# signal - slot 연결
self.btn_print.clicked.connect(self.hello_slot)
self.btn_close.clicked.connect(QCoreApplication.instance().quit)
self.count = 0
print("window geometry:", self.geometry())
print("btn_print position:", self.btn_print.pos())
print("btn_print size:", self.btn_print.size())
def hello_slot(self):
self.count += 1
self.label_print.setText(f"Hello PySide6 {self.count}")
def main():
app = QApplication(sys.argv)
my_wnd = MyWindow()
my_wnd.show()
app.exec()
if __name__ == "__main__":
main()
UI_MainWindow의 setupUI() 함수를 이용해 버튼과 라벨 등을 추가하고 크기와 위치를 잡는다. 이후 btn_print와 btn_close 각각 다른 slot에 연결하여 기능을 구현을 똑같이 해주었다.(label박스 크기를 작게하면 텍스트가 잘려서 다 안보일 수 있으니 텍스트가 안 보인다면 라벨 박스 크기를 키워주자.)
'학부 생활 + 랩실 > PySide' 카테고리의 다른 글
| PySide(2) - GUI Text Editor (0) | 2026.05.01 |
|---|