이 글은 PyQt 시니얼 개발자인 Martin Fitzpatrick의 PyQt 튜토리얼 을 참조하여 작성하였음을 밝힙니다.
PyQt는 QApplication
클래스를 중심으로 작동합니다.
Qapplication 클래스는 하나의 parameter로 작동하고, Qt Event Loop를 작동시키는 역할을 합니다.
Event Loop 은 애플리케이션 내에서 무언가 발생할 때 까지 끊임없이 돌아갑니다. 애플리케이션 내의 모든 활동(커서를 움직인다던지, 클릭을 한다던지)은 Event를 발생시키고, 이 Event는 Event Handler 내부에 있는 Event Queue에 기록됩니다.
Event Handler는 Event와 관련된 일련의 프로세스를 컨트롤합니다.
애플리케이션 하나당, Event Loop은 단 하나만 존재합니다.
그렇다면 실제 코드로 QApplication을 통해 Event Loop를 돌려보겠습니다.
# 애플리케이션당 QApplication instance는 단 하나만 존재한다.
# Command Line Argument를 사용하지 않을 것이라 판단되면, 빈 리스트를 파라미터로 집어넣어도 된다.
app = QApplication(sys.argv)
# 애플리케이션의 윈도우로 이용될 Qt Widget을 만든다.
# QWidget의 default는 hidden임이므로, 꼭 두 번째 줄을 추가해야 한다.
window = QWidget()
window.show()
# Event Loop을 시작한다.
app.exec()
# 애플리케이션은 Event Loop가 끝날 때까지 이 지점에 오지 못한다!
코드를 잘 작성했으면, 다음과 같은 창이 뜹니다. 이 창이 바로 애플리케이션의 뼈대가 될 Window입니다.
Qt에서는 모든 top level Widget은 window입니다.즉, 모든 윈도우는 종속된 Widget이 없습니다. 따라서, 우리는 어떤 Widget을 이용하더라도 Window를 만들 수 있습니다.
Qt에서는 어떤 widget이든지 Window가 될 수 있습니다. 따라서 직전의 코드를 아래와 같이 바꿔도, 잘 작동하게 됩니다.
import sys
from PyQt5.QtWidgets import QApplication, QPushButton
app = QApplication(sys.argv)
window = QPushButton("Push Me") # QWidget 대신에
window.show()
app.exec()
이런 논리로, PyQt에서는 Widget 위에 Widget을 쌓아나갈 수 있고, 이를 통해 아무것도 없는 빈 Window로부터 시작해서 다양한 기능을 지닌 복잡한 UI를 구축할 수 있습니다.
하지만, PyQt에서는 여러 표준적인 위젯들을 담고 있는 QMainWindow
라는 package를 제공해줍니다.
현재로써는 특별히 특이한 기능을 애플리케이션에 집어 넣을 생각이 없어 그냥 이 package를 이용하기로 합니다.
실행을 해보니, 외관상 아까와 동일한 창이 하나 만들어졌습니다.
애플리케이션을 구현하기 위해서는 이보다 훨씬 많은 기능들이 필요할텐데요, 대부분의 Qt Widget들은 QtPy5.QtWidgets
에서 import할 수 있습니다.
그렇다면, Signal과 Slot에 대한 간단한 예시로, QpushButton
기능을 통해 버튼을 눌러 Signal을보내는 프로그램을 구현해 보도록 하겠습니다.
먼저, "Clicked!"라는 메세지를 출력하는 함수를 하나 만들어, 버튼을 누르는 Signal과 연동시켜보겠습니다.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
button.setCheckable(True)
button.clicked.connect(self.the_button_was_clicked)
# Set the central widget of the Window.
self.setCentralWidget(button)
def the_button_was_clicked(self):
print("Clicked!")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
다음으로, 버튼의 state를 <눌려있음, 눌려있지 않음> 두 개로 만들어 boolean data를 전송해보겠습니다.
참고로 이 기능은, 애플리케이션에서 타이머를 만들 때 유용하게 쓰일 기능이라 잘 정리해두려고 합니다.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
button.setCheckable(True)
button.clicked.connect(self.the_button_was_clicked)
button.clicked.connect(self.the_button_was_toggled)
self.setCentralWidget(button)
def the_button_was_clicked(self):
print("Clicked!")
def the_button_was_toggled(self, checked):
print("Checked?", checked)
현재의 상태를 저장해놓는것은 많은 경우에 유용합니다.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.button_is_checked = True
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
button.setCheckable(True)
button.clicked.connect(self.the_button_was_toggled)
button.setChecked(self.button_is_checked)
self.setCentralWidget(button)
def the_button_was_toggled(self, checked):
self.button_is_checked = checked
print(self.button_is_checked)
Qt 애플리케이션에서 일어나는 유저와의 모든 상호작용은 Event입니다. Qt에는 많은 종류에 이벤트가 정의되어 있고, 각각은 다른 형태의 상호작용을 나타냅니다.
Qt에서는 Event Object를 이용해 어떤 일이 일어났는지에 대한 정보를 package up하고, 이는 위젯 안의 Event Handler 에게 넘겨져 처리됩니다.