QPushButton按住鼠标未释放时如何同时响应leaveEvent事件
问题根因
当QPushButton触发鼠标按下事件时,Qt框架会自动为该控件开启鼠标捕获,所有后续鼠标输入事件会直接派发给当前按钮,不会触发常规的enterEvent、leaveEvent这类光标进出边界事件,这就是按住鼠标时重写的leaveEvent不生效的核心原因。
实现方案
不需要依赖leaveEvent处理按住状态下的光标移出逻辑,改为重写mouseMoveEvent,在鼠标按住的状态下实时判断光标是否处于按钮的有效区域内,手动切换图标即可。
修正后的完整实现代码如下:
from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QPushButton class ToggledButton(QPushButton): def __init__(self, icon: QIcon = None, active_icon: QIcon = None): super().__init__() self.active_icon = active_icon self.inactive_icon = icon self._is_pressed = False # 标记鼠标按住状态 self.setIcon(icon) self.setCheckable(True) self.setFlat(False) # 开启鼠标追踪,保证按住状态下能稳定收到移动事件 self.setMouseTracking(True) def mousePressEvent(self, event): super().mousePressEvent(event) if event.button() == Qt.LeftButton: self._is_pressed = True self.setIcon(self.active_icon) def mouseMoveEvent(self, event): super().mouseMoveEvent(event) # 仅在鼠标按住状态下处理区域判断逻辑 if self._is_pressed: # 判断当前光标位置是否在按钮控件的矩形范围内 if self.rect().contains(event.pos()): self.setIcon(self.active_icon) else: self.setIcon(self.inactive_icon) def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) if event.button() == Qt.LeftButton: self._is_pressed = False self.setIcon(self.inactive_icon)
逻辑说明
- 新增
_is_pressed私有标记位,仅在鼠标左键按住时才触发光标位置判断,避免普通状态下误切换图标 - 鼠标按住状态下,每次鼠标移动就校验光标坐标是否落在按钮自身的矩形区域内:在区域内显示激活图标,移出区域显示非激活图标
- 鼠标释放时重置标记位,无论光标当前位置在哪都切回非激活图标,完全匹配预期交互效果
- 原有重写的
leaveEvent可以直接删除,该逻辑已经被mouseMoveEvent中的区域判断覆盖,不会出现状态冲突。
内容的提问来源于stack exchange,提问作者Uncle Bob
相关产品推荐
相关产品推荐

