Post

(PyQt6)2. 기본 위젯 소개

작성할 내용들

  • 기본위젯들
    • QWidgets
    • QDialog
    • QScrollArea
    • QLabel
    • QLineEdit
    • QPushButton
    • QScrollArea
    • QSpinBox / QDoubleSpinBox
  • Pyside6-uic + 코드로 짜기

PyQt6는 HTML처럼 반응형 UI짜기 어렵다. 정 필요하다면 resizeEvent 발생할 때마다 위젯의 폭과 높이를 고려하여 기준 길이보다 짧거나 길 때 레이아웃을 바꾸도록 코드를 짜야한다.

QtWidgets

드래그 앤 드롭으로 UI 짜기

드래그 앤 드롭 방식으로 UI짜기 위해서는 다음 단계를 따른다.

1. Pycharm의 프로젝트를 새로 생성한다.
  • 이 때 virtualEnv를 활성화해야 하는 것을 권장한다.
    • 추후 pyinstaller.exe 만들 때 불필요한 라이브러리로 인해 용량 증가를 방지 목적으로 사용.
    • 또, pyside6-designer.exe, pyside6-uic.exe의 경로를 비교적 쉽게 접근할 수 있다.

2. PySide6을 실행한다. (Qt5버전은 PySide2를 설치하면 된다.)
1
pip install PySide6
3. pyside6-designer.exe 실행

다음과 같이 프로젝트 내부에 Scripts 폴더가 생성될 건데 터미널에 다음 명령어를 치면 PySide6-designer.exe를 실행시킬 수 있다.

1
2
# 보통 Scripts 폴더 내에 생성된다.
.\Scripts\pyside6-designer.exe

실행하면 아래와 같은 창이 뜨는데 MainWindowWidget으로 생성해주자.

4. 드래그 앤 드롭으로 구현

  • 위젯을 놓고 우클릭>배치>원하는 레이아웃으로 배치하기

  • ctr/cmd + r : 위젯 미리보기 실행

  • 버튼 클릭, 값변화 등등의 이벤트/signal 관리하고 싶으면 F4편집>시그널/슬롯 편집 으로 시그널/슬롯을 연결한다.
  • 연결한 슬롯은 uic로 변환 이후 파이썬 파일에 함수/메소드로 구현한다.
    • .gif 파일과 같이 했을 경우 slot1 이름을 가진 함수/메소드에 연결된다.

pyqt_designer_signal_slot

5. 구현 이후

구현 다했으면 .ui 파일로 저장 후 다음 커맨드로 .ui파일을 .py로 치환해서 코드 짤 수 있게 만든다.

1
/Scripts/pyside6-uic.exe [저장한 ui파일이름].uic -o [저장할 파이썬 ui파일 이름].py

terminal로 매번 스크립트 실행하기 귀찮다면 Pycharm의 Setting을 건드리거나 bat 등의 스크립트 파일을 만들어서 실행하자. PyCharm Setting을 설정은 다음과 같이 한다.

(위의 경우 mainUI.ui 파일의 이름으로 저장한 .ui파일을 mainUI.ui파일로 변환한다는 뜻이다.) 우클릭 후 External Tools에 위 이름으로 설정된 Tool을 선택하면 변환해준다.

기본 위젯들 살펴보기

Pyside6-designer.exe 실행하면 확인할 수 있듯이 pyqt에서는 아래 그림과 같은 위젯들을 사용할 수 있다. 주로 사용하는 것들 위주로 빨간 사각형으로 표시했고 그 중 일부만 소개할 예정이다.

Spacers 사용을 권장하지 않은 이유는 QWidgets 상속 받은 위젯들은 .show/hide(), .setVisible 등으로 표시하거나 숨길 수 있는 반면, Spacer들은 상기한 메소드를 사용해서 표시/숨김 기능을 구현할 수 없다. 차라리 QWidgetsetSizePolicy 기능을 활용해서 Spacer 기능을 대체하는 것이 훨씬 낫다.

QMainWindow, QWidget, QDialog

1
2
3
4
class firstapp(QWidget) :  
    def __init__(self):  
        super().__init__() # 파이썬 상속 class 초기화
        
  • QMainWindow : Menubar이 달려있는 메인 창.
  • QWidget : 가장 범용적으로 사용되는 위젯.
    • Menubar나 Status Bar 등 없이 간단한 창 구현할 때 이걸로 프로그램을 만들어도 된다.
    • 많은 위젯들이 QWidget에서 상속되는 경우가 많고 QWidgetQWidget을 구현할 수 있다.
  • QDialog : 앱의 설정창, 입력창 등을 구현할 때 사용하는 위젯
    • 예를 들어, QDialog에 내장된 .accept, .reject 등을 활용하여 종료이벤트를 달리할 수 있다.

QLabel

  • span 태그 같이 텍스트 위주 위젯을 넣을 때 사용한다.
  • 폰트 스타일 변경할 때 Qlabel.setFontQtGui.QFont를 사용한다.

1
2
3
4
#QLabel  
label = QLabel("This is QLabel")
# Inter폰트, 14pt, font.weight, italic 여부
label.setFont(QFont("Inter", 14, QFont.Weight.Bold, False))

가끔 위젯의 기능을 잘 모를 때에는 ctrl/cmd + click으로 위젯의 메소드 및 플래그 등을 확일할 수 있다. vsc, pycharm 모두 지원하긴 하나 vsc는 한 번에 코드를 잘 보여주는 반면, pycharm은 structure 파악하는 데 좋다.

QLineEdit

  • id, password같은 한 줄짜리 입력창을 만들 때 사용하는 위젯이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#QLineEdit  
## 일반적인 경우  
string_input = QLineEdit()  
string_input.setPlaceholderText("아무 문자열 넣어도 입력이 가능합니다.")  
  
## 실수형 LineEdit()real_num_input = QLineEdit()  
real_num_input.setValidator(QDoubleValidator(self))  
real_num_input.setPlaceholderText("실수 값만 입력이 됩니다.")  
real_num_input.textChanged.connect(lambda x :
								   string_input.setText(str(real_num_input.text())))
  
## 비밀번호 받기  
password_input = QLineEdit()  
password_input.setPlaceholderText("password here")  
password_input.setEchoMode(QLineEdit.EchoMode.Password) # PyQt6 문법  
password_input.textEdited.connect(lambda x : print(password_input.text()))  

자주 쓰는 메소드 위주로 소개해보겠다.

  • .setPlaceholderText : QLineEdit가 비어있을 때 어떤 값을 넣어야 할 지 힌트를 주는 문구를 설정한다.
  • .setValidator : 입력값의 타입을 제한한다.
    • PyQt6.QIntValidator : 정수형 문자열만 받는다.
    • PyQt6.QDoubleValidator : 실수형 문자열만 받는다.
  • .setEchoMode : 입력 모드를 변경한다(기본값은 None)
    • QLineEdit.EchoMode.Password 비밀번호처럼 입력값을 가린다.
  • .textChanged(signal) : 값이 변경할 때마다 .connect된 함수를 실행한다.
  • .textEdited(signal) : 값 변경이 완료되었을 때 .connect된 함수를 실행한다.
    • 참고로 .connect()할 때, lambda 함수를 사용하면 첫 번째 인자가 self가 들어가기 때문에 self를 받아줄 인자 x를 선언해야 항수관련 에러가 발생하지 않는다.

QPushButton

클릭 버튼을 만드는 위젯이다.

1
2
3
#QPushbutton  
btn = QPushButton("click!")  
btn.clicked.connect(lambda x : print("Button is clicked"))  

대부분의 경우, clicked signal만 알아도 버튼의 기능을 구현할 수 있다. 다음 글에 이 QPushButton에 클릭하는 효과를 추가하는 방법에 다룰 예정이다.

Q(Double)SpinBox

  • QSpinBox : 정수형 스핀박스
  • QDoubleSpinBox : 실수형 스핀박스
1
2
3
4
5
6
7
8
9
10
11
12
#QSpinbox  
intspinbox = QSpinBox() # QSpinBox는 기본적으로 0 ~ 100 사이 범위를 가진다. step = 1  
# 최대 범위를 1000으로  
intspinbox.setMaximum(1000) 
# 최소 범위를 50intspinbox.setSingleStep(5) # 버튼 클릭당 5씩 값 변화  
intspinbox.setMinimum(50) 
  
#QDoubleSpinbox  
doublebox = QDoubleSpinBox()  
doublebox.setMaximum(31.4135)  
doublebox.setMinimum(-10)  
doublebox.setSingleStep(0.2)  
  • .setMaximum() : 숫자 최대 영역
  • .setMinimum() : 숫자 최소 영역
  • .setSingleStep() : 버튼, 스크롤 등으로 값 증가/감소 단위
    • .valueChanged(signal) : 값이 바뀔 때 .connect 함수를 실행함.

복잡한 계산하는 프로그램을 만들 경우 QSpinBox에 스크롤로 인해 때문에 프리징을 유발할 수 있다. 아래코드처럼 wheelEvent 끄거나 QTimer을 이용해서 입력값 변경하는데 약간의 지연을 주는 것도 좋은 방법이다.

1
2
value = QSpinBox()
value.wheelEvent = lambda event : None 

코드 전체 보기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 이렇게 하나씩 호출해도 좋지만..  
# from PyQt6.QtWidgets import QMainWindow, QWidget, QDialog, QLabel, ...  
# 그냥 아래 코드로 퉁쳐도 된다.  
from PyQt6.QtWidgets import *  
from PyQt6.QtGui import QDoubleValidator, QFont  
  
  
class firstapp(QWidget) :  
    def __init__(self):  
        super().__init__() # 파이썬 상속 class 초기화  
        self.init_layout() # 레이아웃 초기화  
  
    def init_layout(self):  
        layout = QVBoxLayout(self) #QVBoxLayout(parent = self)  
  
        # Scorll Area        QScroll = QScrollArea()  
        QScroll.setWidgetResizable(True)  
        widget = QWidget()  
        QScroll.setWidget(widget)  
        main_layout = QVBoxLayout(widget)  
        layout.addWidget(QScroll)  
  
        #QLabel  
        label = QLabel("This is QLabel")  
        label.setFont(QFont("Inter", 14, QFont.Weight.Bold, False))  
        main_layout.addWidget(label)  
  
        #QLineEdit  
        ## 일반적인 경우  
        string_input = QLineEdit()  
        string_input.setPlaceholderText("아무 문자열 넣어도 입력이 가능합니다.")  
        main_layout.addWidget(string_input)  
  
        ## 실수형 LineEdit()        real_num_input = QLineEdit()  
        real_num_input.setValidator(QDoubleValidator(self))  
        real_num_input.setPlaceholderText("실수 값만 입력이 됩니다.")  
        real_num_input.textChanged.connect(lambda x : string_input.setText(str(real_num_input.text())))  
        main_layout.addWidget(real_num_input)  
  
        ## 비밀번호 받기  
        password_input = QLineEdit()  
        password_input.setPlaceholderText("password here")  
        password_input.setEchoMode(QLineEdit.EchoMode.Password) # PyQt6 문법  
        password_input.textEdited.connect(lambda x : print(password_input.text()))  
        main_layout.addWidget(password_input)  
  
        #QPushbutton  
        btn = QPushButton("click!")  
        btn.clicked.connect(lambda x : print("Button is clicked"))  
        main_layout.addWidget(btn)  
  
        #QSpinbox  
        intspinbox = QSpinBox() # QSpinBox는 기본적으로 0 ~ 100 사이 범위를 가진다. step = 1  
        intspinbox.setMaximum(1000) # 최대 범위를 1000으로  
        intspinbox.setMinimum(50) # 최소 범위를 50       
        intspinbox.setSingleStep(5) # 버튼 클릭당 5씩 값 변화  
        main_layout.addWidget(intspinbox)  
  
        #QDoubleSpinbox  
        doublebox = QDoubleSpinBox()  
        doublebox.setMaximum(31.4135)  
        doublebox.setMinimum(-10)  
        doublebox.setSingleStep(0.2)  
        main_layout.addWidget(doublebox)  
  
  
# 만약 이 파일이 메인 실행파일이라면 아래 코드를 실행  
if __name__ == "__main__" :  
    app = QApplication([]) # 보통은 sys.argv 값을 넣는다.  
    win = firstapp()  
    win.show()  
    app.exec()

pyqt_Qtwidgets

잡다한 팁 모음

이벤트 수정하기

signal의 경우 valuechanged.connect(function)으로 연결할 수 있는데, method Event(Reimplemented Protected Functions)의 경우 다음과 같이 재정의한다.

1
2
wid1 = QLabel("Double Click Test")
wid1.mouseDoubleClickEvent = lambda x : print(wid1.text())

Reference

This post is licensed under CC BY 4.0 by the author.