目录
偏函数应用程序(partial function application)
什么是信号与槽?
在Qt和PyQt中有两种通信机制:
-
低级事件处理机制(low-level event-handling mechanism)
-
高级机制(high-level mechanism)
前者是大部分GUI通用的机制,后者是Qt独有机制,也就是信号和槽。这一章我们主要对信号和槽进行说明
在PyQt中每一个部件(QWidget)都支持信号和槽机制。用户在与GUI进行交互时几乎每一个操作都会触发相应的信号,这些信号默认会被忽略掉,只有连接了槽的信号才会别处理。
在PyQt中槽可以是部件预设的一些方法,也可以是任何可调用对象和方法。
信号与槽例子
import sys
from PySide2 import QtCore
from PySide2 import QtWidgets
class Form(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
dial = QtWidgets.QDial()
dial.setNotchesVisible(True)
spinbox = QtWidgets.QSpinBox()
layout = QtWidgets.QHBoxLayout()
layout.addWidget(dial)
layout.addWidget(spinbox)
self.setLayout(layout)
self.connect(dial, QtCore.SIGNAL("valueChanged(int)"),
spinbox.setValue)
self.connect(spinbox, QtCore.SIGNAL("valueChanged(int)"),
dial, QtCore.SLOT("setValue(int)"))
self.setWindowTitle("Signals and Slots")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
运行结果:
这里创建了两个部件QDial和QSpinBox,然后使用connect将这两个部件的信号和槽分别进行关联,这样当我们改变其中任何一个部件的值时,另一个部件也会随之改变。虽然两个部件信号和槽相互关联,但是并不存在死循环的问题,因为像这种之变化类型的槽在执行是会先检测值是否发生了变化,如果有就执行,没有就不会做任何事。
信号和槽的关联方法
上面的例子中valueChanged(int)方法作为信号,setValue(int)就是槽,信号和槽的关联方法主要有三种:
- self.connect(QWidget, SIGNAL("signalSignature"), functionName())
- self.connect(QWidget, SIGNAL("signalSignature"), instance.methodName)
- self.connect(QWidget, SIGNAL("signalSignature"), instance, SLOT("slotSignature"))
上面的例子中分别使用了2和3的方法,实际应用中2是比较常用的方法。
信号的类型
Qt型信号
Qt预设的信号
Python型信号
在PyQt中自定义的信号,PyQt中使用QObject.emit()方法自定义。
短路信号
没有参数的信号
非短路信号
含有参数的
一个信号关联多个槽的例子
import sys
from PySide2 import QtCore
from PySide2 import QtWidgets
if sys.version_info[:2] < (2, 5):
def partial(func, arg):
def callme():
return func(arg)
return callme
else:
from functools import partial
class Form(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
button1 = QtWidgets.QPushButton("One")
button2 = QtWidgets.QPushButton("Two")
button3 = QtWidgets.QPushButton("Three")
button4 = QtWidgets.QPushButton("Four")
button5 = QtWidgets.QPushButton("Five")
self.label = QtWidgets.QLabel("Click a button...")
layout = QtWidgets.QHBoxLayout()
layout.addWidget(button1)
layout.addWidget(button2)
layout.addWidget(button3)
layout.addWidget(button4)
layout.addWidget(button5)
layout.addStretch()
layout.addWidget(self.label)
self.setLayout(layout)
self.connect(button1, QtCore.SIGNAL("clicked()"), self.one)
self.button2callback = partial(self.anyButton, "Two")
self.connect(button2, QtCore.SIGNAL("clicked()"),
self.button2callback)
self.button3callback = lambda who="Three": self.anyButton(who)
self.connect(button3, QtCore.SIGNAL("clicked()"),
self.button3callback)
self.connect(button4, QtCore.SIGNAL("clicked()"), self.clicked)
self.connect(button5, QtCore.SIGNAL("clicked()"), self.clicked)
self.setWindowTitle("Connections")
def one(self):
self.label.setText("You clicked button 'One'")
def anyButton(self, who):
self.label.setText("You clicked button '%s'" % who)
def clicked(self):
button = self.sender()
if button is None or not isinstance(button, QtWidgets.QPushButton):
return
self.label.setText("You clicked button '%s'" % button.text())
app = QtWidgets.QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
运行结果:
在这个例子中创建5个按钮,当点击其中一个按钮时,最右侧的文本就会发生改变,提示用户当前所执行的的操作。
连接公用槽的方法
上面的例子中每一个按钮对应不同的操作,会产生不同的信号,但能否将他们关联到同一个槽中呢?
要实现这种操作一般有两种方法:
-
偏函数应用程序(partial function application)
-
通过PyQt来了解那个信号调用了该槽
第一种方法可以是把信号和按钮的名字使用partial()方法封装到一起。也可以使用lambda函数,按钮名字作为参数这样的方法实现。
在一个槽的内部,总是可以通过sender()方法获取信号来源的QObject对象。所以第二种方法利用这一点在槽中获取被点击按钮的名字。
打断和阻止操作
QObject.disconnect()
可以取消连接操作
QObject.blockSignals()
用来阻止信号发射