You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:12:25