PyQt개발강좌, QRunnable과 QThreadPool을 사용한 작업 스케줄링

PyQt는 강력한 GUI 애플리케이션을 개발할 수 있도록 지원하는 Python 바인딩입니다. 그러나 GUI 애플리케이션의 성능을 높이기 위해서는
작업의 비동기 처리가 필요합니다. 이를 위해 PyQt는 QRunnableQThreadPool 클래스를 제공합니다.
이 강좌에서는 QRunnableQThreadPool을 사용하여 어떻게 비동기적으로 작업을 스케줄링할 수 있는지
자세히 설명하겠습니다.

비동기 프로그래밍이란?

비동기 프로그래밍(Asynchronous programming)은 작업을 동시에 처리할 수 있는 기술입니다. 일반적으로 GUI 애플리케이션은 사용자와
상호작용해야 하므로, 긴 작업을 수행할 때 애플리케이션이 응답하지 않게 되는 문제를 피해야 합니다.
비동기 프로그래밍을 이용하면 GUI가 사용자에게 반응을 계속 보여줄 수 있으면서도 백그라운드에서 긴 작업을 수행할 수 있습니다.

QRunnable과 QThreadPool 소개

QRunnable은 PyQt에서 작업 단위를 정의하는 클래스입니다. 이 클래스는 강력한 비동기 작업 처리를 위한
기본 구현을 제공합니다. QThreadPool은 여러 개의 QRunnable 객체를 동시에 실행할 수 있도록
관리하는 스레드 풀입니다. 스레드 풀을 사용하면 스레드를 수동으로 만들고 관리하는 것보다 훨씬 더 효율적입니다.

QRunnable 클래스

QRunnable을 사용하여 비동기 작업을 정의하기 위해서는 이 클래스를 상속받아
run 메서드를 재정의해야 합니다. 아래 예제는 QRunnable을 상속받아
간단한 작업을 수행하는 클래스를 보여줍니다.


import time
from PyQt5.QtCore import QRunnable, pyqtSlot


class Worker(QRunnable):
    @pyqtSlot()
    def run(self):
        print("작업 시작")
        time.sleep(5)  # 긴 작업을 시뮬레이션
        print("작업 완료")
    

QThreadPool 클래스

QThreadPool을 사용하여 QRunnable 작업을 관리할 수 있습니다.
QThreadPoolstart 메서드를 호출하면 QRunnable 작업이 스레드 풀에 추가되고
실행됩니다. 다음 코드는 QThreadPool을 사용하여 여러 개의 작업을 동시에 실행하는 예제입니다.


from PyQt5.QtCore import QThreadPool

# QThreadPool 생성
pool = QThreadPool()

# Worker 인스턴스 생성 및 작업 시작
for _ in range(5):
    worker = Worker()
    pool.start(worker)
    

비동기 작업 스케줄링 예제

아래는 PyQt를 사용한 간단한 GUI 애플리케이션 예제입니다. 이 애플리케이션에서는 버튼 클릭 시
백그라운드에서 비동기 작업을 수행합니다.


import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel
from PyQt5.QtCore import QThreadPool

class App(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        self.setWindowTitle('QRunnable 예제')
        layout = QVBoxLayout()

        self.label = QLabel('안녕! 버튼을 클릭하세요.')
        layout.addWidget(self.label)

        self.button = QPushButton('작업 시작')
        self.button.clicked.connect(self.startWork)
        layout.addWidget(self.button)

        self.setLayout(layout)

        self.threadpool = QThreadPool()

    def startWork(self):
        self.label.setText('작업 중...')
        worker = Worker()
        worker.signals.result.connect(self.updateLabel)
        self.threadpool.start(worker)

    def updateLabel(self, result):
        self.label.setText(result)

# 어플리케이션 실행
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    ex.show()
    sys.exit(app.exec_())
    

Signals and Slots (신호와 슬롯)

PyQt에서는 비동기 작업의 결과를 GUI에 반영하기 위해 신호와 슬롯 메커니즘을 사용합니다.
QRunnable과 함께 사용할 수 있는 커스텀 신호를 정의하여 작업 완료 후 GUI
업데이트를 수행할 수 있습니다. 아래는 Постановка для того чтобы создать ваши сигналы в Worker:


from PyQt5.QtCore import pyqtSignal

class Worker(QRunnable):
    signals = pyqtSignal(str)

    @pyqtSlot()
    def run(self):
        print("작업 시작")
        time.sleep(5) # 긴 작업을 시뮬레이션
        self.signals.emit("작업 완료")
    

결론

이번 강좌에서는 PyQt를 사용하여 QRunnableQThreadPool을 통해 비동기
작업을 스케줄링하는 방법을 알아보았습니다. 비동기 프로그래밍은 GUI 애플리케이션의 성능을 향상시키며
사용자 경험을 개선하는 데 중요한 역할을 합니다. 다양한 작업을 더 쉽게 관리할 수 있도록 PyQt의
강력한 기능을 활용해 보세요.