자체 정의 신호와 내장 신호의 주요 차이점은 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)