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

PyQt5中如何实现多个嵌套QFrame同步触发hover悬停效果

多QFrame同步悬停效果实现

原生QSS的:hover伪状态仅对鼠标当前悬停的控件生效,无法直接联动父子/同级控件,要实现三个QFrame同时触发hover效果,用事件监听+动态属性的方案即可,无闪烁、兼容性好。

实现步骤

  • 给三个目标QFrame统一添加自定义布尔属性is_hover,作为悬停状态的标记
  • 为三个QFrame安装事件过滤器,监听鼠标进入(Enter)、离开(Leave)事件
  • 鼠标进入任意一个QFrame区域时,将三个控件的is_hover属性统一设为True,强制刷新控件样式
  • 鼠标离开某一控件时,先判断鼠标是否仍在最外层frame_all的区域范围内,仅当鼠标完全离开整个包裹区域时,才将三个控件的is_hover设为False,避免控件之间移动时出现样式闪烁
  • QSS样式改用属性选择器匹配悬停状态,不再使用原生:hover伪类

修改后完整代码

import sys

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

class Dynamic_Widgets(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Dynamic Widget")
        self.lbl = QLabel("this is text")
        self.btn = QPushButton("Button")
        self.lbl_1 = QLabel("-----------")

        self.lbl_r = QLabel("My text ")
        self.btn_r = QPushButton("Button888888")
        self.lbl_1r = QLabel(('\u2261' * 60))

        my_font_1 = QFont("Arial", 10, QFont.Bold)
        my_font_1.setLetterSpacing(QFont.AbsoluteSpacing, -6)
        self.lbl_1r.setFont(my_font_1)

        self.vbox = QHBoxLayout()
        self.frame = QFrame()
        self.frame_right = QFrame()
        self.frame_all = QFrame()
        self.frame.setObjectName("ob_frame")
        self.frame_right.setObjectName("ob_frame_right")
        self.frame_all.setObjectName("ob_frame_all")
        self.frame.setProperty("type", "2")
        self.frame_right.setProperty("type", "2")
        self.frame_all.setProperty("type", "2")
        # 初始化hover状态属性
        self.frame.setProperty("is_hover", False)
        self.frame_right.setProperty("is_hover", False)
        self.frame_all.setProperty("is_hover", False)

        self.framebox = QVBoxLayout(self.frame)
        self.framebox_right = QVBoxLayout(self.frame_right)
        self.framebox_all = QHBoxLayout(self.frame_all)
        self.framebox_all.setContentsMargins(0, 0, 0, 0)

        self.framebox_all.setSpacing(0)
        self.framebox_all.addWidget(self.frame)
        self.framebox_all.addWidget(self.frame_right)
        self.vbox.addWidget(self.frame_all)
        self.setLayout(self.vbox)

        self.frame.setFixedSize(100, 100)
        self.frame_right.setFixedSize(100, 100)
        self.frame_all.setFixedSize(220, 120)

        # 统一设置样式,用属性选择器匹配悬停状态
        self.setStyleSheet("""
        QFrame#ob_frame{background-color: lightgreen;}
        QFrame#ob_frame_right{background-color: lightgreen;}
        QFrame#ob_frame_all{background-color: green;}
        QFrame[type="2"][is_hover="true"] {
            background-color: yellow;
        }
        """)

        self.framebox.addWidget(self.lbl)
        self.framebox.addWidget(self.btn)
        self.framebox.addWidget(self.lbl_1)

        self.framebox_right.addWidget(self.lbl_r)
        self.framebox_right.addWidget(self.btn_r)
        self.framebox_right.addWidget(self.lbl_1r)

        # 给三个frame安装事件过滤器
        self.frame.installEventFilter(self)
        self.frame_right.installEventFilter(self)
        self.frame_all.installEventFilter(self)

    def eventFilter(self, obj, event):
        target_frames = [self.frame, self.frame_right, self.frame_all]
        if obj in target_frames:
            if event.type() == QEvent.Enter:
                # 进入任意frame,全部设为hover状态
                for f in target_frames:
                    f.setProperty("is_hover", True)
                    f.style().polish(f)
            elif event.type() == QEvent.Leave:
                # 判断鼠标是否完全离开最外层frame区域
                cursor_pos = self.frame_all.mapFromGlobal(QCursor.pos())
                if not self.frame_all.rect().contains(cursor_pos):
                    for f in target_frames:
                        f.setProperty("is_hover", False)
                        f.style().polish(f)
        return super().eventFilter(obj, event)


def main():
    app = QApplication(sys.argv)
    ex = Dynamic_Widgets()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

效果说明

运行后鼠标移动到外层QFrame、任意内层QFrame的区域时,三个QFrame会同时变为黄色悬停样式;鼠标完全移出整个外层QFrame范围后,三个控件恢复初始背景色,在三个控件区域内移动鼠标不会出现样式闪烁。

内容的提问来源于stack exchange,提问作者tckraomuqnt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:06:43