개요
오늘은 각 버튼마다 다른 QThread
워커를 생성한 프로그램을 만들어보려고 한다.
GUI 프로그램에서 멀티스레딩은 사용자 인터페이스가 응답성을 유지하면서 백그라운드 작업을 수행하는 데 필수적이다.
특히 크롤링 작업은 시간이 오래 걸릴 수 있으므로 각 작업을 별도의 스레드에서 실행하는 것을 권장한다.
실습 소개
이번 실습에서는 GUI를 만들고 각 버튼을 클릭하면 서로 다른 크롤링 작업을 수행하도록 구현한다.
각 크롤링 작업은 별도의 QThread에서 실행되어 메인 스레드의 UI가 응답성을 유지하도록 한다.
UI 설계 (Qt Designer)
- 1. Qt Designer 실행
- 2. Main Window 템플릿 선택
- 3. 중앙 위젯에 두 개의 버튼 추가(Push Button)
- 첫 번째 버튼: Object name을
btnCrawl1
로 설정 - 두 번째 버튼: Object name을
btnCrawl2
로 설정
- 첫 번째 버튼: Object name을
- 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()
설명
Worker1
과Worker2
는 각각 다른 크롤링 작업을 수행한다.finished
와progress
신호를 통해 작업이 끝났을 때와 진행 상황을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_crawl1
과start_crawl2
함수에서 버튼 클릭 시 각각의Worker1
과Worker2
를QThread
에 할당하여 스레드에서 작업을 시작한다.update_status
는statusBar()
를 통해 현재 작업의 진행 상태를 보여준다.
코드의 목적
이 프로그램의 주요 기능은 멀티스레딩을 통한 작업 분리이다.
각 버튼을 클릭할 때마다 다른 워커가 다른 스레드에서 실행되므로 UI 응답성이 유지된다.
QThread
와Signal
/Slot
:QThread
와Signal
을 활용해 메인 스레드와 워커 스레드 간에 비동기적으로 메시지를 주고받는다.- 다양한 작업 처리 가능:
Worker1
과Worker2
는 서로 다른 크롤링 로직을 수행할 수 있다.- 크롤링 작업에 맞춰 각 워커 클래스의
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