분석가 황규진 2024. 7. 10. 11:23

PyQt란 무엇인가?

  • Python + Qt를 합쳤다는 의미에서 PyQt라고 이름 붙였으며, C++ 기반의 GUI Framework인 Qt를 Python에서 사용할 수 있게 만든 패키지(Qt의 레이아웃에 Python의 코드를 연결하여 GUI 프로그램을 만들 수 있게 해주는 프레임워크).
  • PyQt는 C++의 Cross Platform GUI Framework인 Qt를 영국의 Riverbank Computing에서 Python 모듈로 변환해 주는 툴을 만들면서 시작 되었음.
  • PyQt4 버전과 PyQt 5버전이 주로 사용되고 있음.

PyQt의 특징

  • Python에도 PyGTK, PySide, Tkinter 등 다양한 GUI Framework가 존재하지만 이러한 GUI Frame는 사용하기 어렵고 시각적으로 이쁘지 않다는 단점이 있음.
  • PyQt는 이러한 Framework들과 다르게 시각적으로도 괜찮은 디자인을 보여주며 Qt Designer라는 프로그램을 이용하여 프로그램을 손쉽게 설계할 수 있다는 장점이 있음. 이러한 이유에서 PyQt 를 이용하여 GUI 프로그램을 만들어 봄.

PyQt 설치

  • Windows의 cmd 창에서 실행
  • pip install pyqt5 pip install pyqt5-tools

Path : MyWindow/MyWindow.py

 

PyQt 실행

import sys      
# PyQt5 패키지의 QtWidgets 모듈에 포함된 모든 것을 가지고 옴
from PyQt5.QtWidgets import *
# QApplication 클래스의 객체를 생성(생성자)하여 app에 저장
# 현재 소스 코드 파일에 대한 경로가 저장된 sys.argv를 QApplication 클래스의 
# 초기화 함수(__init__())에 전달함.
# QApplication는 QtWidgets 모듈 안에 포함된 여러 매서드(함수) 중 하나.
app = QApplication(sys.argv)
# QWidget()는 실제로 화면에 보여질 '윈도우'를 생성
myWindow = QWidget()
# 생성된 '윈도우'를  화면에 보임
myWindow.show()
# 닫기 버튼을 누를 때까지 app를 실행.
# 닫기 버튼을 누를 때까지 루프를 돌며 '윈도우'를 뛰어 놓는 코드
app.exec_()

 

윈도우 타이틀 지정 – setWindowTitle( ‘title of window’ )

import sys
from PyQt5.QtWidgets import *
#QMainWindow 클래스로부터 상속을 받아 MyWindow 클래스 코드 작성
class MyWindow(QMainWindow):
    pass
app = QApplication(sys.argv)
myWindow = MyWindow()
myWindow.show()
app.exec_()

 

윈도우 크기 지정(변경) – setGeometry(x, y, width, height)

# filename : WindowGeometry.py
import sys
from PyQt5.QtWidgets import *
# QMainWindow 클래스로부터 상속을 받아 MyWindow 클래스 생성
class MyWindow(QMainWindow):
    def __init__(self):              # 메서드 오버라이드
        super().__init__()           # 부모 클래스의 __init__() 호출
        # '윈도우'의 타이틀 지정(set)
        self.setWindowTitle('My First PyQt Window')  
        # '윈도우'의 위치 및 크기 setGeometry(x,y, width, height)   
        self.setGeometry(300,300, 320,180)
app = QApplication(sys.argv)
mywindow = MyWindow()
mywindow.show()
app.exec_()

 

PyQt – 첫 코딩 : Hello World! (1)

  • Hello World Window
    • QtWidget을 상속 받아서 생성
      • 원도우와 관련된 여러 메서드를 사용할 수 있음
    • setGeometry( )
      • 원도우의 위치를 지정
    • setWindowTitle( )
      • 창의 제목을 지정
    • QApplication( )
      • 앱을 생성
      • sys.argv
        • 명령행 인자를 QApplication( )에 전달
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
class HelloWorldWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initializedUI()
    
    def initializedUI(self):
        self.setGeometry(300, 300, 320, 180)
        self.setWindowTitle('Hello World')
                app = QApplication(sys.argv)
window = HelloWorldWindow()
window.show()
app.exec()

 

PyQt – 첫 코딩 : Hello World! (2)

  • HelloWorldWindow
    • QLabel( )
      • 라벨 객체를 생성
    • setText( )
      • 객체의 텍스트 속성 값을 지정
    • move( )
      • 객체를 지정된 위치로 이동
    • QApplication( )
      • 앱을 생성
      • sys.argv
        • 명령행 인자를 Qapplication( )에 전달
# filepath : Hello_World/Hello_World_II.py
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
class HelloWorldWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initializedUI()
    
    def initializedUI(self):
        self.setWindowTitle('My First PyQt Window')  
        self.setGeometry(300,300, 320,180)
        
        hello_label = QLabel(self)
        hello_label.setText('Hello World!')
        hello_label.move(120, 40)
  
app = QApplication(sys.argv)
window = HelloWorldWindow()
window.show()
app.exec()

 

PyQt – Welcome to PyQt World!

  • QPushButton( )
    • 푸시 버튼 객체를 생성
  • setText( )
    • 객체의 텍스트 속성 값을 지정
  • move( )
    • 객체를 지정된 위치로 이동
# filepath : Hello_World/Widget_Position.py
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton
class PyQtWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initializedUI()
    
    def initializedUI(self):
        self.setGeometry(300, 300, 480, 100)
        self.setWindowTitle('Welcome to PyQt World!')
        
        hello_label = QLabel(self)
        hello_label.setText('Hello World!')
        hello_label.move(200, 40)
        
        button = QPushButton(self)
        button.setText('OK')
        button.move(200, 60)  
if __name__=='__main__':
    app = QApplication(sys.argv)
    window = PyQtWindow()
    window.show()
    app.exec()

 

 

Qt Designer를 사용할 것이다!

 

 

Qt Designer

  • PyQt를 이용하여 GUI 프로그램밍을 할 때 손쉽게 프로그램의 레이아웃을 편집할 수 있게 해주는 편집기.
  • Windows의 cmd 창에서 실행
    • PyQt 및 Qt Designer 설치
pip install pyqt5

pip install pyqt5-tools

 

Qt Designer의 실행

  • designer.exe 파일 위치 – 파이썬 설치 폴더\lib\site-packages\qt_applications\Qt\bin
    • 예) C:\Users\phiri\AppData\Local\Programs\Python\Python38\Lib\site-packages\qt5_applications\Qt\bin
    • designer 실행
      • Qt Designer의 시작화면이 나타남

Qt Designer의 화면 구성

  • Dialog without Buttons 더블 클릭

  • 빈 Dialog 창이 하나 만들어짐 → 향후 이 창이 실행 화면이 됨.

PyQt 시작 - Qt Designer를 이용한 UI의 제작

  • UI의 저장과 Python 코드와의 연결
    • Dialog 창에 PushButton 위젯 하나만 남김
  • UI의 저장 – File/Save(ctl+s)

  • 위젯들이 가지고 있는 공통적인 속성(Property)에 대해 알아봄'

 

  • 위젯의 글자 변경
    • 속성 편집기에서 선택된 위젯의 속성을 확인, 수정할 수 있음
    • “PushButton” 위젯을 선택 후 속성 편집기에서 text 속성이 PushButton임.
      • 속성 편집기에서 text 속성 값을 Button A로 변경
      • 또는 해당 위젯을 더블 클릭해서 해당 위젯의 text 속성의 값을 변경할 수 있음

위젯의 속성

  • 위젯 마다 속성이 조금씩 다르지만 공통적으로 가지고 있는 속성이 다수 있음.
  • 자주 사용되는 속성들
    • ObjectName : Python 코드에서 사용될 위젯의 이름을 설정.
    • Gemometry : 위젯의 x, y 좌표와 폭과 높이를 지정
    • Palette : 위젯의 색상을 지정
    • Font : 글자의 폰트와 크기 지정
    • ToolTip : 위젯 위에 마우스를 올려 놓았을 때 해당 위젯에 대한 정보를 제공하는 글자 출력
    • TooTipDuration : tooltip이 얼마 동안 보여질지 시간 지정, 단위는 millisecond
    • TooTipDration이 -1이면 해당 위젯에 마우스가 올려져 있으면 지속적으로 tooltip를 출력

UI와 소스 코드 연결

  • UI의 저장과 Python 코드와의 연결
    • UI와 Python 코드의 연결하는 두 가지 방법
      • UI 파일을 Python 코드로 변환하여 변환된 python 코드를 수정하는 방법
      • UI 파일을 Python 코드에서 import하는 방법
  • UI와 Python 코드의 연결 - UI 파일을 Python 코드에서 import
    • 아래 코드를 UI 파일과 동일한 폴더에 저장

 

 

SYS module

  • Thread
import sys
# sys.argv - 명령 행에서 입력된 인수가 저장되어 있음
# $ python sys_module_test.py hello.py
# ['sys_module_test.py', 'hello.py']
print(sys.argv)
# sys.exit() - 스크립트 강제 종료 
x = 0
while True:
    if x == 5:
        sys.exit()
    else:
        print(x)
    x += 1
from threading import Thread
import time
def task_A(value):
    for i in range(5):
        print('Thread - Task A:', i)
        time.sleep(2)
# target = 스레트 함수 이름, argv = 매개변수를 튜플 형태로 전달
threadA = Thread(target = task_A, args=(1,))
# 스레드 시작
threadA.start()
# main task
for i in range(5):
    print('Main Task:', i)
    time.sleep(1)
print('--- Main task end ---')
threadA.join()
  • Thread(target=task_A, args=(1,))
    • task_A( ) 함수를 스레드 함수로 등록
    • task_A( )함수로 전달할 매개변수 값 지정
  • threadA.start( )
    • 스레드 시작(task_A( ) 함수 시작)
    from threading import Thread
    import time
    def task_A(value):
        for i in range(5):
            print('Thread - Task A:', i)
            time.sleep(2)
    threadA = Thread(target = task_A, args=(1,))
    threadA.start()
    # main task
    for i in range(5):
        print('Main Task:', i)
        time.sleep(1)
    print('--- Main task end ---')
    threadA.join()
    
    • 결론
      • ‘main task’가 실행 되는 도중에도 스레드는 백그라운드로 동작함
import time
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
from PyQt5.QtCore import *
from threading import Thread, currentThread

class Thread_A_TASK(QThread):
    def __init__(self, parent):  # parent는 WindowClass에서 잔달하는 self이다.(WindowClass의 인스턴스)
        super().__init__(parent)  # parent를 사용하여 WindowClass 위젯을 제어할 수 있음
    def run(self):
        print('A 업무를 시작합니다')
        for i in range(0, 101, 10): 
            print('A 업무 진행율 {}%'.format(i)) 
            time.sleep(2)
        print('A 업무 종료')
        
class Thread_B_TASK(QThread):
    def __init__(self, parent):
        super().__init__(parent)
    def run(self):
        print('B 업무를 시작합니다')
        for i in range(0, 101, 10): 
            print('B 업무 진행율 {}%'.format(i)) 
            time.sleep(2)
        print('B 업무 종료')

form_class = uic.loadUiType("./10.Thread/QThreadTest.ui")[0] 

class WindowClass(QMainWindow, form_class):
    def __init__(self) : 
        super().__init__() 
        self.setupUi(self)       
        
        self.btn_A_start.clicked.connect(self.slot_A_task)
        self.btn_B_start.clicked.connect(self.slot_B_task)
        
    def slot_A_task(self): 
        A_task = Thread_A_TASK(self)
        A_task.start()
        
    
    def slot_B_task(self): 
        B_task = Thread_B_TASK(self)
        B_task.start()

if __name__ == "__main__" : 
    app = QApplication(sys.argv) 
    myWindow = WindowClass() 
    myWindow.show() 
    app.exec_()