PyQt5 신호 및 슬롯 활용 방법

자체 정의 신호와 내장 신호의 주요 차이점은 emit 호출이 필요하다는 점입니다. 이 점을 반드시 기억해야 합니다.

주의 사항
(1) 자체 정의 신호는 __init__() 함수 전에 정의해야 합니다.
(2) 신호는 int, list, dict, tuple, object 등 다양한 타입의 파라미터를 전달할 수 있습니다.
(3) 신호와 슬롯 간의 호출 로직을 주의하여 무한 루프가 발생하지 않도록 해야 합니다.

하나의 신호는 여러 슬롯과 연결될 수 있으며, 하나의 신호도 다른 신호와 연결될 수 있습니다. 반대로 하나의 슬롯은 여러 신호를 감시할 수 있습니다. 신호 파라미터는 Python의 모든 데이터 타입을 사용할 수 있으며, 동기식 또는 비동기식 방식으로 연결이 가능합니다. 또한 신호-슬롯 연결은 스레드 간에 이루어질 수 있고, 연결은 언제든지 해제될 수 있습니다.

1. 신호 정의

클래스 멤버 변수로 신호 객체를 정의합니다.

class MyWidget(QWidget):  
    # 파라미터 없는 신호
    signal_no_params = pyqtSignal()     
    # 정수형 파라미터를 갖는 신호      
    signal_one_int = pyqtSignal(int)         
    # 정수 또는 문자열 파라미터를 갖는 오버로딩 신호        
    signal_one_overload = pyqtSignal([int],[str])  
    # 두 개의 파라미터를 갖는 신호      
    signal_two_params = pyqtSignal(int,str)    
    # [int,int] 또는 [int,str] 형식의 오버로딩 신호      
    signal_two_overload = pyqtSignal([int,int],[int,str]) 

2. 슬롯 함수 정의

다양한 입력 파라미터를 받는 슬롯 함수를 정의합니다.

class MyWidget(QWidget):  
    def set_no_params(self):   
        '''파라미터 없는 슬롯'''  
        pass  

    def set_one_int(self,nIndex):   
        '''정수형 파라미터 슬롯'''  
        pass

    def set_one_str(self,szIndex):   
        '''문자열 파라미터 슬롯'''  
        pass 

    def set_two_ints(self,x,y):   
        '''두 개의 정수 파라미터 슬롯'''  
        pass  

    def set_int_str(self,x,szY):   
        '''정수와 문자열 파라미터 슬롯'''  
        pass

3. 신호-슬롯 연결

connect 메서드를 사용해 신호와 슬롯을 연결합니다.

app = QApplication(sys.argv)   
widget = MyWidget()   
# 파라미터 없는 신호 연결
widget.signal_no_params.connect(widget.set_no_params)                                          

# 정수 파라미터 신호 연결
widget.signal_one_int.connect(widget.set_one_int)                                         

# 오버로딩 신호 연결
widget.signal_one_overload[int].
    connect(widget.set_one_int)                              

widget.signal_one_overload[str].
    connect(widget.set_one_str )                     

# 두 개의 파라미터 신호 연결
widget.signal_two_params.connect(widget.set_two_ints )                                        

widget.signal_two_overload[int,int].
    connect(widget.set_two_ints )                      

widget.signal_two_overload[int,str].
    connect(widget.set_int_str )              
widget.show()  

4. 신호 발송

emit 메서드를 통해 신호를 발송합니다.

class MyWidget(QWidget):  

    def mousePressEvent(self, event):  
        # 파라미터 없는 신호 발송
        self.signal_no_params.emit() 
        # 정수 파라미터 신호 발송
        self.signal_one_int.emit(1) 
        # 오버로딩 신호 발송
        self.signal_one_overload.emit(1)
        self.signal_one_overload.emit("abc")
        # 두 파라미터 신호 발송
        self.signal_two_params.emit(1,"abc")

5. 예제 코드

파일명: PyQt5/Chapter07/qt07_signalSlot02.py

from PyQt5.QtCore import QObject , pyqtSignal

class CustomSignal(QObject):

    # 파라미터 없는 신호
    sig1 = pyqtSignal()

    # 정수형 파라미터 신호
    sig2 = pyqtSignal(int)

    # 정수+문자열 파라미터 신호
    sig3 = pyqtSignal(int,str)

    # 리스트형 파라미터 신호
    sig4 = pyqtSignal(list)

    # 딕셔너리형 파라미터 신호
    sig5 = pyqtSignal(dict)

    # 다중 오버로딩 신호
    sig6 = pyqtSignal([int,str], [str])

    def __init__(self,parent=None):
        super(CustomSignal,self).__init__(parent)

        # 신호-슬롯 연결
        self.sig1.connect(self.slot1)
        self.sig2.connect(self.slot2)
        self.sig3.connect(self.slot3)
        self.sig4.connect(self.slot4)
        self.sig5.connect(self.slot5)
        self.sig6[int,str].connect(self.slot6)
        self.sig6[str].connect(self.slot6_overload)

        # 신호 발송
        self.sig1.emit()
        self.sig2.emit(1)
        self.sig3.emit(1,"text")
        self.sig4.emit([1,2,3,4])
        self.sig5.emit({"name":"wangwu","age":"25"})
        self.sig6[int,str].emit(1,"text")
        self.sig6[str].emit("text")

    def slot1(self):
        print("sig1 발송")

    def slot2(self,val):
        print("sig2 발송,value:",val)

    def slot3(self,val,text):
        print("sig3 발송,value:",val,text)

    def slot4(self,val):
        print("sig4 발송,value:",val)

    def slot5(self,val):
        print("sig5 발송,value:",val)

    def slot6(self,val,text):
        print("sig6 발송,value:",val,text)

    def slot6_overload(self,val):
        print("sig6 오버로딩 발송,value:",val)

if __name__ == '__main__':  
    custSignal = CustomSignal()

실행 결과:

sig1 발송
sig2 발송,value: 1
sig3 발송,value: 1 text
sig4 발송,value: [1, 2, 3, 4]
sig5 발송,value: {'name': 'wangwu', 'age': '25'}
sig6 발송,value: 1 text
sig6 오버로딩 발송,value: text

2. 커스텀 파라미터 전달

신호가 0개의 파라미터를 전달하고 슬롯이 1개의 파라미터를 기대하는 경우 해결 방법

button1.clicked.connect(lambda: self.on_click(1)) 
button2.clicked.connect(lambda: self.on_click(2))

lambda 표현식을 사용하거나 functools.partial을 활용해 파라미터를 전달할 수 있습니다.

3. 데코레이터 활용 신호-슬롯

데코레이터를 사용해 신호와 슬롯을 정의하는 방법:

@QtCore.pyqtSlot()
def on_okButton_clicked(self):
    print("OK 버튼 클릭")

이를 위해 QMetaObject.connectSlotsByName 메서드를 호출해야 하며, 객체 이름을 설정해야 합니다.

4. 신호-슬롯 연결 해제 및 재연결

특정 신호-슬롯 연결을 임시 또는 영구적으로 해제하는 방법:

signal1.disconnect(slot1)
signal1.disconnect(slot2)

5. 멀티스레드에서의 신호-슬롯 활용

QThread를 사용한 멀티스레드 예제:

class BackendThread(QThread):
    update_date = pyqtSignal(str)

    def run(self):
        while True:
            data = QDateTime.currentDateTime()
            currTime = data.toString("yyyy-MM-dd hh:mm:ss")
            self.update_date.emit(str(currTime))
            time.sleep(1)

태그: PyQt5 신호 슬롯 멀티스레드 오버로딩

6월 12일 19:39에 게시됨