1.是什么
其实这个机制非常好理解,我们拿红绿灯来做个类比。
当红灯信号发射后,行人就会停下;当绿灯信号发射后,行人就会前进。我们用red和green来表示信号,用stop()和go()函数来表示行人的动作,这两个函数也被称为槽函数。
也就是说,当red信号发射后,stop()槽函数就会被调用;当green信号发射后,go()槽函数会被调用。不过信号和槽只有在连接之后才可以起作用,连接方式如图1-21所示
在下图中
(1)widget就是PyQt中的控件对象。其实就是组件
(2)signal就是控件对象拥有的信号
(3)connect()方法用于连接信号和槽,
(4)slot是槽函数名称。
red信号和stop()槽函数进行连接
green信号和go()槽函数进行连接
扫描二维码关注公众号,回复: 16531455 查看本文章只有这样连接后,发射的信号才可以调用相应的槽函数。
总结起来就一句话:连接后,信号发射,槽函数“启动”
traffic_light.red.connect(stop)
traffic_light.green.connect(go)
在connect()方法中传入的是函数名
2.一个信号连接一个槽
1.案例一:start按钮与stop按钮之间转换
实现功能:我们可以在很多窗口上看到“Start”按钮,单击之后文本从“Start”变成了“Stop”
import sys
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
btn = QPushButton('Start', self)
btn.clicked.connect(self.change_text)
def change_text(self):
btn = self.sender()
if btn.text() == 'Start':
btn.setText('Stop')
else:
btn.setText('Start')
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
(#1) 实例化一个QPushButton按钮控件之后,我们将按钮的clicked信号与自定义的change_text()槽函数连接起来。
(#2) 在槽函数中,我们首先通过text()方法获取到当前单击按钮的文本,如果是“Start”,就调用setText()方法将按钮的文本修改为“Stop”。而如果文本是“Stop”,就将其修改为“Start”。
因为要在槽函数中使用btn对象,所以应该在类的初始化函数__init__()中将btn设置为成员变量,也就是self.btn。当然我们也可以直接通过sender()方法获取到当前发射信号的控件对象
每个控件都有相应的内置信号,比如QPushButton控件有clicked、pressed、released等内置信号。当然我们也可以给控件或窗口自定义一个信号
2.案例二:信号传值
信号是可以传值的,比如QLineEdit控件有一个textChanged信号,它会在输入框中的文本发生改变时被发射,并且会携带当前的文本。
import sys
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.resize(180, 30)
line = QLineEdit(self)
line.textChanged.connect(self.show_text)
def show_text(self, text): # 1
print(text)
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
(#1) show_text()槽函数有一个text参数,textChanged信号携带的值会传给这个参数。运行程序,每当修改输入框中的文本时,控制台都会将修改后的文本输出
3.案例三
如果信号(比如clicked信号)无法传值,而我们想要让它连接一个带参数的槽函数,这时候要怎么做呢?答案是使用lambda匿名函数。
import sys
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
btn = QPushButton('Start', self)
btn.clicked.connect(lambda: btn.setText('Stop')) # 1
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
在信号和槽连接时,我们必须往connect()方法中传入一个可调用对象,也就是传入函数名,不带括号。
如果带了括号,就表示我们传入了函数的执行结果。
setText(‘Stop’)方法执行后返回None,信号跟None连接明显不合理。
如果要将setText()用作和clicked信号连接的槽函数,就必须使用lambda匿名函数把setText()方法“包装”一下,以返回一个可调用对象。
3.一个信号连接多个槽
一个信号可以连接多个槽函数,也就是信号只用发射一次,就可以调用多个槽函数。
import sys
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.btn = QPushButton('Start', self)
self.btn.clicked.connect(self.change_text) #注释1开始
self.btn.clicked.connect(self.change_size) #注释1结束
def change_text(self):
if self.btn.text() == 'Start':
self.btn.setText('Stop')
else:
self.btn.setText('Start')
def change_size(self): # 2
self.btn.resize(150, 30)
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
(#1) clicked信号连接了change_text()槽函数和change_size()槽函数。
(#2) change_size()槽函数用于改变按钮的尺寸
4.多个信号连接一个槽
QPushButton除了有clicked信号,还有pressed信号和released信号。
pressed信号是在按钮被“按下”那一刻发射,而released信号则是在按钮被“松开”后发射。
“按下”和“松开”其实就构成了一次单击,也就会发射clicked信号。
import sys
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.btn = QPushButton('Start', self)
self.btn.pressed.connect(self.change_text) #注释1开始
self.btn.released.connect(self.change_text) #注释1结束
def change_text(self):
if self.btn.text() == 'Start':
self.btn.setText('Stop')
else:
self.btn.setText('Start')
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
#1 将pressed信号和released信号都跟change_text()槽函数连接起来。
那么当按钮被“按下”(不“松开”)时,槽函数就会被调用,按钮文本从“Start”变成了“Stop”。
当“松开”按钮后,槽函数再次被调用,按钮文本从“Stop”变回了“Start”。
5.信号与信号连接
信号不仅可以跟槽函数连接,还可以跟信号连接
import sys
from PyQt5.QtWidgets import *
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.btn = QPushButton('Start', self)
self.btn.pressed.connect(self.btn.released) #注释1开始
self.btn.released.connect(self.change_text) #注释1结束
def change_text(self):
if self.btn.text() == 'Start':
self.btn.setText('Stop')
else:
self.btn.setText('Start')
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
(#1) 我们将pressed信号同released信号进行连接,而released信号则跟槽函数进行连接。
当按钮被“按下”(不“松开”)后,pressed信号发射,released信号也会马上跟着发射,槽函数就会被执行,改变按钮的文本。
当按钮被“松开”后,released信号再次发射,槽函数再次被调用。
6.自定义信号
在PyQt中,各个控件内置的信号已经能够让我们实现许多功能需求,但是如果想要更加个性化的功能,我们还得借助自定义信号来实现。
本小节会详细介绍如何自定义信号,并通过自定义信号进行传值。
1.创建自定义信号-案例一
自定义信号是通过pyqtSignal来创建的,接下来通过示例代码来演示自定义信号的创建过程。
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Window(QWidget):
my_signal = pyqtSignal() # 1
def __init__(self):
super(Window, self).__init__()
self.my_signal.connect(self.my_slot) # 2
def my_slot(self):
print(self.width())
print(self.height())
def mousePressEvent(self, event): # 3
self.my_signal.emit()
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
(#1) 实例化一个pyqtSignal对象。
(#2) 将自定义信号与my_slot()槽函数连接。
(#3) mousePressEvent()是鼠标按下事件函数,每当鼠标被按下时,该事件函数就会被执行。my_siganl信号调用emit()方法将自己发射出去,这样my_slot()槽函数就会被执行,输出窗口的宽和高。
2.让自定义信号携带值
如果想要获取鼠标指针在窗口上的x坐标和y坐标,可以通过信号将坐标值发送过来
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Window(QWidget):
my_signal = pyqtSignal(int, int) # 1
def __init__(self):
super(Window, self).__init__()
self.my_signal.connect(self.my_slot)
def my_slot(self, x, y): # 2
print(x)
print(y)
def mousePressEvent(self, event): # 3
x = event.pos().x()
y = event.pos().y()
self.my_signal.emit(x, y)
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())
(#1) 要通过自定义信号传值,我们必须在实例化pyqtSignal对象时明确要传递的值的类型。由于x坐标和y坐标都是整型值,因此要给pyqtSignal传入两个int。
(#2 )槽函数也要稍做修改,需要增加两个参数,分别用于接收x坐标和y坐标。
(#3 )现在我们需要在鼠标按下事件中获取鼠标指针的x和y坐标,并通过emit()方法将其随信号一同发射出去。
除了整型值,我们还可以让自定义信号携带其他类型(包括Python语言所支持的值类型和PyQt自定义的数据类型)的值,如下表