본문 바로가기

코딩/Python

[Python/파이썬] PyQt5 사용시 필요한 쓰레드 개념 이해 : 파이썬 GUI 응답없음 해결 방법 - 2

반응형

※ 이 글을 쓰는 사람은 SW 비전공자입니다.

※ 개인 공부를 위해 정리하는 글이며, 작성한 코드들은 효율성, 깔끔함(?) 등과는 거리가 멀 수 있습니다.

 

1편 : 2021.03.06 - [코딩/Python] - [Python/파이썬] PyQT5 및 QT Designer 소개, .ui 파일 .py로 변환 방법

2편 : 2021.03.28 - [코딩/Python] - [Python/파이썬] PyQt5를 통한 GUI 구성 및 사용법 이해하기

3편 : 2021.03.29 - [코딩/Python] - [Python/파이썬] PyQt5 사용시 필요한 쓰레드 개념 이해 - 1

4편 : 2021.03.30 - [코딩/Python] - [Python/파이썬] PyQt5 사용시 필요한 쓰레드 개념 이해-2 (GUI 응답없음 해결)

5편 : 2021.07.19 - [코딩/Python] - [Python/파이썬] PyQt5 - 시그널(Signal)과 슬롯(Slot) 개념

6편 : 2021.07.22 - [코딩/Python] - [Python/파이썬] PyQt5 - 사용자 정의 시그널(Custom Signal)과 Emit 사용법

 


 

  이전편에서 파이썬 쓰레드와 GIL(Global Interpreter Lock)이라는 개념을 정리했다. 요약하자면 쓰레드는 프로세스 내에서 여러개의 동작을 동시에 할 수 있게 한다. 파이썬에서는 멀티 쓰레드를 지원하나 GIL 때문에 쓰레드를 동시 동작할 수는 없고 여러개의 쓰레드를 번갈아 실행한다고 했었다.

 

  이번 포스팅에서는 이전 편의 경주마 예시를 쓰레드 개념을 통해 동작시켜보려고 한다. 추가로 콘솔 창에 출력되는 결과를

GUI의 Textbrowser 까지 활용하여 구현해보았다. 아래 image는 이전 편의 GUI를 그대로 가져온 것이다. 

 

 

  코드를 구현하기 전에 파이썬의 쓰레드 구현법에는 Threading이라는 내장 모듈을 활용하는 방법이 있다. PyQt5와 같이 GUI기반의 쓰레드를 사용하고 싶다면 PyQt5의 Qthread 클래스를 사용하면 된다. 참고로, GUI라고 해서 Threading 모듈을 사용하지 못하는 것은 아니다. 이번 편에서는 Qthread를 활용하였다. 기본적인 사용법은 아래 내용을 정리하였으니 참고!!

 

  QThread 코드를 이해하려면 파이썬 클래스의 개념과 클래스 상속을 이해해야 한다. 이전에 클래스 이해를 위해 포스팅을 했어서 관련 링크를 첨부함.


※ 참고링크 

2021.03.14 - [코딩/Python] - [Python/파이썬] Class(클래스) 기초 정리 - 1 : 개념, 사용법

2021.03.20 - [코딩/Python] - [Python/파이썬] Class(클래스) 기초 정리 - 2 : has-a 관계, 상속 개념


1. QThread 사용법

 

<코드>

 

import time
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
from PyQt5.QtCore import *

#QThread 클래스 선언하기, QThread 클래스를 쓰려면 QtCore 모듈을 import 해야함.
class Thread(QThread):
    #초기화 메서드 구현    
    def __init__(self, parent): #parent는 WndowClass에서 전달하는 self이다.(WidnowClass의 인스턴스)
        super().__init__(parent)    
        self.parent = parent #self.parent를 사용하여 WindowClass 위젯을 제어할 수 있다.
        
    def run(self)
        #쓰레드로 동작시킬 함수 내용 구현
 
class WindowClass(QMainWindow, form_class): 
    def threadAction(self):
        x = Thread(self) #self는 WindowClass의 인스턴스, Thread 클래스에서 parent로 전달
        x.start() #쓰레드 클래스의 run 메서드를 동작시키는 부분

if __name__ == "__main__" :
    app = QApplication(sys.argv)
    myWindow = WindowClass()
    myWindow.show()
    app.exec_()        

 

 

1) class Thread(Qthread)

 

-. 쓰레드 클래스를 구현할 때 PyQt5의 Qthread 클래스를 상속받으면 된다.  Qthread 클래스는 PyQt 의 QtCore 모듈에 포함된다.

 

-. parent의 경우 WindowClass의 인스턴스를 받는 인자이다.

   (코드에서 x= Thread(self)의 self가 인수가 됨)

 

-. def run() 메서드에 직접 실행시킬 내용을 작성한다. 쓰레드를 구현하기 위해선 쓰레드 클래스의 메서드를 run으로 지정해야 한다.

 

 

2) class WindowClass(QMainWindow, form_class)

 

-. WindowClass의 경우 GUI를 구성하는 클래스이다. QMainWindow와 form_class를 상속 받는다.

 

    ▶  QMainWindow : PyQt5로 GUI 구성시 윈도우 창을 생성하는 클래스

    ▶  form_class : 이전 포스팅에서 사용했던 부분으로 .ui 파일을 직접 불러와서  클래스로 받는 부분

 

-. x = Thread(self),   x.start()

  

    Thread 클래스의 객체 x는 선언하고 x의 run 메서드를 동작시키는 부분이다. 이 때, self는 Thread 클래스의 parent

         인수로 전달되는 인자이다.

 

 

2. 실제 코드 구현(경주마1, 경주마2 예시)

 

    쓰레드 클래스의 인스턴스인 x를 생성해서 run을 동작시키는 부분이다. 위와 같이 쓰레드 코드를 구현하면 GUI와 함수가 따로 동작하게 할 수 있다. 그래서 위 내용을 활용해서 이전 포스팅의 경주마 예시를 쓰레드로 구현해보았다.

 

 

<코드>

import time
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
from PyQt5.QtCore import *

#UI파일 연결
#단, UI파일은 Python 코드 파일과 같은 디렉토리에 위치해야한다.
form_class = uic.loadUiType("test_pyqt2.ui")[0]

#경주마 1을 위한 쓰레드 클래스
class Thread1(QThread):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent

    def run(self):
        self.parent.textBrowser.append("경주마1이 출발하였습니다.")
        for i in range(20):
            self.parent.textBrowser.append("경주마1이"+str(i)+"km째 달리고 있습니다.")
            time.sleep(2)      
        self.parent.textBrowser.append("경주마1이 결승지에 도착하였습니다.")

#경주마 2를 위한 쓰레드 클래스
class Thread2(QThread):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent

    def run(self):
        self.parent.textBrowser.append("경주마2가 출발하였습니다.")
        for i in range(20):
            self.parent.textBrowser.append("경주마2가"+str(i)+"km째 달리고 있습니다.")
            time.sleep(2)      
        self.parent.textBrowser.append("경주마2가 결승지에 도착하였습니다.")

class WindowClass(QMainWindow, form_class) :
    def __init__(self) :
        super().__init__()
        self.setupUi(self)

        #각 버튼에 대한 함수 연결
        self.runButton1.clicked.connect(self.actionFunction1)
        self.runButton2.clicked.connect(self.actionFunction2)

    #경주마1 출발 버튼을 눌렀을 때 실행 될 메서드
    def actionFunction1(self):
        h1 = Thread1(self)
        h1.start()
    
    #경주마2 출발 버튼을 눌렀을 때 실행 될 메서드
    def actionFunction2(self):
        h2 = Thread2(self)
        h2.start()

if __name__ == "__main__" :
    app = QApplication(sys.argv)
    myWindow = WindowClass()
    myWindow.show()
    app.exec_()

 

-. 코드가 길어 복잡해보이지만 이전편의 멀티 쓰레드 개념도를 생각하여 구현해보았다.(쓰레드 2개) 

    위 1번의 사용법 코드를 그대로 사용하여 2개의 Thead 클래스를 구현하였다.

 

<파이썬 멀티쓰레드 도식화 - 쓰레드 2개>

 

-. 프로그램 결과는 GUI가 멈추지 않고 textBrowser 창에 결과를 출력한다.

-. 경주마 1과 경주마 2가 번갈아 달리고 있는 것을 확인할 수 있다.

-. 같은 방법을 사용하여 진행 막대(progressBar)도 프로그램에 구현 가능하다.

 

 

< 2개 Thread 구현시 프로그램 결과 >

 

 

 

 

728x90