赞
踩
信号与槽可以说是对事件处理机制的高级封装,如果说事件是用来创建窗口控件的,那么信号与槽就是用来对这个窗口控件进行使用的。比如一个按钮,当我们使用这个按钮时,只关心clicked信号,至于这个按钮如何接收并处理鼠标点击事件,然后再发射这个信号,则不用关心。但是如果要重载一个按钮,这时就要关心这个问题了。比如可以改变它的行为:在鼠标按键按下时触发clicked信号,而不是在释放时。
●键盘事件:按键按下和松开。
●鼠标事件:鼠标指针移动、鼠标按键按下和松开。
●拖放事件:用鼠标进行拖放。
●滚轮事件:鼠标滚轮滚动。
●绘屏事件:重绘屏幕的某些部分。
●定时事件:定时器到时。
●焦点事件:键盘焦点移动。
●移动事件:Widget的位置改变。
●大小改变事件: Widget的大小改变。
●显示和隐藏事件:Widget显示和隐藏。
●窗口事件:窗口是否为当前窗口。
还有一些常见的Qt事件,比如Socket事件、剪贴板事件、字体改变事件、布局改变事件等。
(1)重新实现事件函数
比如mousePressEvent()、keyPressEvent()、paintEvent()。这是最常规的事件处理方法。
(2)重新实现QObject.event()
一般用在PyQt没有提供该事件的处理函数的情况下,即增加新事件时。
(3)安装事件过滤器
如果对QObject调用installEventFilter,则相当于为这个QObject安装了一个事件过滤器,对于QObject的全部事件来说,它们都会先传递到事件过滤函数eventFilter中,在这个函数中我们可以抛弃或者修改这些事件,比如可以对自己感兴趣的事件使用自定义的事件处理机制,对其他事件使用默认的事件处理机制。由于这种方法会对调用installEventFiter的所有QObject的事件进行过滤,因此如果要过滤的事件比较多,则会降低程序的性能。
(4)在QApplication中安装事件过滤器
这种方法比上一种方法更强大:QApplication的事件过滤器将捕获所有QObject的所有事件,而且第一个获得该事件。也就是说,在将事件发送给其他任何一个事件过滤器之前(就是在第三种方法之前),都会先发送给QApplication的事件过滤器。
(5)重新实现QApplication的notify()方法
PyQt使用notify(来分发事件。要想在任何事件处理器之前捕获事件,唯一的方法就是重新实现QApplication的notify()。在实践中,在调试时才会使用这种方法。
- import sys
- from PyQt5.QtCore import (QEvent, QTimer, Qt)
- from PyQt5.QtWidgets import (QApplication, QMenu, QWidget)
- from PyQt5.QtGui import QPainter
-
-
- class Widget(QWidget):
- def __init__(self, parent=None):
- super(Widget, self).__init__(parent)
- self.justDoubleClicked = False
- self.key = ""
- self.text = ""
- self.message = ""
- self.resize(400, 300)
- self.move(100, 100)
- self.setWindowTitle("Events")
- QTimer.singleShot(0, self.giveHelp) # 避免窗口大小重绘事件的影响,可以把参数0改变成3000(3秒),然后在运行,就可以明白这行代码的意思。
-
- def giveHelp(self):
- self.text = "请点击这里触发追踪鼠标功能"
- self.update() # 重绘事件,也就是触发paintEvent函数。
-
- '''重新实现关闭事件'''
-
- def closeEvent(self, event):
- print("Closed")
-
- '''重新实现上下文菜单事件'''
-
- def contextMenuEvent(self, event):
- menu = QMenu(self)
- oneAction = menu.addAction("&One")
- twoAction = menu.addAction("&Two")
- oneAction.triggered.connect(self.one)
- twoAction.triggered.connect(self.two)
- if not self.message:
- menu.addSeparator()
- 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("\n\n")
- if i >= 0:
- text = text[0:i]
- if self.key: # 若触发了键盘按钮,则在文本信息中记录这个按钮信息。
- text += "\n\n你按下了: {0}".format(self.key)
- painter = QPainter(self)
- painter.setRenderHint(QPainter.TextAntialiasing)
- painter.drawText(self.rect(), Qt.AlignCenter, text) # 绘制信息文本的内容
- if self.message: # 若消息文本存在则在底部居中绘制消息,5秒钟后清空消息文本并重绘。
- painter.drawText(self.rect(), Qt.AlignBottom | Qt.AlignHCenter,
- self.message)
- 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()
- if self.key:
- self.key = self.key
- self.update()
- else:
- QWidget.keyPressEvent(self, event)
-
- '''重新实现其他事件,适用于PyQt没有提供该事件的处理函数的情况,Tab键由于涉及焦点切换,不会传递给keyPressEvent,因此,需要在这里重新定义。'''
-
- def event(self, event):
- 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_()

第二种事件处理方法是重载event函数。对于窗口所有的事件都会传递给event函数,event函数会根据事件的类型,把事件分配给不同的函数进行处理。比如对于绘图事件,event会交给paintEvent函数处理;对于鼠标移动事件,event会交给mouseMoveEvent函数处理;对于键盘按下事件,event会交给keyPressEvent函数处理。有一种特殊情况是对Tab键的触发行为,event函数对Tab键的处理机制是把焦点从当前窗口控件的位置切换到Tab键次序中下一个窗口控件的位置,并返回True,而不是交给keyPressEvent函数处理。因此这里需要在event函数中对按下Tab键的处理逻辑重新改写,使它与键盘上普通的键没什么不同。
- from PyQt5.QtGui import *
- from PyQt5.QtCore import *
- from PyQt5.QtWidgets import *
- import sys
-
-
- 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("images/cartoon1.ico")
- self.image2 = QImage("images/cartoon1.ico")
- self.image3 = QImage("images/cartoon1.ico")
-
- self.width = 600
- self.height = 300
-
- self.resize(self.width, self.height)
-
- # 对要过滤的控件设置installEventFilter
- # 这些控件的所有事件都会被eventFilter函数接收并处理
- self.label1.installEventFilter(self)
- self.label2.installEventFilter(self)
- self.label3.installEventFilter(self)
-
- mainLayout = QGridLayout(self)
- mainLayout.addWidget(self.label1, 500, 0)
- mainLayout.addWidget(self.label2, 500, 1)
- mainLayout.addWidget(self.label3, 500, 2)
- mainLayout.addWidget(self.LabelState, 600, 1)
- self.setLayout(mainLayout)
-
- def eventFilter(self, watched, event):
- if watched == self.label1: # 只对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()
- dialog.show()
- sys.exit(app.exec_())

- from PyQt5.QtGui import *
- from PyQt5.QtCore import *
- from PyQt5.QtWidgets import *
- import sys
-
-
- 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("images/cartoon1.ico")
- self.image2 = QImage("images/cartoon1.ico")
- self.image3 = QImage("images/cartoon1.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)
-
- mainLayout = QGridLayout(self)
- mainLayout.addWidget(self.label1, 500, 0)
- mainLayout.addWidget(self.label2, 500, 1)
- mainLayout.addWidget(self.label3, 500, 2)
- mainLayout.addWidget(self.LabelState, 600, 1)
- self.setLayout(mainLayout)
-
- def eventFilter(self, watched, event):
- print(type(watched))
- if watched == self.label1: # 只对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()
- # 对于在QApplication中安装installEventFilter, 下面代码的意思是dialog的所有
- # 事件都要经过eventFilter函数处理,而不仅仅是三个标签控件的事件
- app.installEventFilter(dialog)
- dialog.show()
- sys.exit(app.exec_())

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。