系统地讲解信号与槽(✘),系统过一遍信号与槽(✔)(狗头)
这可以说是Qt中最核心的概念之一了。
一个小的Demo
信号与槽是Qt中的核心概念,有了信号与槽,我们就可以通过在UI上点击、输入等操作触发程序内部的函数发挥作用,比如在控制台上打印一段文本,比如将用户在QTextEdit
中输入的文本读入程序做后续处理。因此想要真正做到通过UI让用户和内部程序进行交互,信号与槽是必不可少的(在Qt的框架下)。
信号(signal)是由事件(event)发生,进而引起控件发出,只要信号发出,那么与信号绑定(connect)的槽函数(slot)就会被执行。比如最简单的信号就是clicked
,如果用户在UI上点击了按钮之类的控件,那么这个点击事件就会使得这些被点击的控件发出clicked
信号,这些信号会被与控件绑定的若干个槽函数捕获,然后这些槽函数就会被执行。大致的逻辑如此。
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class SignalSlotDemo(QWidget):
def __init__(self):
super(SignalSlotDemo, self).__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("")
self.resize(400, 300)
self.btn = QPushButton("我的按钮", self)
self.btn.clicked.connect(self.onClick)
def onClick(self):
print("信号已发出")
if __name__ == '__main__':
app = QApplication(sys.argv)
main = SignalSlotDemo()
main.show()
sys.exit(app.exec_())
运行效果:
需要说明的是,一个控件发出的信号可以与多个槽函数绑定,多个控件发出的信号也可以与同一个槽函数绑定。
自定义信号
之前用到过了自定义信号(内嵌信号),PyQt5提供了信号对象pyqtSignal
来允许用户创建自定义信号,从而使得系统处理事件更加灵活。
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
# 自定义信号
class MyTypeSignal(QObject):
# 定义一个信号
sendmsg = pyqtSignal(object)
# 发出信号的函数
def run(self):
self.sendmsg.emit("Hello PyQt5")
# 自定义的槽
class MySlot(QObject):
def get(self, msg):
print("信息" + msg)
if __name__ == '__main__':
send = MyTypeSignal()
slot = MySlot()
# 直接把信号和槽绑定
send.sendmsg.connect(slot.get)
# 发出信号
send.run()
运行效果:
信息Hello PyQt5
需要注意自定义的信号和槽都是继承自QObject
对象。将信号和槽绑定后,只要信号发出(即我们使用了send.run()
来发送信号),那么我们绑定的槽的函数就会被触发。
我们还可以通过disconnect()
来让原本已经绑定的信号和槽解除绑定:
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class MyTypeSignal(QObject):
# 定义一个信号
sendmsg = pyqtSignal(object)
# 发出信号的函数,这个信号只传递一个参数,即字符串"Hello PyQt5"
def run(self):
self.sendmsg.emit("Hello PyQt5")
class MySlot(QObject):
def get(self, msg):
print("信息" + msg)
if __name__ == '__main__':
send = MyTypeSignal()
slot = MySlot()
# 直接把信号和槽绑定
send.sendmsg.connect(slot.get)
send.sendmsg.disconnect(slot.get)
# 发送信号
send.run()
运行结果:
不打印任何信息,也就是说槽函数没有被触发
可以传递多个参数的信号
在使用pyqtSignal()
时,可以通过设置参数来制定该信号所能传送的参数的个数
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class MyTypeSignal(QObject):
# 定义一个信号
sendmsg = pyqtSignal(object)
# 发送3个参数的信号,参数类型分别为str,int,int
sendmsg1 = pyqtSignal(str, int, int)
# 发出信号的函数
def run(self):
self.sendmsg.emit("Hello PyQt5")
# 发送传送三个参数的信号
def run1(self):
self.sendmsg1.emit("Hello", 3, 4)
class MySlot(QObject):
def get(self, msg):
print("信息" + msg)
def get1(self, msg, a, b):
print(msg)
print(a + b)
if __name__ == '__main__':
send = MyTypeSignal()
slot = MySlot()
# 直接把信号和槽绑定
send.sendmsg.connect(slot.get)
send.sendmsg1.connect(slot.get1)
# 发送信号
send.run()
send.run1()
运行结果:
信息Hello PyQt5
Hello
7
为类添加多个信号
可以往类中添加任意数量的信号,而且信号传递的参数的类型可以是任意类型
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class MultiSignal(QObject):
signal1 = pyqtSignal()
signal2 = pyqtSignal(int)
signal3 = pyqtSignal(int, str)
signal4 = pyqtSignal(list)
signal5 = pyqtSignal(dict)
# 申明一个重载版本的信号
# 也就是说槽函数接受的参数可以是int和str类型的,也可以是只有一个str类型的参数
# 具体是哪一个视传入的参数情况而定
signal6 = pyqtSignal([int, str], [str])
def __init__(self):
super(MultiSignal, self).__init__()
self.signal1.connect(self.signalCall1)
self.signal2.connect(self.signalCall2)
self.signal3.connect(self.signalCall3)
self.signal4.connect(self.signalCall4)
self.signal5.connect(self.signalCall5)
self.signal6[int, str].connect(self.signalCall6)
self.signal6[str].connect(sel