PyQt中提供了两种针对事件处理的机制:一种是事件,另一种则是信号和槽。
一、事件
事件处理在PyQt中是比较底层的,常用的事件有键盘事件、鼠标事件、拖放事件、滚轮事件、定时事件、焦点事件、进入和离开事件(光标移入控件或者移出),移动事件(窗口位置变化),显示和隐藏事件,窗口事件(窗口是否为当前窗口)、以及常见的Qt事件:Socket事件、剪贴板事件、文字改变事件,布局改变事件等。
针对这些事件,PyQt提供了多种事件处理和过滤方法,其中最常用的有两种:
(1)重写事件具体的函数(例如:mousePressEvent()/keyPressEvent()....)
(2)重新实现QObject.event()一般用在PyQt没有提供该事件的处理函数的情况下,即添加一个新的事件;
1.1 重写事件
1 import sys,os 2 from PyQt5.QtCore import Qt 3 from PyQt5.QtWidgets import QWidget, QApplication,QMessageBox 4 from PyQt5.QtGui import QIcon 5 6 path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 7 8 class MyWindow(QWidget): 9 10 def __init__(self): 11 super(MyWindow, self).__init__() 12 self.initUI() 13 14 def initUI(self): 15 self.setGeometry(300, 300, 300, 250) 16 self.setWindowTitle('重写事件示例') 17 self.setWindowIcon(QIcon(r'%s\4.图标素材\chuan.ico' % path)) 18 19 def closeEvent(self, QCloseEvent): 20 ''' 21 重写closeEvent方法,关闭窗口时触发 22 ''' 23 reply = QMessageBox.question(self,'本程序',"是否要退出程序?", 24 QMessageBox.Yes | QMessageBox.No, 25 QMessageBox.No) 26 if reply == QMessageBox.Yes: 27 QCloseEvent.accept() 28 else: 29 QCloseEvent.ignore() 30 31 32 def keyPressEvent(self, QKeyEvent): 33 ''' 34 重写keyPressEvent事件,按下ESC就会退出程序 35 ''' 36 if QKeyEvent.key() == Qt.Key_Escape: 37 self.close() 38 39 40 if __name__ == '__main__': 41 app = QApplication(sys.argv) 42 win = MyWindow() 43 win.show() 44 sys.exit(app.exec_())
这段代码里重写了keyPressEvent和closeEvent两个事件。实现了两个功能:(1)按下ESC是可退出关闭窗口;(2)关闭当前窗口时,弹出对话框。
效果图如下:
在pycharm中编辑时,我们可以发现,QWidget类下虽然有该keyPressEvent和closeEvent方法,但并没有编写有意义的内容。
1.2 创建新事件
可参考前面小节的center(),不再详述。
除上述内容外,再对三个概念加以学习,即事件对象、事件发送。
事件对象时python用来描述一系列自身属性的对象。
1 import sys,os 2 from PyQt5.QtWidgets import QApplication,QWidget,QGridLayout,QLabel 3 from PyQt5.QtGui import QIcon 4 from PyQt5.QtCore import Qt 5 6 path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 7 8 class MyWindow(QWidget): 9 10 def __init__(self): 11 super().__init__() 12 self.initUI() 13 14 def initUI(self): 15 self.setGeometry(600,300,350,250) 16 self.setWindowTitle('信号和槽') 17 self.setWindowIcon(QIcon(r'%s\4.图标素材\chuan.ico' % path)) 18 19 grid = QGridLayout() 20 grid.setSpacing(10) 21 22 x=0 23 y=0 24 self.text = "x:{},y:{}".format(x,y) 25 self.label = QLabel(self.text,self) 26 grid.addWidget(self.label,0,0,Qt.AlignTop) 27 28 self.setMouseTracking(True) #开启鼠标追踪 29 self.setLayout(grid) 30 31 def mouseMoveEvent(self, QMouseEvent): 32 x = QMouseEvent.x() 33 y = QMouseEvent.y() 34 text = "x:{},y:{}".format(x, y) 35 self.label.setText(text) 36 37 38 if __name__ == '__main__': 39 app = QApplication(sys.argv) 40 win = MyWindow() 41 win.show() 42 sys.exit(app.exec_())
在这个示例中,我们再label里显示鼠标的坐标。mouseMoveEvent方法下的QMouseEvent即鼠标移动的事件对象,也就是需要捕捉的信号来源。
事件发送是在程序设计与调试时,我们需要知道是哪一个控件发送了信号,利用PyQt5中的sender()方法可以实现。
1 import sys,os 2 from PyQt5.QtWidgets import QMainWindow,QApplication,QPushButton 3 from PyQt5.QtGui import QIcon 4 5 path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 6 7 class MyWindow(QMainWindow): 8 9 def __init__(self): 10 super().__init__() 11 self.initUI() 12 13 def initUI(self): 14 self.setGeometry(600,300,300,260) 15 self.setWindowTitle('事件发送') 16 self.setWindowIcon(QIcon(r'%s\4.图标素材\chuan.ico' % path)) 17 18 btn1 = QPushButton('按钮一',self) 19 btn2 = QPushButton('按钮二',self) 20 21 btn1.move(30,50) 22 btn2.move(150,50) 23 24 btn1.clicked.connect(self.buttonClicked) 25 btn2.clicked.connect(self.buttonClicked) 26 27 self.statusBar() 28 29 def buttonClicked(self): 30 sender = self.sender() 31 self.statusBar().showMessage(sender.text() + '被按下') 32 33 34 if __name__ == '__main__': 35 app = QApplication(sys.argv) 36 win = MyWindow() 37 win.show() 38 sys.exit(app.exec_())
sender()方法可以返回发送信号的控件名称。这在我们调试时可以起到极大的辅助作用。方便快速了解程序背后的设计思路。
二、信号和槽
信号(Signal)和槽(Slot)是Qt中的核心机制,也是在PyQt编程中对象之间进行通信的机制。在Qt中,每个QObject对象和PyQt中所有继承自QWidget的控件(这些都是QObject的子对象)都支持信号与槽机制。当信号发射时,连接的槽函数将会自动执行。在PyQt5中信号与槽通过object.signal.connect()方法连接。
使用时,既可以自定义信号,也可以使用pyqt内置信号。信号与槽具有以下几个特点:
- 一个信号可以连接多个槽;
- 一个信号也可以连接另一个信号;
- 一个槽可以监听多个信号;
- 信号与槽的连接可能同步或者异步,还可能跨线程连接;
- 信号也可能断开。
除此之外,还有几个常用的操作方法需要说明:
- connect()方法可以将信号与槽函数连接。注意这里的槽函数不能加括号(后面会讲到);
- disconnect可以将接触信号与槽的连接;
- emit()可以发射信号
- 如果需要自定义信号,可以使用PyQt5.QtCore下的pyqtSignal方法。
1 import sys,os 2 from PyQt5.QtCore import pyqtSignal,QObject 3 from PyQt5.QtWidgets import QMainWindow,QApplication 4 from PyQt5.QtGui import QIcon 5 6 path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 7 8 class Communicate(QObject): 9 closeApp = pyqtSignal() 10 11 class MyWindow(QMainWindow): 12 13 def __init__(self): 14 super(MyWindow, self).__init__() 15 self.initUI() 16 17 def initUI(self): 18 self.setGeometry(600,300,500,400) 19 self.setWindowIcon(QIcon(r'%s\4.图标素材\chuan.ico' % path)) 20 self.setWindowTitle('信号发送') 21 22 self.c = Communicate() 23 self.c.closeApp.connect(self.close) 24 25 def mousePressEvent(self, *args, **kwargs): 26 self.c.closeApp.emit() 27 28 29 if __name__ == '__main__': 30 app = QApplication(sys.argv) 31 win = MyWindow() 32 win.show() 33 sys.exit(app.exec_())
在这个示例中,我们创建了一个closeApp的信号,该信号会在鼠标按下时触发。且该事件与QMainwindow绑定。
class Communicate(QObject): closeApp = pyqtSignal()
这里,我们创建了一个Communicate类,并在该类下创建了信号。
self.c = Communicate() self.c.closeApp.connect(self.close)
首先将communicate()类示例化给self.c。然后将closeApp信号与窗口的close()方法连接。
def mousePressEvent(self, *args, **kwargs): self.c.closeApp.emit()
点击鼠标按键时,emit()方法会将closeApp信号发射。