본문 바로가기
IT, Software/Python

Python PyQt로 윈도우 어플 만들기 - Thread 정보 UI에 업데이트하기

by 기타마을이장 2021. 1. 27.

대부분의 GUI 프로그램들이 그렇듯이 UI 에서는 사용자 액션을 처리하고,

실제 기능수행은 엔진 영역에 별도로 처리하도록 개발해야 한다.

그래야 어플이 Freezing 되는 걸 막을 수 있다.(일명 어플리케이션 일시 멈춤)

또, 잘못된 조작 혹은 어플이 동작중인 상태에서 중간에 중단시키는 동작들도 가능해진다.

이를 위해서 일반적으로 사용되는 방법이 엔진영역(Worker Thread)와 과 UI영역(Window)를 분리하는 방법이다.

 

이번에 만들고 있는 어플도 사용자 액션과 상태 표시등은 UI에 하고,

액션에 따른 실제 기능 동작 수행은 Thread로 구현했다.

그리고 Thread에서 중간중간 수행하는 과정 등을 Window에 표시하거나,

기능 동작에 따른 버튼 상태 등을 변경하려 한다.

 

PyQt UI코드와 엔진 영역 Thread 간에 Event Handler 추가하기

이를 위해서는 Qt Designer를 통해 생성된 UI코드와(Window영역)

실제 어플이 동작하기 위해 기능을 처리하는 엔진영역(Thread) 사이를 연결해 줘야 한다.

(그냥 간단한 테스트 정도의 어플을 만들 거라면 UI코드에 엔진 코드도 같이 넣어도 무방하기는 하겠지만;;;)

이를 위해서 방법을 찾다 보니 역시 국내 블로그에서는 못 찾았고

구글링을 통해 StackOverflow에서 방법을 찾아냈다.

방법은 pyqtSignal 을 사용하는 것이었다.

 

 

Update PyQt GUI from a Python thread

I have a GUI made in Designer (pyqt5). A function in my main class needs to work on a separate thread. I also catch the stdout on a QtextEdit LIVE during operations. Everything so far works. Right...

stackoverflow.com

class Form(QMainWindow):
    finished = pyqtSignal()
    updateProgress = pyqtSignal(int)

    def __init__(self, parent=None):
	..코드 생략..
	self.finished.connect(self.end_task)
        self.updateProgress.connect(self.ui.progressBar.setValue)

    def start_task(self):
        self.thread = threading.Thread(target=self.run_test)
        self.thread.start()
        self.ui.pushButton_run.setEnabled(False)

    def end_task(self):
        self.ui.pushButton_run.setEnabled(True)

	..코드 생략..

    def run_test(self):
        for i in range(100):
            per = i + 1
            self.updateProgress.emit(per)
            print("%%%s" % per)
            time.sleep(0.15)  # simulating expensive task

        print("Task Completed!")
        time.sleep(1.5)
        self.ui.progressBar.reset()
        self.finished.emit()

링크의 샘플 코드를 간단히 정리하면...

Thread에서 UI 처리를 위한 이벤트 발생시키기

  1. Window Class에 finished 라는 pyqtSignal() 전역 변수를 정의해두고
  2. finished 변수에 connect method로 시그널이 발생되는 경우 연결되어 수행할 함수를 정의해 준다.
    샘플 코드에서는 end_task 함수를 연결해 줬다.
  3. 그리고 thread가 종료되는 시점에 finished 변수의 emit method를 호출해줘서
    UI의 end_task 함수가 최종 호출되게 된다.

Thread에서 UI처리 이벤트 발생할 때 변수 함께 넘기기

thread에서 시그널을 발생시킬 때 변수로 함께 넘길 수 있다.

progress 바를 업데이트해주기 위해 finished 와 동일한 방식으로 updateProgress 변수도 정의했는데

pyqtSignal() 메서드에 파라미터 형태를 int 미리 정의해두면

int 타입의 value를 넘길 수 있다.

역시 세상의 모든 코드와 그 해법은 StackOverflow에 있나 보다ㅠ

반응형

댓글