信号槽是Qt的核心机制,也是PyQt编程中对象进行通讯的机制。在Qt中,QObject对象和PyQt中全部继承自QWidget的控件都支持信号槽机制。当信号发射时,链接的槽函数会自动执行。在PyQt5中,信号与槽函数经过object.signal.connect()方法进行链接。
信号槽特色以下:
(1)一个信号能够链接多个槽
(2)一个信号能够链接另外一个信号
(3)信号参数能够是任意Python类型
(4)一个槽能够监听多个信号
(5)信号与槽的链接方式能够是同步链接,也能够是异步链接。
(6)信号与槽的链接能够跨线程
(7)信号能够断开
在编写一个类时,要首先定义的类的信号与槽,在类中信号与槽进行链接,实现对象之间的数据传输。当事件或状态发生变化时,会发出信号,进而触发执行事件或信号关联的槽函数。信号槽机制示意以下:编程
PyQt的内置信号是自动定义的,使用PyQt5.QtCore.pyqtSignal函数能够为QObject对象建立一个信号,使用pyqtSignal函数能够把信号定义为类的属性。app
class pyqtSignal: def __init__(self, *types, name: str = ...) -> None: ...
types参数表示定义信号时参数的类型,name参数表示信号的名称,默认使用类的属性名称。
使用pyqtSignal函数建立一个或多个重载的未绑定的信号做为类的属性,信号只能在QObject的子类中定义。信号必须在类建立时定义,不能在类建立后做为类的属性动态添加进来。使用pyqtSignal函数定义信号时,信号能够传递多各参数,并指定信号传递参数的类型,参数类型是标准的Python数据类型,包括字符串、日期、布尔类型、数字、列表、字典、元组。异步
from PyQt5.QtCore import pyqtSignal, QObject class StandardItem(QObject): # 定义信号,信号有两个参数,两个参数的类型分别为str,str,信号名称为dataChanged data_changed = pyqtSignal(str, str, name="dataChanged") # 更新信息,发送信号 def update(self): self.dataChanged.emit("old status", "new status")
使用connect函数能够将信号绑定到槽函数上,使用disconnect函数能够解除信号与槽函数的绑定,使用emit函数能够发射信号。QObject.signal.connect(self, slot, type=None, no_receiver_check=False)
创建信号到槽函数的链接,type为链接类型。QObject.signal.disconnect(self, slot=None)
断开信号到槽的链接emit(self, *args)
发送信号,args为可变参数。ide
import sys from PyQt5.QtCore import pyqtSignal, QObject, QCoreApplication class StandardItem(QObject): # 定义信号,信号有两个参数,两个参数的类型分别为str,str,信号名称为dataChanged data_changed = pyqtSignal(str, str, name="dataChanged") # 更新信息,发送信号 def update(self): self.dataChanged.emit("old status", "new status") # 定义槽函数 def onDataChanged(self, old, new): print(old) print(new) if __name__ == "__main__": app = QCoreApplication(sys.argv) item = StandardItem() item.dataChanged.connect(item.onDataChanged) item.update() sys.exit(app.exec_()) # OUTPUT: # old status # new status
内置信号是QObject对象自动定义的信号,内置槽函数是QObject对象自动定义的槽函数,能够经过QObject.signal.connect函数将QObject对象的内置信号链接到QObject对象的槽函数。函数
import sys from PyQt5.QtWidgets import QWidget, QApplication, QPushButton class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) button = QPushButton("close", self) # 链接内置信号与自定义槽 button.clicked.connect(self.onClose) # 自定义槽函数 def onClose(self): self.close() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
点击按钮时触发按钮内置的clicked信号,执行绑定的自定义槽函数onClose。性能
import sys from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QWidget, QApplication, QPushButton class MainWindow(QWidget): closeSignal = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) button = QPushButton("close", self) # 链接内置信号与自定义槽 button.clicked.connect(self.onClose) # 链接自定义信号closeSignal与内置槽函数close self.closeSignal.connect(self.close) # 自定义槽函数 def onClose(self): # 发送自定义信号 self.closeSignal.emit() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
经过链接内置信号clicked到自定义槽函数onClose,在自定义槽函数onClose内发送自定义信号closeSignal,并将自定义信号closeSignal与内置槽函数close链接。ui
import sys from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QWidget, QApplication, QPushButton class MainWindow(QWidget): closeSignal = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) button = QPushButton("close", self) # 链接内置信号与自定义槽 button.clicked.connect(self.onClicked) # 链接自定义信号closeSignal与内置槽函数close self.closeSignal.connect(self.onClose) # 自定义槽函数 def onClicked(self): # 发送自定义信号 self.closeSignal.emit() # 自定义槽函数 def onClose(self): self.close() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
一般经过类变量定义信号对象,在__init__
函数前定义自定义信号。线程
class TestObject(QObject): # 定义无参的信号 noParametersSignal = pyqtSignal() # 定义一个参数的信号,参数类型为int oneParameterSignal = pyqtSignal(int) # 定义一个参数的重载版本的信号,参数类型能够为int或str oneParameterOverloadSignal = pyqtSignal([int], [str]) # 定义两个参数的重载版本的信号,参数类型为int,str或int,int twoParametersOverloadSignal = pyqtSignal([int, str], [int, int]) # 定义一个list参数类型的信号 oneParameterSignalList = pyqtSignal(list) # 定义一个dict参数类型的信号 oneParameterSignalDict = pyqtSignal(dict)
类的槽函数定义与类的普通方法定义相同。code
class TestObject(QObject): # 定义无参的信号 noParametersSignal = pyqtSignal() # 定义一个参数的信号,参数类型为int oneParameterSignal = pyqtSignal(int) # 定义一个参数的重载版本的信号,参数类型能够为int或str oneParameterOverloadSignal = pyqtSignal([int], [str]) # 定义两个参数的重载版本的信号,参数类型为int,str或int,int twoParametersOverloadSignal = pyqtSignal([int, str], [int, int]) # 定义无参的槽函数 def onNoParameterSlot(self): pass # 定义一个参数的槽函数,参数为整型nIndex def onOneParameterSlot(self, nIndex): pass # 定义两个参数的槽函数,参数为整型nIndex,字符串型sStatus def onTwoParametersSlot(self, nIndex, sStatus): pass
经过connect方法链接信号与槽函数,信号与槽函数能够属于同一个QObject对象,也能够是不一样的QObject对象。orm
test = TestObject() test.noParametersSignal.connect(test.onNoParameterSlot) test.oneParameterSignal.connect(test.onOneParameterSlot) test.twoParametersOverloadSignal.connect(test.onTwoParametersSlot)
信号的发送经过emit方法进行发送。
def update(self): self.noParametersSignal.emit() self.oneParameterSignal.emit(100) self.oneParameterOverloadSignal("Hello, PyQt5") self.twoParametersOverloadSignal(100, "Hello, PyQt5")
Qt中信号发出的参数个数必须大于等于槽函数的参数个数,PyQt使用自定义参数传递解决槽函数参数比信号参数多的问题。使用Lambda表达式或functools的partial函数能够传递自定义参数给槽函数,自定义参数类型能够是Python任意类型。
import sys from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QHBoxLayout from functools import partial class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) button1 = QPushButton("Button1", self) button2 = QPushButton("Button2", self) layout = QHBoxLayout() layout.addWidget(button1) layout.addWidget(button2) self.setLayout(layout) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) # lambda button1.clicked.connect(lambda: self.onButtonClicked(1)) button2.clicked.connect(lambda: self.onButtonClicked(2)) # partial button1.clicked.connect(partial(self.onButtonClicked, 1)) button2.clicked.connect(partial(self.onButtonClicked, 2)) # 自定义槽函数 def onButtonClicked(self, n): print("Button {0} is Clicked".format(n)) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
PyQt中能够经过Python装饰器定义信号与槽函数,使用方法以下:
@PyQt5.QtCore.pyqtSlot(bool) def on_发送者对象名称_发射信号名称(self, parameter): pass
发送者对象名称是使用setObjectName为QObject对象设置的对象名称,经过信号名称链接到槽函数的connectSlotsByName函数以下:QtCore.QMetaObject.connectSlotsByName(self, QObject)
connectSlotsByName用于将QObject子孙对象的某些信号根据名称链接到某些QObject对象的相应槽函数。
import sys from PyQt5 import QtCore from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QHBoxLayout class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) button1 = QPushButton("Button1", self) button1.setObjectName("Button1") button2 = QPushButton("Button2", self) button2.setObjectName("Button2") layout = QHBoxLayout() layout.addWidget(button1) layout.addWidget(button2) self.setLayout(layout) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) QtCore.QMetaObject.connectSlotsByName(self) @QtCore.pyqtSlot() def on_Button1_clicked(self): print("Button1 is clicked") @QtCore.pyqtSlot() def on_Button2_clicked(self): print("Button2 is clicked") if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
PyQt为事件处理提供了高级别的信号槽机制和低级别的事件处理机制,信号槽机制是事件处理机制的高级封装。使用控件时,不用考虑事件处理机制,只须要关心信号槽便可;对于自定义派生控件,必须考虑事件处理机制,根据控件的行为需求从新实现相应的事件处理函数。
PyQt提供了5种事件处理和过滤方法,分别为:
(1)从新实现事件处理函数
经常使用的事件处理函数如paintEvent、mouseMoveEvent、mousePressEvent、mouseReleaseEvent等。
(2)从新实现QObject.event事件分发函数
在增长新的事件时,须要从新实现QObject.event方法,并增长新事件的分发路由。
(3)安装事件过滤器
若是对QObject对象调用installEventFilter方法,则为QObject对象安装事件过滤器。QObject对象的全部事件都会先传递到事件过滤器eventFilter函数,在事件过滤器eventFilter函数中能够丢弃或修改某些事件,对感兴趣的事件使用自定义的事件处理机制,对其它事件使用默认事件处理机制。事件过滤机制会对QObject的全部事件进行过滤,所以若是要过滤的事件比较多则会影响程序性能。
(4)在QApplication安装事件过滤器
在QApplication对象安装事件过滤器将会对全部QObject对象的全部事件进行过滤,而且会首先得到事件,即将事件发送给其它任何一个事件过滤器前,都会首先发送给QApplication的事件过滤器。
(5)从新QApplication的notify方法
PyQt使用QApplication对象的notify方法进行分发事件,要想在任何事件过滤器前捕获事件惟一的方法就是从新实现QApplication的notify方法。
QDialog对话框在ESC按键按下时会自动退出,使用事件处理和过滤对按下ESC按键进行处理。
(1)从新实现事件处理函数
import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) # 从新实现keyPressEvent def keyPressEvent(self, event): if event.key() != Qt.Key_Escape: QDialog.keyPressEvent(self, event) if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() dialog.exec_() sys.exit(app.exec_())
(2)从新实现event函数
import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) # 从新实现keyPressEvent def event(self, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return True else: return QDialog.event(self, event) if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() dialog.exec_() sys.exit(app.exec_())
(3)QObject安装事件过滤器
import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.installEventFilter(self) def eventFilter(self, watched, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return True else: return QDialog.eventFilter(self, watched, event) if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() dialog.exec_() sys.exit(app.exec_())
(4)QApplication安装事件过滤器
import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) def eventFilter(self, watched, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return True else: return QDialog.eventFilter(self, watched, event) if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() app.installEventFilter(dialog) dialog.exec_() sys.exit(app.exec_())