CastleJo의 개발일지

PyQT 사용하기

|

이번 포스팅에서는 pyqt5의 기본적인 사용법에 대해서 알아보려고 한다.

설치

pip install pyqt5
pip install pyqt5-tools

or

pip3 install pyqt5
pip3 install pyqt5-tools

이 후 설치가 되었나 확인해보자.

python
>>> import PyQt5

디자이너도 설치되었는지 확인하자. 환경변수로 저장되었을테니 터미널(윈도우에서는 CMD) 에서 실행하자.

designer

디자이너가 켜지면 성공이다.

QT 디자이너

디자이너는 크게 설명하진 않겠다.
.ui -> .py 으로 바꾸는 방법만을 설명하겠다.

일단 예시코드로 둔 배쉬 쉘 스크립트를 올리겠다.

#!/bin/bash

python -m PyQt5.uic.pyuic -x $1 -o ~/Desktop/frames-client/client/gui/$1.py;

설명하자면, 매개변수를 하나 받아 지정된 경로에 저장하는것이다.
뒤에 .py를 안붙이면 에러가 난다.

사용법은 이렇다.

python -m PyQt5.uic.pyuic -x {.ui 파일} -o {저장할 파일}

상대경로로 하면 python이 설치되어있는 곳 부터 시작하므로, 절대경로를 지정해주는 것을 추천한다.

좀 더 보기 쉽게

ui파일을 바꿔봤는데… 좀 가독성이 안좋아서 내 맘대로 리팩토링을 해봤다.

기존 파일

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.frame = QtWidgets.QFrame(self.centralwidget)
        self.frame.setGeometry(QtCore.QRect(530, 10, 261, 181))
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.label = QtWidgets.QLabel(self.frame)
        self.label.setGeometry(QtCore.QRect(110, 10, 56, 12))
        self.label.setObjectName("label")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "한글"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

뭐 변환된 그대로 쓰려면 써도 된다.
하지만 나는 병적으로 클래스화를 좋아하여 바꾸었다.

변환 결과

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from .gui_objectmaker import ObjectMaker

LOCATION = "MainWindow"

OM = ObjectMaker(LOCATION)

class Ui_Main(object):

    def __init__(self, W, H):
        
        super().__init__()
        self.__app = QtWidgets.QApplication(sys.argv)

        # Make MainWindow
        self.__MainWindow = OM.makeMainWindow(W,H)
 
        #Central Widget
        self.__centralwidget = OM.makeCentralWidget(self.__MainWindow)

        #StatusBar (하단)
        self.statusbar = OM.makeStatusBar(self.__MainWindow)

        # SetUp Ui
        self.setupTemperature(W,H)

        QtCore.QMetaObject.connectSlotsByName(self.__MainWindow)


    def startUi(self):
        
        self.__MainWindow.show()
        sys.exit(self.__app.exec_())

    def setupTemperature(self,W,H):
        
        half_W = int(W / 2)
        half_H = int(H / 2)
        tick_W = int(W / TICK)
        tick_H = int(H / TICK)

        frame_W = half_W - 2 * (tick_W)
        frame_H = 29 * tick_H
        
        # Temperature Frame
        self.FR_TEMP = OM.makeFrame(self.__centralwidget, 
            startX = half_W + tick_W, 
            startY = tick_H, 
            W = frame_W, 
            H = frame_H)
        
        self.LB_TEMP_Main = OM.makeLabel(self.FR_TEMP,
            frame_W/2 - 28, tick_H, 56, 12, "한글")

특징점을 찾아보자.

  1. OM 이라는 클래스를 만들었다. 화면을 많이 만들것인데 메소드를 중복해서 사용할 필요를 못느껴서 상위레벨로 포장한 메소드의 묶음 클래스를 만들었다.
  2. 크기를 절대적인 크기에서 상대적으로 바꾸었다. 레이아웃쪽을 공부하여 더 효율적인 방법을 찾아 볼 예정이다.
  3. Width, Height를 변수로 받는다. 취향이다.

OM

from PyQt5 import QtCore, QtWidgets


class ObjectMaker:

    def __init__(self, crtWindow):
        self.__crtWindow = crtWindow
        self.__callNum = 0
        self.__translate = QtCore.QCoreApplication.translate

    def makeMainWindow(self, W, H):
        mainWindow = QtWidgets.QMainWindow()
        mainWindow.setObjectName(self.__crtWindow)
        mainWindow.resize(W, H)

        return mainWindow

    def makeCentralWidget(self, location):
        centralwidget = QtWidgets.QWidget(location)
        centralwidget.setObjectName(self.__makeObjectName("centralwidget"))
        location.setCentralWidget(centralwidget)

        return centralwidget

    def makeStatusBar(self, location):
        statusbar = QtWidgets.QStatusBar(location)
        statusbar.setObjectName(self.__makeObjectName("statusbar"))
        location.setStatusBar(statusbar)

        return statusbar


    def makeLabel(self,location,startX,startY,W,H,text=""):
        label = QtWidgets.QLabel(location)
        label.setGeometry(QtCore.QRect(startX,startY,W,H))
        label.setObjectName(self.__makeObjectName("label"))
        label.setText(self.__translate(self.__crtWindow, text))
    
        return label
    
    def makeFrame(self, location, startX, startY, W, H):
        frame = QtWidgets.QFrame(location)
        frame.setGeometry(QtCore.QRect(startX,startY,W,H))
        frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        frame.setFrameShadow(QtWidgets.QFrame.Raised)
        frame.setObjectName(self.__makeObjectName("frame"))
    
        return frame

    def __callNameNum(self):
        self.__callNum += 1
        return (self.__callNum - 1)

    def __makeObjectName(self, name):
        return self.__crtWindow + name + str(self.__callNameNum)

OM의 메소드들이다. 특이점을 찾아보자.

  1. __callNameNum() 과 __makeObjectName()의 존재 의미 한가지 윈도우에서는 이름이 중복되면 안된다.
    따라서 고유한 넘버링을 통해 중복을 피한다.
    하지만 이 때 호출하기 어려워 질 가능성이 있으니, 고려하여 사용하면 된다.

  2. 각종 매개변수들
    코드를 보면 이해가 빠를 것이라고 생각한다.
    기본적으로 뼈대만 만들어두고, 호출하는 곳에서 상세 조정을 하는 것이 좋아보인다.

main

def main():
    mainUi = Ui_Main(1440,900)
    mainUi.startUi()

크기를 지정하고 실행하는 방식으로 구현했다.
W,H를 인자로 받아 유연하다.

다음엔 멀티스레딩을 이용한 opencv 실시간 화면 송출을 포스팅하겠다.