Profile picture

[Python / GUI] PySide6: 버튼별 QThread 만들어보기

JaehyoJJAng2024년 08월 19일

개요

오늘은 각 버튼마다 다른 QThread 워커를 생성한 프로그램을 만들어보려고 한다.

GUI 프로그램에서 멀티스레딩은 사용자 인터페이스가 응답성을 유지하면서 백그라운드 작업을 수행하는 데 필수적이다.

특히 크롤링 작업은 시간이 오래 걸릴 수 있으므로 각 작업을 별도의 스레드에서 실행하는 것을 권장한다.


실습 소개

이번 실습에서는 GUI를 만들고 각 버튼을 클릭하면 서로 다른 크롤링 작업을 수행하도록 구현한다.

각 크롤링 작업은 별도의 QThread에서 실행되어 메인 스레드의 UI가 응답성을 유지하도록 한다.


UI 설계 (Qt Designer)

  • 1. Qt Designer 실행
  • 2. Main Window 템플릿 선택
  • 3. 중앙 위젯에 두 개의 버튼 추가(Push Button)
    • 첫 번째 버튼: Object name을 btnCrawl1로 설정
    • 두 번째 버튼: Object name을 btnCrawl2로 설정
  • 4. UI 파일을 layout.ui 로 저장.

워커 클래스 구현하기

각 버튼에 대응하는 워커 클래스를 만들어야 한다.

워커 클래스는 QObject를 상속하고, 실제 크롤링 로직을 run 메소드 구현한다.

# workers.py
from PySide6.QtCore import QObject, Signal, Slot
import time  # 실제 크롤링 시 필요한 모듈로 대체

class Worker1(QObject):
    finished = Signal()
    progress = Signal(str)

    @Slot()
    def run(self):
        # 크롤링 로직 구현
        for i in range(5):
            time.sleep(1)  # 크롤링 작업 시뮬레이션
            self.progress.emit(f"Worker1: {i+1}단계 완료")
        self.finished.emit()

class Worker2(QObject):
    finished = Signal()
    progress = Signal(str)

    @Slot()
    def run(self):
        # 다른 크롤링 로직 구현
        for i in range(3):
            time.sleep(2)  # 크롤링 작업 시뮬레이션
            self.progress.emit(f"Worker2: {i+1}단계 완료")
        self.finished.emit()

설명

  • Worker1Worker2는 각각 다른 크롤링 작업을 수행한다.
  • finishedprogress 신호를 통해 작업이 끝났을 때와 진행 상황을 MainWindow에 알린다.

메인 애플리케이션 구현하기

메인 프로그램에서는 UI를 로드하고, 버튼 클릭 이벤트에 워커와 스레드를 연결한다.

# main.py
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QThread
from PySide6.QtUiTools import QUiLoader
from workers import Worker1, Worker2

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # UI 로드
        loader = QUiLoader()
        with open("main_window.ui", "r", encoding="utf-8") as ui_file:
            self.ui = loader.load(ui_file, self)
        self.setCentralWidget(self.ui)

        # 버튼 이벤트 연결
        self.ui.btnCrawl1.clicked.connect(self.start_crawl1)
        self.ui.btnCrawl2.clicked.connect(self.start_crawl2)

    def start_crawl1(self):
        # 스레드 및 워커 생성
        self.thread1 = QThread()
        self.worker1 = Worker1()
        self.worker1.moveToThread(self.thread1)

        # 시그널 및 슬롯 연결
        self.thread1.started.connect(self.worker1.run)
        self.worker1.finished.connect(self.thread1.quit)
        self.worker1.finished.connect(self.worker1.deleteLater)
        self.thread1.finished.connect(self.thread1.deleteLater)
        self.worker1.progress.connect(self.update_status)

        # 스레드 시작
        self.thread1.start()

    def start_crawl2(self):
        # 스레드 및 워커 생성
        self.thread2 = QThread()
        self.worker2 = Worker2()
        self.worker2.moveToThread(self.thread2)

        # 시그널 및 슬롯 연결
        self.thread2.started.connect(self.worker2.run)
        self.worker2.finished.connect(self.thread2.quit)
        self.worker2.finished.connect(self.worker2.deleteLater)
        self.thread2.finished.connect(self.thread2.deleteLater)
        self.worker2.progress.connect(self.update_status)

        # 스레드 시작
        self.thread2.start()

    def update_status(self, message):
        # 상태 표시줄에 메시지 출력
        self.ui.statusbar.showMessage(message)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

설명

  • start_crawl1start_crawl2 함수에서 버튼 클릭 시 각각의 Worker1Worker2QThread에 할당하여 스레드에서 작업을 시작한다.
  • update_statusstatusBar()를 통해 현재 작업의 진행 상태를 보여준다.

코드의 목적

이 프로그램의 주요 기능은 멀티스레딩을 통한 작업 분리이다.

각 버튼을 클릭할 때마다 다른 워커가 다른 스레드에서 실행되므로 UI 응답성이 유지된다.

  • QThreadSignal/Slot: QThreadSignal을 활용해 메인 스레드와 워커 스레드 간에 비동기적으로 메시지를 주고받는다.
  • 다양한 작업 처리 가능: Worker1Worker2는 서로 다른 크롤링 로직을 수행할 수 있다.
    • 크롤링 작업에 맞춰 각 워커 클래스의 run 메서드에 적절한 크롤링 코드를 추가할 수 있다.

확장 및 응용

더 많은 크롤링 작업이 필요하다면, 새로운 버튼과 워커 클래스를 추가하면 된다.

# workers.py에 새로운 워커 클래스 추가
class Worker3(QObject):
    finished = Signal()
    progress = Signal(str)

    @Slot()
    def run(self):
        # 새로운 크롤링 로직 구현
        pass
# main.py에서 새로운 버튼 이벤트 연결
self.ui.btnCrawl3.clicked.connect(self.start_crawl3)

def start_crawl3(self):
    # 스레드 및 워커 생성 및 연결
    pass

Loading script...