임베디드/Qt for python

[Qt] GUI with Qt : Qwidget

KimuGamJa 2024. 5. 12. 23:02

 

Qt란? : GUI 프로그램 개발용 Cross Flatform Framework
  • Qt의 모토 : One framework, One Codebase, Any Platform

 

⭐Qt for Python
  • Qt는 C++기반 Framework이다.
    => 하지만, 다른 언어로 제작된 Library 를 Python 으로 호출할 수 있는 연결 모듈인 python Binding 모듈을 이용하면 Qt for Python을 다룰 수 있다!
  • ✅ 사용할 Qt Python Binding Set : PySide (Qt 공식 Framework) 
  • ✅ 사용할 Qt IDE : Qt Designer (LGPL)
    => Qt Designer는 Editor가 포함되어 있지 않기 때문에 PyCharm과 함께 사용한다.
  • [참고] Qt for Python document : https://doc.qt.io/qtforpython-6/index.html

 

 

🩵Widget ( 위젯)
class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.main()

            ```
            생략
            ```

 

 

💚Qt 기본 실습코드 - 창 2개 띄우기
from PySide6.QtWidgets import *


class MyApp(QWidget):
    def __init__(self, name):
        super().__init__()
        self.appName = name  # 생성자에서 appName 설정
        self.main()

    def main(self):
        # App Title
        self.setWindowTitle(self.appName)
        # 윈도우 x좌표, y좌표, 가로크기, 세로크기
        self.setGeometry(0, 0, 400, 300)


if __name__ == '__main__':
    app = QApplication()
    win1 = MyApp("win1")
    win1.show()
    win2 = MyApp("win2")
    win2.show()
    app.exec()

 


 

Qt 사용하기 - 기본 구성

 

App + widget

if __name__ == '__main__':
    app = QApplication()
    win = MyApp()
    win.show()
    app.exec()

 

 

■ QApplication 인스턴스 생성

QApplication()

  • QApplication에 대한 인스턴스 생성
  • 이벤트를 받는다.
  • App 전체에 대한 정보가 담겨 있다.
  • App 프로그램에는 1개의 QApplication 인스턴스가 존재한다.
  • .exec( ) 함수를 통해 프로그램을 직접 끄기 전까지 무한 루프를 돌며, 이벤트(클릭이벤트, 종료이벤트 등 사용자 행동)를 기다리게 한다.

 

 

■ Qwidget 객체 생성

 win = MyApp()

  • 여기서 MyApp()은 QWidget을 상속받은 GUI Widget 클래스이다.
  • wind 객체는 눈에 보일 수 있는 기본 위젯 (window) 이다.
  • show() 메서드를 호출해야 눈에 보여진다.
  • 화면을 구성하고, Widget 내부에 다른 Widget 추가가 가능하다.

 


 

QWidget을 상속받은 GUI Widget 클래스 정의

 

💡 클래스에서 사용되는 self는 해당 클래스로 생성한객체를 나타낸다.
=> 즉, win = MyApp() 객체 생성시, class에서 self는 win을 의미한다.

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.main()

    def main(self):
        # App Title
        self.setWindowTitle("Qt GUI App")
        # 윈도우 x좌표, y좌표, 가로크기, 세로크기
        self.setGeometry(0, 0, 400, 300)

QWidget 클래스로부터 상속받는다!

 

 

 

■ 생성자 함수

    def __init__(self):
        super().__init__()
        #부모 클래스의 생성자를 호출
        # 이 경우, QWidget 클래스의 생성자를 호출하여 MyApp 클래스의 인스턴스를 초기화
        self.main()
        #클래스 내부의 다른 메서드인 main()을 호출

 

 

■ App Widget 꾸미기

    def main(self):
        # App Title
        self.setWindowTitle("Qt GUI App")
        # 윈도우 x좌표, y좌표, 가로크기, 세로크기
        self.setGeometry(0, 0, 400, 300)

 

.setWindowTitle(text) : widget 창 제목 변경

 

.setGeometry(위치x, 위치y, 가로, 세로) : 상위 객체를 기준으로 widget의 위치와 가로 세로 크기 정하고 띄운다.

  • 여기서 self(=win)의 상위객체는 app창(=윈도우창)이므로, 윈도우 창을 기준으로 0, 0 위치에 win 창을 생성한다.

 


 

 

Qt 사용 예제

 

 

🖤공통 메서드

 

 .setGeometry(위치x, 위치y, 가로, 세로) :상위 객체를 기준으로 widget의 위치와 가로 세로 크기 정하고 띄운다.

 

.setText(텍스트) : text를 지정한다.

 

 

 

❤️QLabel
        #"Hello!" 문구와 함께 레이블을 win에 생성
        self.lbl = QLabel("Hello!", self)
        #레이블의 위치를 win 기준으로 100,100 위치에 가로 50, 세로 50의 크기로 생성
        self.lbl.setGeometry(100, 100, 50, 50)

 

QLabel("label text", 부모위젯) : label 생성 및 text 지정

  • 부모 위젯을 지정하면 QLabel은 그 위젯의 자식으로 추가되며, 부모 위젯이 삭제되면 QLabel도 삭제된다.
  • 만약 None이나 다른 부모를 지정하지 않으면, QLabel은 최상위 윈도우에 배치된

 


 

🩷QPushButton
        self.btn = QPushButton("버튼 클릭", self)
        self.btn.setGeometry(100, 0, 100, 100)

 

 QPushButton("button text", 부모위젯) : botton 생성 및 text 지정

 

 


 

🧡QLineEdit

        self.lineEdit = QLineEdit(self)
	inputText = self.lineEdit.text()

 

QLineEdit(부모위젯) : 한 줄 입력받는 텍스트에디터 생성

 

QLineEdit객체.text() : QLineEdit에 입력된 값을 반환해준다.

 


 

💛QMessageBox

        self.msg = QMessageBox()
        
        self.msg.setText( self.lineEdit.text() ) #QMessageBox의 text를 텍스트에디터에 적힌 text로 정한다.
        
        self.msg.exec()
        #QMessageBox는 독립적인 창이므로, User가 닫기 버튼을 누를 때까지 실행될 수 있게 .exec() 사용한다.

 

QMessageBox() : QMessageBox 객체를 생성한다.

 

Box객체.exec(): QMessageBox는 독립적인 창이므로, User가 닫기 버튼을 누를 때까지 실행될 수 있게 .exec() 해주어야 한다.

 

🚨여기서, exec()를 사용하는 경우, 해당 창을 Modal 창이 되기 때문에

닫기 전까진 다른 창을 제어하지 못한다.

 


 

Signal & Slot
  • Signal : 위젯을 통해 감지되는 신호
  • Slot : Signal을 감지, Signal에 대응하는 함수 호출
    (하나의 Widget/Signal에는 여러 Slot 이 존재 할 수 있다.)

 

from PySide6.QtWidgets import *

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.main()

    def main(self):
        self.setWindowTitle("Qt GUI App")
        self.setGeometry(0, 0, 400, 300)
        
        self.btn = QPushButton("버튼 클릭", self)
        self.btn.setGeometry(100, 0, 100, 100)
        
        #버튼의 clicked 시그널이 발생 시, KFC() 호출하도록 연결
        self.btn.clicked.connect(self.KFC)
    
    #버튼이 clicked 시그널 발생 시 호출되는 KFC() 
    def KFC(self):
        print("click")

if __name__ == '__main__':
    app = QApplication()
    win = MyApp()
    win.show()
    app.exec()

 

 

 

■ 시그널(signal)과 슬롯(slot)을 연결하기

객체.이벤트.connect(함수)

        self.btn.clicked.connect(self.KFC)
  • self.btn객체를 clicked 할 때, self.KFC() 함수를 호출하도록 시그널과 슬롯을 연결한다.
  • 즉, User가 버튼을 누르는 순간, 버튼이 눌리는 signal 인 clicked 가 발생한다.
  • slot은 버튼의 clicked 시그널이 발생하면, KFC() 호출해준다.

 

💡[참고] 몇몇 widget의 Event

더보기

▶ QPushButton 이벤트

  • pressed(): 버튼을 누를 때마다 발생
  • released(): 버튼에서 손을 뗄 때마다 발생
  • toggled(bool): 버튼의 상태가 변경될 때마다 발생

 

▶ QLabel 이벤트

  • mousePressEvent(event): 마우스 버튼을 누르면 발생
  • mouseReleaseEvent(event): 마우스 버튼을 뗄 때 발생
  • mouseMoveEvent(event): 마우스를 움직일 때 발생
  • enterEvent(event): 위젯에 마우스 커서가 들어갈 때 발생
  • leaveEvent(event): 위젯에서 마우스 커서가 나갈 때 발생
  • keyPressEvent(event): 키를 누르면 발생
  • keyReleaseEvent(event): 키를 뗄 때 발생
  • focusInEvent(event): 위젯이 포커스를 받을 때 발생
  • focusOutEvent(event): 위젯이 포커스를 잃을 때 발생

 

▶ QLineEdit 이벤트

  • editingFinished(): 편집이 완료되었을 때 발생
  • returnPressed(): 엔터 키를 눌렀을 때 발생
  • textChanged(const QString &text): 텍스트가 변경될 때 발생
  • cursorPositionChanged(int old, int new): 커서 위치가 변경될 때 발생

 

▶ QCheckBox 이벤트

  • stateChanged(int state): 체크박스의 상태가 변경될 때 발생하는 시그널. 상태는 Qt.CheckState로 나타남.

 

QRadioButton 이벤트

  • toggled(bool checked): 라디오 버튼이 토글될 때 발생

 

QListWidget 이벤트

  • currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous): 현재 선택된 항목이 변경될 때 발생
  • itemClicked(QListWidgetItem *item): 항목을 클릭할 때 발생
  • itemDoubleClicked(QListWidgetItem *item): 항목을 더블 클릭할 때 발생

 


 

Qt Desiner & Layout

 

Qt Designer란?
  • 위젯을 코드로 생성하고 배치하는 것이 아닌, 마우스를 이용하여 편리하게 배치하는 프로그램 (즉, GUI 프로그램)

 

 

Layout을 써야하는 이유
  • 현대 장치들은 Display 사이즈가 모두 다르기 때문에 절대좌표/크기로 위젯을 배치하면 제대로 보이지 않는다.
  • => 따라서 Layout 기반 배치를 통해 다양한 크기의 Display에서도 모두 동작할 수 있도록 해주어야 한다.

 

 

💚Layout 

⭐각각의 widget을 독립적으로 먼저 만든 후, 

상위객체.addWidget(하위객체) 함수를 이용해 widget을 넣고

상위객체.addLayout( 하위객체 ) 함수를 이용해 layout을 넣는

방식으로 개발한다! 

#수직 배치, 수평 배치 레이아웃을 같이 사용한 샘플코드
#디자인을 보고, 어떤 레이아웃을 사용할 지 결정하고 사용해야 한다.
#QHBoxLayout : 수평배치 레이아웃 생성
#.addLayout() : 레이아웃을 추가하는 API

from PySide6.QtWidgets import *
class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.main()

    def main(self):
        self.setWindowTitle("Qt GUI App")
        self.setGeometry(0, 0, 300, 300)
        
        #버튼 3개 생성
        self.btn1 = QPushButton("Top")
        self.btn2 = QPushButton("LEFT")
        self.btn3 = QPushButton("RIGHT")
        
        #수직배치, 수평배치 레이아웃 생성
        self.vlay = QVBoxLayout(self)
        self.hlay = QHBoxLayout(self)
        
        #수직배치 레이아웃에 btn1 추가
        self.vlay.addWidget(self.btn1)
        
        #수평배치 레이아웃에 btn2, btn3 추가
        self.hlay.addWidget(self.btn2)
        self.hlay.addWidget(self.btn3)
        
        #수직배치 레이아웃에 수평배치 레이아웃 
        self.vlay.addLayout(self.hlay)

if __name__ == '__main__':
    app = QApplication()
    win = MyApp()
    win.show()
    app.exec()

 

 

 수직 레이아웃 객체 생성

QVBoxLayout(부모객체)

 

 수평 레이아웃 객체 생성

QHBoxLayout(부모객체)

 

▶ 레이아웃에 객체 추가

레이아웃객체.addWidget(추가할객체)

 

▶ 레이아웃에 레이아 추가

레이아웃객체.addLayout(추가할객체)

 

수직배치애 객체를 add하면 수직으로 들어가고,

수평배치에 객체를 add하면 수평으로 들어간다!

 

 

💡 [참고] 코드순서 == 배치순서이다.

  • addWidget() , addLayout() 등 코드 작성 순서가 GUI 화면의 배치 순서로 결정된다.