事件(Events)是 GUI 程序中很重要的一部分,它由用户操作或系统产生。当我们调用程序的 exec_()方法时,程序就会进入主循环中。主循环捕获事件并将它们发送给相应的对象进行处理。
为此,Qt引入了一种独一无二的处理模式:信号与槽机制。信号和槽可以说是 Qt 的精髓所在。
信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性,也是 QT 区别于其它工具包的重要地方。它为高层次的事件处理自动生成所需要的附加代码。在我们所熟知的很多 GUI 工具包中,窗口小部件 (widget) 都有一个回调函数用于响应它们能触发的每个动作,这个回调函数通常是一个指向某个函数的指针。但是,在 QT 中信号和槽取代了这些凌乱的函数指针,使得我们编写这些通信程序更为简洁明了。
所有从 QObject 或其子类 ( 例如 Qwidget) 派生的类都能够包含信号和槽。当对象改变其状态时,信号就由该对象发射 (emit) 出去,这就是对象所要做的全部事情,它不知道另一端是谁在接收这个信号。这就是真正的信息封装,它确保对象被当作一个真正的软件组件来使用。槽用于接收信号,但它们是普通的对象成员函数。一个槽并不知道是否有任何信号与自己相连接。而且,对象并不了解具体的通信机制。你可以将很多信号与单个的槽进行连接,也可以将单个的信号与很多的槽进行连接,甚至于将一个信号与另外一个信号相连接也是可能的,这时无论第一个信号什么时候发射系统都将立刻发射第二个信号。
简而言之:
信号(singal)与槽(slot)用于对象相互通信。
信号:当某个对象的某个事件发生时,触发一个信号
槽:响应指定信号的所做的反应
信号与槽构造了一个强大的部件编程机制,这种机制很多程度提高了类的封装性和完整性。
PyQt的窗体控件类已经有很多的内置信号,开发者也可以添加自己的自定义信号,信号槽有如下特点:
- 一个信号可以连接到许多插槽。
- 一个信号也可以连接到另一个信号。
- 信号参数可以是任何Python类型。
- 一个插槽可以连接到许多信号。
- 连接可能会直接(即同步)或排队(即异步)。
- 连接可能会跨线程。
- 信号可能会断开
我们可以使用Qt Designer编辑界面的同时,编辑其信号槽处理。
简单示例,给一个QMainWindow窗体添加了一个PushButton按钮,当用户点击按钮时,关闭窗口。
下图中,我们先点击上方工具栏中“编辑信号/槽”按钮,将编辑器切换进入信号/槽编辑状态。
然后,将提前添加好的BtnClose按钮做拖动到主窗体的操作,这时候会弹出一个“配置连接”窗口。
左边列表中是按钮对应事件的信号,右边是窗体MainWindow可以被绑定的槽。
我们选择BtnClose的Clicked信号绑定到窗体的Close槽。
得到如下代码
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'TestFrm.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(701, 441)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.BtnClose = QtWidgets.QPushButton(self.centralwidget)
self.BtnClose.setGeometry(QtCore.QRect(450, 350, 75, 23))
self.BtnClose.setObjectName("BtnClose")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 701, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.BtnClose.clicked.connect(MainWindow.close)#将BtnClose的clicked信号和MainWindow的close槽连接
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.BtnClose.setText(_translate("MainWindow", "Close"))
if __name__=="__main__":
import sys
app=QtWidgets.QApplication(sys.argv)
formObj=QtWidgets.QMainWindow() #注意,这里和我们一开始创建窗体时使用的界面类型相同
ui=Ui_MainWindow()
ui.setupUi(formObj)
formObj.show()
sys.exit(app.exec_())
运行代码,我们发现已经可以点击Close按钮将窗口关闭了
我们也可以自行定义响应函数,下面我们将原来BtnClose按钮绑定的槽换成我们自己编写的成员函数
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'TestFrm.ui'
#
# Created by: PyQt5 UI code generator 5.5.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(701, 441)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.BtnClose = QtWidgets.QPushButton(self.centralwidget)
self.BtnClose.setGeometry(QtCore.QRect(450, 350, 75, 23))
self.BtnClose.setObjectName("BtnClose")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 701, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
#self.BtnClose.clicked.connect(MainWindow.close)#将BtnClose的clicked信号和MainWindow的close槽连接
self.BtnClose.clicked.connect(self.clickTime)#换成绑定自己编写的函数 <-----------
self.clickCnt = 0#记录点击次数 <-----------
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.BtnClose.setText(_translate("MainWindow", "Close"))
#当点击按钮时触发,修改按钮名 <-----------
def clickTime(self):
self.clickCnt+=1#点击次数递增
_translate = QtCore.QCoreApplication.translate
self.BtnClose.setText(_translate("MainWindow", "点击次数 %d"%self.clickCnt))#将按钮名改成点击次数
if __name__=="__main__":
import sys
app=QtWidgets.QApplication(sys.argv)
formObj=QtWidgets.QMainWindow() #注意,这里和我们一开始创建窗体时使用的界面类型相同
ui=Ui_MainWindow()
ui.setupUi(formObj)
formObj.show()
sys.exit(app.exec_())
运行代码,实现效果如下,每次点击按钮,按钮的文字内容就会修改成点击的次数