/ PROGRAMMING, PYTHON, GUI

PyQt6의 MultiThreading

이 글에서는 PyQt6에서 제공하는 클래스를 이용해 멀티쓰레딩을 이용하는 방법을 다룬다.

PyQt 정보


Multithreading in Qt

Qt에서는 크게 두 종류의 쓰레드가 존재할 수 있다. 첫 번째는 메인 윈도우를 실행하고 위젯들의 동작을 관리하는 메인 쓰레드이다. Qt 어플리케이션에서는 이 메인 쓰레드가 항상 존재하게 된다.

그런데 메인 쓰레드 내에서 발생하는 모든 동작은 순차적으로 처리되므로, 실행 시간이 오래 걸리는 슬롯 함수 등을 호출하면 GUI는 해당 함수가 실행을 마칠 때까지 유저와 상호작용할 수 없게 된다.

이러한 류의 task를 워커 쓰레드에 맡기고 그 결과를 어플리케이션의 GUI 컴포넌트에 반영하는 방식으로 프리징 현상을 해결할 수 있다. 이는 GUI 컴포넌트가 실제 작업을 수행하는 쓰레드로부터 정보를 받는 소비자(consumer)의 역할을 하게 됨을 뜻한다.


워커 쓰레드 QThread

QThread를 상속하여 만든 워커 쓰레드는 고유의 event loop를 가지며, PyQt의 시그널, 슬롯 메커니즘을 지원한다.

생성 방법

QThread를 이용한 쓰레드 생성에는 두 가지 방식이 있다.

  1. QObject를 상속하는 워커 클래스 생성 후, 수행하고자 하는 작업 메소드를 구현한다. 워커 오브젝트 인스턴스를 만들고, QThread() 인스턴스도 만든다. 그 이후에 워커의 .moveToThread() 메소드를 호출해 인자로 대상 쓰레드 인스턴스를 넣어 워커를 QThread화 한다. 이제 그 인스턴스에 대한 시그널과 슬롯을 connect한 후, .start() 메소드로 쓰레드를 시작한다.

  2. QThread를 상속하는 워커 클래스를 생성 후, 수행하고자 하는 작업 메소드를 run() 메소드에 구현한다. 워커 오브젝트 인스턴스를 만들고, 인스턴스에 대한 시그널과 슬롯을 connect한 후, .start() 메소드로 쓰레드를 시작한다.

첫 번째 방식은 고유의 이벤트 루프가 제공되나, 두 번째는 기본적으로 제공되지 않는다. .exec() 메소드를 호출함으로써 명시적으로 고유 이벤트 루프를 부여할 수 있다.

시그널

.started(), .finished: 각각 쓰레드가 시작, 종료 시 발생한다.

메소드

  • .start()

  • .wait()

  • .exit()

  • .quit(): 이벤트 루프가 없는 쓰레드에선 아무 효과가 없으니 주의해야 한다.

  • .isFinished()

  • .isRunning()


QRunnableQThreadPool

QRunnable 은 멀티쓰레딩 대상이 되는 작업을 위한 컨테이너에 해당하고, QThreadPool은 해당 작업을 쓰레딩할 수 있게 해주는 메소드에 해당한다.

class Worker(QRunnable):
    '''
    Worker thread
    '''

    @pyqtSlot()
    def run(self):
        '''
        Your code goes in this function
        '''
        print("Thread start")
        time.sleep(5)
        print("Thread complete")

QRunnable을 상속하여 원하는 작업을 수행할 Worker 클래스를 만든다.

이제 QThreadPool()로 생성한 인스턴스에 Worker의 인스턴스를 넘겨주면 된다.


참고문헌

PythonGUIS