python GUI库图形界面开发之PyQt5信号与槽事件处理机制详细介绍与实例解析
PyQt5中信号与槽可以说是对事件处理机制的高级封装,如果说事件是用来创建窗口控件的,那么信号与槽就是用来对这个控件进行使用的,比如一个按钮,当我们使用按钮时,只关心clicked信号,至于这个按钮如何接受并处里鼠标点击事件,然后在发射这个信号,则不关心,但是如果要重载一个按钮,这时候就要关心了,比如可以改变它的行为:在鼠标按下时触发clicked信号,而不是释放时
PyQt5常见事件类型pyqt是对Qt的封装,qt程序是事件驱动的,它的每个动作都有幕后某个事件所触发,Qt事件类型有很多,常见的如下
键盘事件:按键的按下与松开 鼠标事件:鼠标指针的移动,鼠标按键的按下与松开 拖放事件:用鼠标进行拖放 滚轮事件:鼠标滚轮滚动 绘屏事件:重绘制屏幕的某些部分 定时事件:定时器到时 焦点事件:键盘焦点移动 进入和离开事件:鼠标指针移入Widget内,或者移出 移动事件:Widget的位置改变 大小改变事件:widget的大小改变 显示和隐藏事件:widget显示与隐藏 窗口事件:窗口是否为当前窗口还有一些常见的qt事件,比如Socket事件,剪切板事件,字体改变事件,布局改变事件
使用事件处理的方法pyqt提供如下5中事件处理和过滤的方法(有弱到强),其中只有前两种方法使用最频繁
1 、重新实现事件函数比如mousePressEvent(),keyPressEvent(),paintEvent(),这是最常规的事件处理方法
2 、重新实现QObject.event()一般用在pyqt没有提供该事件的处理函数的情况下,即增加新事件时
3 、安装事件过滤器如果对QObject调用installEventFilter,则相当于为这个QObject安装了一个事件过滤器,对于QObject的全部事件来说,它们都会先传递到事件过滤函数eventFilter中,在这个函数中,我们可以抛弃或者修改这些事件,比如对自己感兴趣的事件使用自定义的处理机制,对其他事件采用默认的事件处理机制,由于这中方法会调用installEventFilter的所有QObject的事件进行过滤,因此如果要过滤的事件比较多,则会降低程序的性能
4 、在QApplication中安装事件过滤器这种方法比上一种更强大,QApplication的事件过滤器将捕获所有的QObject事件,而且第一个获得该事件,也就是说,在将事件发送给其他任何一个事件过滤器之前,都会发送给QApplication的事件过滤器
5 、重新实现QApplication的notify()方法pyqt使用notify来分发事件,要想在任何事件处理器之前捕获事件,唯一的方法就是重新实现QApplication的notify(),在实践中,在调试才会用这中方法
PyQt5信号与槽事件处理经典案例import sysfrom PyQt5.QtCore import (QEvent, QTimer, Qt)from PyQt5.QtWidgets import (QApplication, QMenu, QWidget)from PyQt5.QtGui import QPainterclass Widget(QWidget): def __init__(self, parent=None): super(Widget, self).__init__(parent) #初始化数据 #鼠标双击False self.justDoubleClicked = False #按键,输出文本,提示消息为空 self.key = '' self.text = '' self.message = '' #设置窗口初始大小与位置 self.resize(400, 300) self.move(100, 100) #设置标题 self.setWindowTitle('Events') #定时器1秒后执行槽函数 QTimer.singleShot(1000, self.giveHelp) # 避免窗口大小重绘事件的影响,可以把参数0改变成3000(3秒),然后在运行,就可以明白这行代码的意思。 def giveHelp(self): self.text = '请点击这里触发追踪鼠标功能' # 重绘事件,也就是触发paintEvent函数。 self.update() ’’’重新实现关闭事件’’’ def closeEvent(self, event): print('Closed') ’’’重新实现上下文菜单事件’’’ def contextMenuEvent(self, event): #实例化菜单,添加子菜单one two并附加快捷键功能,关联槽函数 menu = QMenu(self) oneAction = menu.addAction('&One') twoAction = menu.addAction('&Two') oneAction.triggered.connect(self.one) twoAction.triggered.connect(self.two) #如果message为空,执行 if not self.message: #在菜单中添加一条分割线 menu.addSeparator() #添加自菜单three,关联槽函数 threeAction = menu.addAction('Thre&e') threeAction.triggered.connect(self.three) #菜单栏出现在鼠标的位置 menu.exec_(event.globalPos()) ’’’上下文菜单槽函数’’’ def one(self): self.message = 'Menu option One' self.update() def two(self): self.message = 'Menu option Two' self.update() def three(self): self.message = 'Menu option Three' self.update() ’’’重新实现绘制事件’’’ def paintEvent(self, event): text = self.text i = text.find('nn') if i >= 0: text = text[0:i] # 若触发了键盘按钮,则在文本信息中记录这个按钮信息。 if self.key: text += 'nn你按下了: {0}'.format(self.key) painter = QPainter(self) painter.setRenderHint(QPainter.TextAntialiasing) # 绘制信息文本的内容 painter.drawText(self.rect(), Qt.AlignCenter, text) # 若消息文本存在则在底部居中绘制消息,5秒钟后清空消息文本并重绘。 if self.message: #显示给定坐标处的文本,坐标,对齐方式。文本内容 painter.drawText(self.rect(), Qt.AlignBottom | Qt.AlignHCenter, self.message) #5秒钟后触发清空信息的函数,并重新绘制事件 QTimer.singleShot(5000, self.clearMessage) QTimer.singleShot(5000, self.update) ’’’清空消息文本的槽函数’’’ def clearMessage(self): self.message = '' ’’’重新实现调整窗口大小事件’’’ def resizeEvent(self, event): self.text = '调整窗口大小为: QSize({0}, {1})'.format( event.size().width(), event.size().height()) self.update() ’’’重新实现鼠标释放事件’’’ def mouseReleaseEvent(self, event): # 若鼠标释放为双击释放,则不跟踪鼠标移动 if self.justDoubleClicked: self.justDoubleClicked = False # 若鼠标释放为单击释放,则需要改变跟踪功能的状态,如果开启跟踪功能的话就跟踪,不开启跟踪功能就不跟踪 else: # 单击鼠标 self.setMouseTracking(not self.hasMouseTracking()) if self.hasMouseTracking():self.text = '开启鼠标跟踪功能.n' + '请移动一下鼠标!n' + '单击鼠标可以关闭这个功能' else:self.text = '关闭鼠标跟踪功能.n' + '单击鼠标可以开启这个功能' self.update() ’’’重新实现鼠标移动事件’’’ def mouseMoveEvent(self, event): #如果没有鼠标双击,执行 if not self.justDoubleClicked: # 窗口坐标转换为屏幕坐标 globalPos = self.mapToGlobal(event.pos()) self.text = '''鼠标位置: 窗口坐标为:QPoint({0}, {1}) 屏幕坐标为:QPoint({2}, {3}) '''.format(event.pos().x(), event.pos().y(), globalPos.x(), globalPos.y()) self.update() ’’’重新实现鼠标双击事件’’’ def mouseDoubleClickEvent(self, event): self.justDoubleClicked = True self.text = '你双击了鼠标' self.update() ’’’重新实现键盘按下事件’’’ def keyPressEvent(self, event): self.key = '' if event.key() == Qt.Key_Home: self.key = 'Home' elif event.key() == Qt.Key_End: self.key = 'End' elif event.key() == Qt.Key_PageUp: if event.modifiers() & Qt.ControlModifier:self.key = 'Ctrl+PageUp' else:self.key = 'PageUp' elif event.key() == Qt.Key_PageDown: if event.modifiers() & Qt.ControlModifier:self.key = 'Ctrl+PageDown' else:self.key = 'PageDown' elif Qt.Key_A <= event.key() <= Qt.Key_Z: if event.modifiers() & Qt.ShiftModifier:self.key = 'Shift+' self.key += event.text() #如果key有字符,不为空,则绘制字符 if self.key: self.key = self.key self.update() #否则就继续监视这个事件 else: QWidget.keyPressEvent(self, event) ’’’重新实现其他事件,适用于PyQt没有提供该事件的处理函数的情况,Tab键由于涉及焦点切换,不会传递给keyPressEvent,因此,需要在这里重新定义。’’’ def event(self, event): #如果有按键按下,并且按键是tab键 if (event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab): self.key = '在event()中捕获Tab键' self.update() return True return QWidget.event(self, event)if __name__ == '__main__': app = QApplication(sys.argv) form = Widget() form.show() app.exec_()代码解析
首先是类的建立,建立text和message两个变量,使用painEvent函数把他们输出到窗口中
update函数的作用是更新窗口,由于窗口更新过程中会触发一次paineEvent函数(paintEvent是窗口基类QWidget的内部函数),因此在本例中,update函数的作用等同于paintEvent函数
import sysfrom PyQt5.QtCore import (QEvent, QTimer, Qt)from PyQt5.QtWidgets import (QApplication, QMenu, QWidget)from PyQt5.QtGui import QPainterclass Widget(QWidget): def __init__(self, parent=None): super(Widget, self).__init__(parent) #初始化数据 #鼠标双击False self.justDoubleClicked = False #按键,输出文本,提示消息为空 self.key = '' self.text = '' self.message = '' #设置窗口初始大小与位置 self.resize(400, 300) self.move(100, 100) #设置标题 self.setWindowTitle('Events') #定时器1秒后执行槽函数 QTimer.singleShot(1000, self.giveHelp) # 避免窗口大小重绘事件的影响,可以把参数0改变成3000(3秒),然后在运行,就可以明白这行代码的意思。 def giveHelp(self): self.text = '请点击这里触发追踪鼠标功能' # 重绘事件,也就是触发paintEvent函数。 self.update()
初始化运行结果如下
然后是重新实现窗口关闭事件与上下文菜单事件,主要影响message标量的结果,paintEvent负责把这个变量在窗口底部输出
’’’重新实现关闭事件’’’ def closeEvent(self, event): print('Closed') ’’’重新实现上下文菜单事件’’’ def contextMenuEvent(self, event): #实例化菜单,添加子菜单one two并附加快捷键功能,关联槽函数 menu = QMenu(self) oneAction = menu.addAction('&One') twoAction = menu.addAction('&Two') oneAction.triggered.connect(self.one) twoAction.triggered.connect(self.two) #如果message为空,执行 if not self.message: #在菜单中添加一条分割线 menu.addSeparator() #添加自菜单three,关联槽函数 threeAction = menu.addAction('Thre&e') threeAction.triggered.connect(self.three) #菜单栏出现在鼠标的位置 menu.exec_(event.globalPos()) ’’’上下文菜单槽函数’’’ def one(self): self.message = 'Menu option One' self.update() def two(self): self.message = 'Menu option Two' self.update() def three(self): self.message = 'Menu option Three' self.update()
绘制事件是代码的核心事件,它的作用是时刻跟随text和message这两个变量的信息,并把text内容绘制到窗口的中部,把message的内容绘制到窗口的底部
’’’重新实现绘制事件’’’ def paintEvent(self, event): text = self.text i = text.find('nn') if i >= 0: text = text[0:i] # 若触发了键盘按钮,则在文本信息中记录这个按钮信息。 if self.key: text += 'nn你按下了: {0}'.format(self.key) painter = QPainter(self) painter.setRenderHint(QPainter.TextAntialiasing) # 绘制信息文本的内容 painter.drawText(self.rect(), Qt.AlignCenter, text) # 若消息文本存在则在底部居中绘制消息,5秒钟后清空消息文本并重绘。 if self.message: #显示给定坐标处的文本,坐标,对齐方式。文本内容 painter.drawText(self.rect(), Qt.AlignBottom | Qt.AlignHCenter, self.message) #5秒钟后触发清空信息的函数,并重新绘制事件 QTimer.singleShot(5000, self.clearMessage) QTimer.singleShot(5000, self.update) ’’’清空消息文本的槽函数’’’ def clearMessage(self): self.message = ''
接下来是调整窗口大小事件
’’’重新实现调整窗口大小事件’’’ def resizeEvent(self, event): self.text = '调整窗口大小为: QSize({0}, {1})'.format( event.size().width(), event.size().height()) self.update()
实现鼠标释放事件,若为双击释放,则不跟随鼠标移动,若为单击释放,则需要跟随鼠标移动状态进行更改,如果开启跟踪功能就跟踪,否则就不跟综
’’’重新实现鼠标释放事件’’’ def mouseReleaseEvent(self, event): # 若鼠标释放为双击释放,则不跟踪鼠标移动 if self.justDoubleClicked: self.justDoubleClicked = False # 若鼠标释放为单击释放,则需要改变跟踪功能的状态,如果开启跟踪功能的话就跟踪,不开启跟踪功能就不跟踪 else: # 单击鼠标 self.setMouseTracking(not self.hasMouseTracking()) if self.hasMouseTracking():self.text = '开启鼠标跟踪功能.n' + '请移动一下鼠标!n' + '单击鼠标可以关闭这个功能' else:self.text = '关闭鼠标跟踪功能.n' + '单击鼠标可以开启这个功能' self.update()
实现鼠标移动事件
’’’重新实现鼠标移动事件’’’ def mouseMoveEvent(self, event): #如果没有鼠标双击,执行 if not self.justDoubleClicked: # 窗口坐标转换为屏幕坐标 globalPos = self.mapToGlobal(event.pos()) self.text = '''鼠标位置: 窗口坐标为:QPoint({0}, {1}) 屏幕坐标为:QPoint({2}, {3}) '''.format(event.pos().x(), event.pos().y(), globalPos.x(), globalPos.y()) self.update() ’’’重新实现鼠标双击事件’’’ def mouseDoubleClickEvent(self, event): self.justDoubleClicked = True self.text = '你双击了鼠标' self.update()
实现键盘按下事件
’’’重新实现键盘按下事件’’’ def keyPressEvent(self, event): self.key = '' if event.key() == Qt.Key_Home: self.key = 'Home' elif event.key() == Qt.Key_End: self.key = 'End' elif event.key() == Qt.Key_PageUp: if event.modifiers() & Qt.ControlModifier:self.key = 'Ctrl+PageUp' else:self.key = 'PageUp' elif event.key() == Qt.Key_PageDown: if event.modifiers() & Qt.ControlModifier:self.key = 'Ctrl+PageDown' else:self.key = 'PageDown' elif Qt.Key_A <= event.key() <= Qt.Key_Z: if event.modifiers() & Qt.ShiftModifier:self.key = 'Shift+' self.key += event.text() #如果key有字符,不为空,则绘制字符 if self.key: self.key = self.key self.update() #否则就继续监视这个事件 else: QWidget.keyPressEvent(self, event)
重载tab键
’’’重新实现其他事件,适用于PyQt没有提供该事件的处理函数的情况,Tab键由于涉及焦点切换,不会传递给keyPressEvent,因此,需要在这里重新定义。’’’ def event(self, event): #如果有按键按下,并且按键是tab键 if (event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab): self.key = '在event()中捕获Tab键' self.update() return True return QWidget.event(self, event)
过滤器的使用import sysfrom PyQt5 import Qtfrom PyQt5.QtGui import *from PyQt5.QtCore import *from PyQt5.QtWidgets import *class EventFilter(QDialog): def __init__( self, parent=None ): super(EventFilter, self).__init__(parent) self.setWindowTitle(’事件过滤器’) #实例化并设置四个标签文本 self.label1 = QLabel(’请点击’) self.label2 = QLabel(’请点击’) self.label3 = QLabel(’请点击’) self.labelState = QLabel(’test’) #加载三个图片 self.image1 = QImage(’imagescartoon1.ico’) self.image2 = QImage(’imagescartoon2.ico’) self.image3 = QImage(’imagescartoon3.ico’) self.width = 600 self.height = 300 #设置初始大小 self.resize(self.width, self.height) #使用事假过滤器 self.label1.installEventFilter(self) self.label2.installEventFilter(self) self.label3.installEventFilter(self) #设置窗口布局方式并添加控件 layoyt = QGridLayout(self) layoyt.addWidget(self.label1, 500, 0) layoyt.addWidget(self.label2, 500, 1) layoyt.addWidget(self.label3, 500, 2) layoyt.addWidget(self.labelState, 600, 1) def eventFilter( self, watched, event ): #对事件一的处理过滤机制 if watched == self.label1: if event.type() == QEvent.MouseButtonPress:mouseEvent = QMouseEvent(event)if mouseEvent.buttons() == Qt.LeftButton: self.labelState.setText(’按下鼠标左键’)elif mouseEvent.buttons() == Qt.MidButton: self.labelState.setText(’按下鼠标中间键’)elif mouseEvent.buttons() == Qt.RightButton: self.labelState.setText(’按下鼠标右键’)#转换图片大小transform=QTransform()transform.scale(0.5,0.5)tmp=self.image1.transformed(transform)self.label1.setPixmap(QPixmap.fromImage(tmp)) if event.type()==QEvent.MouseButtonRelease:self.labelState.setText(’释放鼠标按键’)self.label1.setPixmap(QPixmap.fromImage(self.image1)) return QDialog.eventFilter(self,watched,event)if __name__ == ’__main__’: app=QApplication(sys.argv) dialog=EventFilter() app.installEventFilter(dialog) dialog.show() app.exec_()
运行效果如图
代码解析下面的代码意思是这个过滤器只对label1的事件进行处理,并且只处理它的鼠标按下事件和鼠标释放事件
def eventFilter( self, watched, event ): #对事件一的处理过滤机制 if watched == self.label1: if event.type() == QEvent.MouseButtonPress:mouseEvent = QMouseEvent(event)if mouseEvent.buttons() == Qt.LeftButton: self.labelState.setText(’按下鼠标左键’)elif mouseEvent.buttons() == Qt.MidButton: self.labelState.setText(’按下鼠标中间键’)elif mouseEvent.buttons() == Qt.RightButton: self.labelState.setText(’按下鼠标右键’)#转换图片大小transform=QTransform()transform.scale(0.5,0.5)tmp=self.image1.transformed(transform)self.label1.setPixmap(QPixmap.fromImage(tmp)) if event.type()==QEvent.MouseButtonRelease:self.labelState.setText(’释放鼠标按键’)self.label1.setPixmap(QPixmap.fromImage(self.image1)) #对于其他的情况会返回系统默认的处理方法 return QDialog.eventFilter(self,watched,event)
一下四行代码的意思是如果按下这个鼠标键,就会对label1装载的图片进行缩放一半
#转换图片大小transform=QTransform()transform.scale(0.5,0.5)tmp=self.image1.transformed(transform)self.label1.setPixmap(QPixmap.fromImage(tmp))
在QApplication中安装事件过滤器的使用也非常简单,只需要修改俩个地方
#使用事件过滤器
# self.label1.installEventFilter(self)
# self.label2.installEventFilter(self)
# self.label3.installEventFilter(self)
if __name__ == ’__main__’: app=QApplication(sys.argv) dialog=EventFilter() app.installEventFilter(dialog) dialog.show() app.exec_()
运行效果是一样的
好了,本文主要讲解了PyQt5信号与槽事件处理机制详细介绍与实例解析,更多关于PyQt5信号与槽的知识请查看下面的相关链接
相关文章: