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信号与槽事件处理经典案例
importsys
fromPyQt5.QtCoreimport(QEvent,QTimer,Qt)
fromPyQt5.QtWidgetsimport(QApplication,QMenu,QWidget)
fromPyQt5.QtGuiimportQPainter
classWidget(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秒),然后在运行,就可以明白这行代码的意思。
defgiveHelp(self):
self.text="请点击这里触发追踪鼠标功能"
#重绘事件,也就是触发paintEvent函数。
self.update()
'''重新实现关闭事件'''
defcloseEvent(self,event):
print("Closed")
'''重新实现上下文菜单事件'''
defcontextMenuEvent(self,event):
#实例化菜单,添加子菜单onetwo并附加快捷键功能,关联槽函数
menu=QMenu(self)
oneAction=menu.addAction("&One")
twoAction=menu.addAction("&Two")
oneAction.triggered.connect(self.one)
twoAction.triggered.connect(self.two)
#如果message为空,执行
ifnotself.message:
#在菜单中添加一条分割线
menu.addSeparator()
#添加自菜单three,关联槽函数
threeAction=menu.addAction("Thre&e")
threeAction.triggered.connect(self.three)
#菜单栏出现在鼠标的位置
menu.exec_(event.globalPos())
'''上下文菜单槽函数'''
defone(self):
self.message="MenuoptionOne"
self.update()
deftwo(self):
self.message="MenuoptionTwo"
self.update()
defthree(self):
self.message="MenuoptionThree"
self.update()
'''重新实现绘制事件'''
defpaintEvent(self,event):
text=self.text
i=text.find("\n\n")
ifi>=0:
text=text[0:i]
#若触发了键盘按钮,则在文本信息中记录这个按钮信息。
ifself.key:
text+="\n\n你按下了:{0}".format(self.key)
painter=QPainter(self)
painter.setRenderHint(QPainter.TextAntialiasing)
#绘制信息文本的内容
painter.drawText(self.rect(),Qt.AlignCenter,text)
#若消息文本存在则在底部居中绘制消息,5秒钟后清空消息文本并重绘。
ifself.message:
#显示给定坐标处的文本,坐标,对齐方式。文本内容
painter.drawText(self.rect(),Qt.AlignBottom|Qt.AlignHCenter,
self.message)
#5秒钟后触发清空信息的函数,并重新绘制事件
QTimer.singleShot(5000,self.clearMessage)
QTimer.singleShot(5000,self.update)
'''清空消息文本的槽函数'''
defclearMessage(self):
self.message=""
'''重新实现调整窗口大小事件'''
defresizeEvent(self,event):
self.text="调整窗口大小为:QSize({0},{1})".format(
event.size().width(),event.size().height())
self.update()
'''重新实现鼠标释放事件'''
defmouseReleaseEvent(self,event):
#若鼠标释放为双击释放,则不跟踪鼠标移动
ifself.justDoubleClicked:
self.justDoubleClicked=False
#若鼠标释放为单击释放,则需要改变跟踪功能的状态,如果开启跟踪功能的话就跟踪,不开启跟踪功能就不跟踪
else:
#单击鼠标
self.setMouseTracking(notself.hasMouseTracking())
ifself.hasMouseTracking():
self.text="开启鼠标跟踪功能.\n"+\
"请移动一下鼠标!\n"+\
"单击鼠标可以关闭这个功能"
else:
self.text="关闭鼠标跟踪功能.\n"+\
"单击鼠标可以开启这个功能"
self.update()
'''重新实现鼠标移动事件'''
defmouseMoveEvent(self,event):
#如果没有鼠标双击,执行
ifnotself.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()
'''重新实现鼠标双击事件'''
defmouseDoubleClickEvent(self,event):
self.justDoubleClicked=True
self.text="你双击了鼠标"
self.update()
'''重新实现键盘按下事件'''
defkeyPressEvent(self,event):
self.key=""
ifevent.key()==Qt.Key_Home:
self.key="Home"
elifevent.key()==Qt.Key_End:
self.key="End"
elifevent.key()==Qt.Key_PageUp:
ifevent.modifiers()&Qt.ControlModifier:
self.key="Ctrl+PageUp"
else:
self.key="PageUp"
elifevent.key()==Qt.Key_PageDown:
ifevent.modifiers()&Qt.ControlModifier:
self.key="Ctrl+PageDown"
else:
self.key="PageDown"
elifQt.Key_A<=event.key()<=Qt.Key_Z:
ifevent.modifiers()&Qt.ShiftModifier:
self.key="Shift+"
self.key+=event.text()
#如果key有字符,不为空,则绘制字符
ifself.key:
self.key=self.key
self.update()
#否则就继续监视这个事件
else:
QWidget.keyPressEvent(self,event)
'''重新实现其他事件,适用于PyQt没有提供该事件的处理函数的情况,Tab键由于涉及焦点切换,不会传递给keyPressEvent,因此,需要在这里重新定义。'''
defevent(self,event):
#如果有按键按下,并且按键是tab键
if(event.type()==QEvent.KeyPressand
event.key()==Qt.Key_Tab):
self.key="在event()中捕获Tab键"
self.update()
returnTrue
returnQWidget.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函数
importsys
fromPyQt5.QtCoreimport(QEvent,QTimer,Qt)
fromPyQt5.QtWidgetsimport(QApplication,QMenu,QWidget)
fromPyQt5.QtGuiimportQPainter
classWidget(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秒),然后在运行,就可以明白这行代码的意思。
defgiveHelp(self):
self.text="请点击这里触发追踪鼠标功能"
#重绘事件,也就是触发paintEvent函数。
self.update()
初始化运行结果如下
然后是重新实现窗口关闭事件与上下文菜单事件,主要影响message标量的结果,paintEvent负责把这个变量在窗口底部输出
'''重新实现关闭事件'''
defcloseEvent(self,event):
print("Closed")
'''重新实现上下文菜单事件'''
defcontextMenuEvent(self,event):
#实例化菜单,添加子菜单onetwo并附加快捷键功能,关联槽函数
menu=QMenu(self)
oneAction=menu.addAction("&One")
twoAction=menu.addAction("&Two")
oneAction.triggered.connect(self.one)
twoAction.triggered.connect(self.two)
#如果message为空,执行
ifnotself.message:
#在菜单中添加一条分割线
menu.addSeparator()
#添加自菜单three,关联槽函数
threeAction=menu.addAction("Thre&e")
threeAction.triggered.connect(self.three)
#菜单栏出现在鼠标的位置
menu.exec_(event.globalPos())
'''上下文菜单槽函数'''
defone(self):
self.message="MenuoptionOne"
self.update()
deftwo(self):
self.message="MenuoptionTwo"
self.update()
defthree(self):
self.message="MenuoptionThree"
self.update()
绘制事件是代码的核心事件,它的作用是时刻跟随text和message这两个变量的信息,并把text内容绘制到窗口的中部,把message的内容绘制到窗口的底部
'''重新实现绘制事件'''
defpaintEvent(self,event):
text=self.text
i=text.find("\n\n")
ifi>=0:
text=text[0:i]
#若触发了键盘按钮,则在文本信息中记录这个按钮信息。
ifself.key:
text+="\n\n你按下了:{0}".format(self.key)
painter=QPainter(self)
painter.setRenderHint(QPainter.TextAntialiasing)
#绘制信息文本的内容
painter.drawText(self.rect(),Qt.AlignCenter,text)
#若消息文本存在则在底部居中绘制消息,5秒钟后清空消息文本并重绘。
ifself.message:
#显示给定坐标处的文本,坐标,对齐方式。文本内容
painter.drawText(self.rect(),Qt.AlignBottom|Qt.AlignHCenter,
self.message)
#5秒钟后触发清空信息的函数,并重新绘制事件
QTimer.singleShot(5000,self.clearMessage)
QTimer.singleShot(5000,self.update)
'''清空消息文本的槽函数'''
defclearMessage(self):
self.message=""
接下来是调整窗口大小事件
'''重新实现调整窗口大小事件'''
defresizeEvent(self,event):
self.text="调整窗口大小为:QSize({0},{1})".format(
event.size().width(),event.size().height())
self.update()
实现鼠标释放事件,若为双击释放,则不跟随鼠标移动,若为单击释放,则需要跟随鼠标移动状态进行更改,如果开启跟踪功能就跟踪,否则就不跟综
'''重新实现鼠标释放事件''' defmouseReleaseEvent(self,event): #若鼠标释放为双击释放,则不跟踪鼠标移动 ifself.justDoubleClicked: self.justDoubleClicked=False #若鼠标释放为单击释放,则需要改变跟踪功能的状态,如果开启跟踪功能的话就跟踪,不开启跟踪功能就不跟踪 else: #单击鼠标 self.setMouseTracking(notself.hasMouseTracking()) ifself.hasMouseTracking(): self.text="开启鼠标跟踪功能.\n"+\ "请移动一下鼠标!\n"+\ "单击鼠标可以关闭这个功能" else: self.text="关闭鼠标跟踪功能.\n"+\ "单击鼠标可以开启这个功能" self.update()
实现鼠标移动事件
'''重新实现鼠标移动事件'''
defmouseMoveEvent(self,event):
#如果没有鼠标双击,执行
ifnotself.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()
'''重新实现鼠标双击事件'''
defmouseDoubleClickEvent(self,event):
self.justDoubleClicked=True
self.text="你双击了鼠标"
self.update()
实现键盘按下事件
'''重新实现键盘按下事件''' defkeyPressEvent(self,event): self.key="" ifevent.key()==Qt.Key_Home: self.key="Home" elifevent.key()==Qt.Key_End: self.key="End" elifevent.key()==Qt.Key_PageUp: ifevent.modifiers()&Qt.ControlModifier: self.key="Ctrl+PageUp" else: self.key="PageUp" elifevent.key()==Qt.Key_PageDown: ifevent.modifiers()&Qt.ControlModifier: self.key="Ctrl+PageDown" else: self.key="PageDown" elifQt.Key_A<=event.key()<=Qt.Key_Z: ifevent.modifiers()&Qt.ShiftModifier: self.key="Shift+" self.key+=event.text() #如果key有字符,不为空,则绘制字符 ifself.key: self.key=self.key self.update() #否则就继续监视这个事件 else: QWidget.keyPressEvent(self,event)
重载tab键
'''重新实现其他事件,适用于PyQt没有提供该事件的处理函数的情况,Tab键由于涉及焦点切换,不会传递给keyPressEvent,因此,需要在这里重新定义。''' defevent(self,event): #如果有按键按下,并且按键是tab键 if(event.type()==QEvent.KeyPressand event.key()==Qt.Key_Tab): self.key="在event()中捕获Tab键" self.update() returnTrue returnQWidget.event(self,event)
过滤器的使用
importsys
fromPyQt5importQt
fromPyQt5.QtGuiimport*
fromPyQt5.QtCoreimport*
fromPyQt5.QtWidgetsimport*
classEventFilter(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('images\cartoon1.ico')
self.image2=QImage('images\cartoon2.ico')
self.image3=QImage('images\cartoon3.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)
defeventFilter(self,watched,event):
#对事件一的处理过滤机制
ifwatched==self.label1:
ifevent.type()==QEvent.MouseButtonPress:
mouseEvent=QMouseEvent(event)
ifmouseEvent.buttons()==Qt.LeftButton:
self.labelState.setText('按下鼠标左键')
elifmouseEvent.buttons()==Qt.MidButton:
self.labelState.setText('按下鼠标中间键')
elifmouseEvent.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))
ifevent.type()==QEvent.MouseButtonRelease:
self.labelState.setText('释放鼠标按键')
self.label1.setPixmap(QPixmap.fromImage(self.image1))
returnQDialog.eventFilter(self,watched,event)
if__name__=='__main__':
app=QApplication(sys.argv)
dialog=EventFilter()
app.installEventFilter(dialog)
dialog.show()
app.exec_()
运行效果如图
代码解析
下面的代码意思是这个过滤器只对label1的事件进行处理,并且只处理它的鼠标按下事件和鼠标释放事件
defeventFilter(self,watched,event):
#对事件一的处理过滤机制
ifwatched==self.label1:
ifevent.type()==QEvent.MouseButtonPress:
mouseEvent=QMouseEvent(event)
ifmouseEvent.buttons()==Qt.LeftButton:
self.labelState.setText('按下鼠标左键')
elifmouseEvent.buttons()==Qt.MidButton:
self.labelState.setText('按下鼠标中间键')
elifmouseEvent.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))
ifevent.type()==QEvent.MouseButtonRelease:
self.labelState.setText('释放鼠标按键')
self.label1.setPixmap(QPixmap.fromImage(self.image1))
#对于其他的情况会返回系统默认的处理方法
returnQDialog.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信号与槽的知识请查看下面的相关链接
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。