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
相关产品推荐
相关产品推荐

