pyqt5 事件event 过滤 installEventFilter eventFilter QEvent.MetaCall

本文深入探讨PyQt5中的事件处理机制,包括Qt事件类型、自定义事件注册及使用,同时介绍了如何利用installEventFilter捕获并处理应用事件,以及使用nativeEvent识别Windows系统消息的方法。

代码:

from PyQt5.QtCore import QThread, pyqtSignal, QDateTime, QObject,QEvent
from PyQt5.QtWidgets import QApplication, QDialog, QLineEdit, QLabel
import time
import sys


class BackendThread(QObject):
    # 通过类成员对象定义信号
    update_date = pyqtSignal(str)

    # 处理业务逻辑
    def run(self):
        while True:
            data = QDateTime.currentDateTime()
            currTime = data.toString("yyyy-MM-dd hh:mm:ss")
            self.update_date.emit(str(currTime))
            time.sleep(1)


class Window(QDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.setWindowTitle('PyQt 5界面实时更新例子')
        self.resize(400, 100)
        self.input = QLabel(self)
        self.input.resize(400, 100)
        self.initUI()

    def initUI(self):
        # 创建线程
        self.backend = BackendThread()
        # 连接信号
        self.backend.update_date.connect(self.handleDisplay)
        self.thread = QThread()
        self.backend.moveToThread(self.thread)
        # 开始线程
        self.thread.started.connect(self.backend.run)
        self.thread.start()



    # 将当前时间输出到文本框
    def handleDisplay(self, data):
        self.input.setText(data)

    def keyPressEvent(self, event):
        print('keyPressEvent')
        pass

    def eventFilter(self, objwatched, event):
        eventType = event.type()
        #print(eventType)
        if eventType == QEvent.KeyPress:
            print('eventFilter KeyPress')

        elif eventType == QEvent.MetaCall:
            print('eventFilter MetaCall')
        return super().eventFilter(objwatched, event)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    app.installEventFilter(win)
    sys.exit(app.exec_())
参考资料:

PyQt学习随笔:Qt事件QEvent.type类型常量及其含义资料汇总详细内容速查
https://blog.youkuaiyun.com/LaoYuanPython/article/details/102527651

PyQt5-事件处理机制
https://www.cnblogs.com/ygzhaof/p/10132824.html

 def eventFilter(self, obj, event):
       
       if obj == self.lineEdit:
        if event.type()== QEvent.FocusIn:
            #self.inp_text_signal.emit("已进")
            if self.lineEdit.text().strip() == '请输入用户名':
                self.lineEdit.clear()
            
                
            print("ok1")
        elif event.type()== QEvent.FocusOut:
            if self.lineEdit.text().strip() == '':
                self.lineEdit.setText("请输入用户名")
            print("ok2")
        else:
            pass
        return False
        

除了《PyQt学习随笔:Qt事件QEvent.type类型常量及其含义资料速查》介绍的Qt已经定义的事件外,Qt还支持自定义事件。

为了方便起见,可以使用 registerEventType()函数为应用程序注册和保留自定义事件类型。这样做可以避免意外地重新使用应用程序中其他地方已在使用的自定义事件类型。

用户自定义事件的值必须在QEvent.User事件到QEvent.MaxUser事件之间,其中QEvent.User事件的常量值为1000,QEvent.MaxUser事件的常量值为65535。
PyQt学习随笔:应用中通过installEventFilter安装重写的eventFilter捕获应用事件的方法
https://blog.youkuaiyun.com/LaoYuanPython/article/details/102539773
8、registerEventType(hint = -1)方法,用于注册和返回一个用户自定义事件类型,它是线程安全的函数。其中

参考:

PyQt5 使用windows系统消息 识别设备插拔

关键字: python, PyQt5, nativeEvent, WM_DEVICECHANGE, winEvent

以下代码已经过精简,去除无关部分,可直接运行,运行环境为python3.7.2 64bit PyQt5.11.3 64bit

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
from ctypes.wintypes import MSG

class MainWindow(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)
        
    def nativeEvent(self, eventType, msg):
        message = MSG.from_address(int(msg))
        if message.message == 0x219:  # WM_DEVICECHANGE
            if message.wParam == 0x8000:  # DBT_DEVICEARRIVAL
                print("in")
            elif message.wParam == 0x8004:  # DBT_DEVICEREMOVECOMPLETE
                print("out")
            elif message.wParam == 0x0007:  # DBT_DEVNODE_CHANGED
                print("node")
        return False, msg

app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

运行时,会有一个很小的界面,界面里什么都没有。插拔设备会在解释器的终端里输出。

之前就有想用PyQt5识别windows系统消息。但是今天才开始去弄这个。查了很多资料,花了有半天时间,才弄完。因为资料很多都已经过期了。所以我遇到的几个问题跟大家说一下。

nativeEvent是QWidget的虚函数
关于这一部分的Qt5的文档在这里。Qt5是C++的多平台的GUI库。
http://doc.qt.io/qt-5/qwidget.html#nativeEvent
PyQt是针对Python的。使用起来略有不同,相关文档也并不是很全,不是很系统化,也只能用的时候再找了。

网上查的MSG.from_address(msg.init())并不能在我的环境中运行。
我试了一下可以使用MSG.from_address(int(msg))
但我并不清楚是什么原因。

关于nativeEvent的返回值。有些资料说必须是False 否则会有问题。这个我没有试。第二个返回值也是按资料上填的。运行倒是能成功,但如果有人熟悉这一块,欢迎回复我。

我查阅的一些资料:
https://blog.youkuaiyun.com/hidxl/article/details/53633874
https://blog.youkuaiyun.com/neverstop_2009/article/details/47361309
https://blog.youkuaiyun.com/swqqcs/article/details/7251321
————————————————
https://blog.youkuaiyun.com/josephzhang1012/article/details/86694876

<think>在PyQt5中,继承QWidget并重写eventFilter方法是一种常见的事件处理方式。eventFilter方法允许你监控其他对象的事件,并在事件被处理之前进行拦截或处理。以下是正确重写eventFilter方法的步骤和示例: ### 步骤1:创建自定义QWidget子类 首先,创建一个继承自QWidget的类,并重写其`eventFilter`方法。 ### 步骤2:重写eventFilter方法 在`eventFilter`方法中,你需要: 1. 判断事件来源对象(obj) 2. 判断事件类型(event.type()) 3. 根据需要进行处理,并决定是否拦截事件(返回True表示拦截,False表示继续传递) ### 步骤3:安装事件过滤器 使用`installEventFilter`方法将事件过滤器安装到需要监控的对象上。 ### 完整示例代码 ```python import sys from PyQt5.QtCore import QEvent, Qt from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QMessageBox class MyWidget(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle('事件过滤器示例') self.setGeometry(300, 300, 300, 200) layout = QVBoxLayout() # 创建一个标签 self.label = QLabel('点击我试试', self) self.label.setAlignment(Qt.AlignCenter) self.label.setStyleSheet("background-color: lightblue; border: 1px solid black;") layout.addWidget(self.label) # 安装事件过滤器到label self.label.installEventFilter(self) self.setLayout(layout) # 重写eventFilter方法 def eventFilter(self, obj, event): # 只处理label的事件 if obj == self.label: # 鼠标按下事件 if event.type() == QEvent.MouseButtonPress: if event.button() == Qt.LeftButton: QMessageBox.information(self, "提示", "你点击了标签!") return True # 事件被处理,不再传递 # 鼠标移动事件 elif event.type() == QEvent.MouseMove: # 这里可以处理鼠标移动事件 pass # 其他事件交给父类处理 return super().eventFilter(obj, event) if __name__ == '__main__': app = QApplication(sys.argv) window = MyWidget() window.show() sys.exit(app.exec_()) ``` ### 关键点说明: 1. **事件过滤器的安装**:在`initUI`方法中,通过`self.label.installEventFilter(self)`将当前窗口(`self`)的事件过滤器安装到标签上。这样,标签的事件会先经过窗口的`eventFilter`方法。 2. **事件判断**:在`eventFilter`方法中,首先判断事件来源对象`obj`是否为`self.label`,然后判断事件类型(如`QEvent.MouseButtonPress`)。 3. **事件处理**:对特定事件进行处理,例如当鼠标左键按下时弹出消息框。返回`True`表示事件已被处理,不再传递给目标对象;返回`False`或调用父类方法表示事件继续传递。 4. **调用父类方法**:对于不处理的事件,应调用`super().eventFilter(obj, event)`,以确保父类的事件过滤器有机会处理。 ### 注意事项: - 如果只对特定控件的事件感兴趣,务必先检查`obj`,避免处理不相关控件的事件。 - 在事件处理中,可以使用`event`对象获取详细信息。例如,鼠标事件可以转换为`QMouseEvent`来获取位置等。 - 不要忘记对不需要拦截的事件返回父类的`eventFilter`方法,以确保事件传递链的完整性。 ### 扩展:处理键盘事件 ```python def eventFilter(self, obj, event): if obj == self.label: if event.type() == QEvent.KeyPress: # 键盘按下事件 key_event = event # 实际上event已经是QKeyEvent类型 if key_event.key() == Qt.Key_Space: print("按下了空格键") return True return super().eventFilter(obj, event) ``` ### 引用说明: 以上实现参考了事件过滤器的基本使用方法[^1][^3]。 §§相关问题§§ 1.PyQt5中,如何为多个控件安装同一个事件过滤器? 2. 事件过滤器和重写事件处理函数(如mousePressEvent)有什么区别? 3. 如何移除已经安装的事件过滤器?</think>### PyQt5 继承 QWidget 正确重写 `eventFilter` 方法指南 在 PyQt5 中,通过继承 `QWidget` 并重写 `eventFilter` 方法,可以实现对控件事件的精细化监控。以下是完整实现步骤: #### 一、基本框架结构 ```python from PyQt5.QtCore import QEvent, Qt from PyQt5.QtWidgets import QWidget, QApplication class CustomWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) # 初始化UI并安装事件过滤self.setup_ui() self.target_widget.installEventFilter(self) # 关键步骤[^1][^3] def eventFilter(self, obj, event): """重写的事件过滤器方法""" # 1. 检查事件来源 if obj is self.target_widget: # 2. 检查事件类型 if event.type() == QEvent.MouseButtonPress: # 3. 事件处理逻辑 print("目标控件被点击") return True # 拦截事件 # 4. 其他事件传递给父类处理 return super().eventFilter(obj, event) def setup_ui(self): """创建UI界面""" self.target_widget = QLabel("点击我测试事件过滤器", self) # ... 其他UI初始化代码 ``` #### 二、核心实现步骤详解 1. **安装事件过滤器** 在构造函数中为目标控件安装过滤器: ```python self.target_widget.installEventFilter(self) ``` 这样目标控件的事件会首先经过 `eventFilter` 方法[^1][^3] 2. **识别事件来源对象** ```python def eventFilter(self, obj, event): # 精确匹配特定控件 if obj is self.target_button: # 匹配控件类型 if isinstance(obj, QLineEdit): ``` 3. **处理常用事件类型** | 事件类型 | 常量 | 说明 | |---------|------|------| | 鼠标点击 | `QEvent.MouseButtonPress` | 鼠标按下时触发 | | 鼠标释放 | `QEvent.MouseButtonRelease` | 鼠标释放时触发 | | 键盘按下 | `QEvent.KeyPress` | 按键按下时触发 | | 焦点进入 | `QEvent.FocusIn` | 控件获得焦点 | | 焦点离开 | `QEvent.FocusOut` | 控件失去焦点 | 4. **事件拦截决策** ```python if event.type() == QEvent.MouseButtonPress: # 检查鼠标按键 if event.button() == Qt.LeftButton: print("左键点击") return True # 拦截事件 elif event.button() == Qt.RightButton: print("右键点击") return True # 拦截事件 return False # 允许事件继续传递 ``` #### 三、完整示例:带点击检测的自定义Widget ```python import sys from PyQt5.QtCore import QEvent, Qt from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QVBoxLayout, QMessageBox) class EventFilterWidget(QWidget): def __init__(self): super().__init__() self.setup_ui() self.setWindowTitle("事件过滤器示例") self.resize(300, 200) def setup_ui(self): layout = QVBoxLayout() # 创建目标控件 self.target_label = QLabel("点击我触发事件过滤器", self) self.target_label.setStyleSheet(""" background-color: #e0e0e0; border: 2px solid #a0a0a0; padding: 20px; font-size: 16px; """) self.target_label.setAlignment(Qt.AlignCenter) # 安装事件过滤self.target_label.installEventFilter(self) layout.addWidget(self.target_label) self.setLayout(layout) def eventFilter(self, obj, event): # 只处理目标标签的事件 if obj is self.target_label: # 鼠标按下事件 if event.type() == QEvent.MouseButtonPress: if event.button() == Qt.LeftButton: QMessageBox.information(self, "事件触发", "左键点击检测成功!") return True # 拦截事件 # 鼠标悬停事件 elif event.type() == QEvent.Enter: self.target_label.setStyleSheet("background-color: #a0d0ff;") return True # 鼠标离开事件 elif event.type() == QEvent.Leave: self.target_label.setStyleSheet("background-color: #e0e0e0;") return True # 其他事件交给父类处理 return super().eventFilter(obj, event) if __name__ == "__main__": app = QApplication(sys.argv) window = EventFilterWidget() window.show() sys.exit(app.exec_()) ``` #### 四、高级用法与技巧 1. **多控件统一处理** ```python def eventFilter(self, obj, event): # 处理所有按钮的鼠标进入事件 if isinstance(obj, QPushButton): if event.type() == QEvent.Enter: obj.setStyleSheet("background: yellow;") return True elif event.type() == QEvent.Leave: obj.setStyleSheet("") return True return False ``` 2. **键盘事件处理** ```python def eventFilter(self, obj, event): if obj is self.text_edit: if event.type() == QEvent.KeyPress: if event.key() == Qt.Key_Return: print("回车键在文本框中按下") return True # 拦截回车键 return False ``` 3. **动态管理过滤器** ```python # 启用/禁用事件过滤def toggle_event_filter(self, enabled): if enabled: self.target_widget.installEventFilter(self) else: self.target_widget.removeEventFilter(self) ``` #### 五、最佳实践 1. **对象生命周期管理** ```python def __init__(self): super().__init__() self.target_widget = QLabel(self) # 建立父子关系 self.target_widget.installEventFilter(self) ``` 2. **性能优化** ```python def eventFilter(self, obj, event): # 快速排除不关心的控件 if obj not in [self.widget1, self.widget2]: return False # 具体事件处理... ``` 3. **事件传递机制** - 后安装的过滤器先执行(栈结构) - 父控件的过滤器优先于子控件执行 - 返回 `True` 阻止事件继续传递,返回 `False` 允许传递 > 提示:对于简单事件处理,可直接重写控件的事件方法(如 `mousePressEvent()`),但 `eventFilter` 提供更灵活的跨对象监控能力[^1]。 #### 六、常见问题解决 **问题:事件过滤器不生效** 解决方案: 1. 确认已调用 `installEventFilter()` 2. 检查 `obj` 比较是否正确 3. 确保返回 `super().eventFilter()` 处理未拦截事件 **问题:事件处理冲突** 解决方案: 1. 使用 `event.ignore()` 允许事件继续传递 2.过滤器方法中精确控制返回 `True/False` 通过正确重写 `eventFilter` 方法,可以实现高度定制化的事件处理逻辑,增强PyQt5应用程序的交互能力[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值