查看: 335|回复: 0

PyQt6 QML消息提示框动态添加与悬停暂停实现

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
在桌面端 PyQt6 项目中,消息提示往往不能只弹一个阻塞式对话框,还需要处理多类型、自动关闭、悬停暂停、位置切换和操作按钮回调。原文的实现把通知列表封装成独立的 MessageListView.qml,由 main.qml 负责页面和触发按钮,main.py 只加载 QML。工程结构上,main.py 与 main.qml 同目录,MessageListView.qml 作为独立通知列表组件被引用。原文目录中列出了“库安装”,但正文未给出具体安装命令,本文不补写。

Python 入口使用 PyQt6.QtGui.QGuiApplication 和 PyQt6.QtQml.QQmlApplicationEngine,通过 QUrl.fromLocalFile 加载同目录 main.qml,并在 engine.rootObjects() 为空时打印错误后 sys.exit(-1)。
  1. import sys
  2. import os
  3. from PyQt6.QtCore import QUrl
  4. from PyQt6.QtGui import QGuiApplication
  5. from PyQt6.QtQml import QQmlApplicationEngine
  6. def main():
  7.     app = QGuiApplication(sys.argv)
  8.     engine = QQmlApplicationEngine()
  9.     qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "main.qml")
  10.     engine.load(QUrl.fromLocalFile(qml_path))
  11.     if not engine.rootObjects():
  12.         print("错误:无法加载 QML 文件", file=sys.stderr)
  13.         sys.exit(-1)
  14.     sys.exit(app.exec())
  15. if __name__ == "__main__":
  16.     main()
复制代码

main.qml 中 ApplicationWindow 尺寸为 1000×700,最小 360×240,定义 property int position 保存通知位置,MessageListView 设置 z:100、id: notifier、position 绑定 root.position,并监听 onActionResultClicked。当操作按钮回传的 resultText 非空时,再调用 notifier.addMsg("info", resultText, "", "", "") 弹出一条信息提示。
  1. MessageListView {
  2.     z: 100
  3.     id: notifier
  4.     position: root.position
  5.     onActionResultClicked: (msgId, label, resultText) => {
  6.         if (resultText !== "")
  7.             notifier.addMsg("info", resultText, "", "", "")
  8.     }
  9. }
复制代码

底部控件面板中的四个 MsgButton 分别调用 addMsg 添加 success、error、warning、info 消息,参数依次为类型、标题、正文、操作按钮文字和按钮点击后的提示文本。例如 error 调用传入“重试连接”和“正在重新连接...”,warning 传入“立即清理”和“正在清理缓存文件...”。PosButton 只覆盖左上、顶部居中、右上三个位置。

MessageListView.qml 根节点是 Item,枚举 Position 定义为 TopLeft、TopCenter、TopRight。文档注释写支持左上、顶部居中、右上、右下四种位置,但实际枚举和 main.qml 的位置按钮只有三种,ListView 的 x 计算也只处理 TopLeft、TopCenter 和默认右上;右下没有对应分支。这是原文注释与实现不一致的地方,若需要右下,必须扩展枚举和 x/y 计算。

核心数据用 ListModel 管理,字段包括 msgId、type、title、content、duration、actionLabel、actionResultText、removing。nextId 从 1 递增。addMsg 的 duration 默认 4000,error 为 6000,warning 为 5000;actionLabel 为空字符串时不显示操作按钮。对外方法还有 dismissMsg(msgId) 和 clearAll():dismissMsg 遍历模型找到 msgId,先把 removing 置 true,再 remove(i),代码注释说明 delayRemove 让条目在动画期间仍显示;clearAll 倒序把 removing 置 true 后逐条 remove。操作按钮通过 actionResultClicked(int msgId, string label, string resultText) 信号回传。
  1. function addMsg(typeStr, title, content, actionLabel, actionResultText) {
  2.     var dur = 4000
  3.     if (typeStr === "error") dur = 6000
  4.     else if (typeStr === "warning") dur = 5000
  5.     messageModel.append({
  6.         msgId: nextId++,
  7.         type: typeStr,
  8.         title: title,
  9.         content: content,
  10.         duration: dur,
  11.         actionLabel: actionLabel || "",
  12.         actionResultText: actionResultText || "",
  13.         removing: false
  14.     })
  15. }
复制代码

四种消息类型的视觉映射在 colorForType、bgForType、iconForType 中完成:success 用 #10b981、背景 #ecfdf5、图标 ✓;error 用 #ef4444、背景 #fef2f2、图标 ✕;warning 用 #f59e0b、背景 #fffbeb、图标 !;info 用 #3b82f6、背景 #eff6ff、图标 i。默认分支按 info 处理。

自动关闭由 MessageCard 内的 progressTimer 完成,interval 为 100,running 条件为 autoDismissMs > 0 && !isRemoving && !paused,每次 progress += 100 / autoDismissMs,达到 1.0 后发出 dismissRequested()。paused 的悬停判断来自 hoverArea、actionArea、closeArea 的 containsMouse;同时组件维护 _hoverCount 和 anyCardHovered,delegate 把 paused 设为 notifierRoot.anyCardHovered,实现任一卡片悬停时全部暂停。从 QML 绑定关系看,delegate 显式传入 paused 后会覆盖 MessageCard 内部“property bool paused: isHovered”的初始绑定,实际生效的是全局悬停广播。

列表动画包括 add、remove、move、populate:添加时透明度 0→1、x 从 380 进入,时长 400ms;移除时透明度到 0、x 到 760,时长约 300–350ms;重排时对 y 和 x 做 400ms 动画;populate 让透明度 0→1、y 从 2000 进入。delayRemove 通过 _delayRemove 和 releaseDelayTimer 控制,Timer interval 380ms,略长于淡出动画 300ms,确保动画播完再释放。ListView 在 onCountChanged 中用 Qt.callLater(positionViewAtEnd()) 延迟滚动到底部,避免 contentHeight 未刷新。ListView 宽度 380,高度取 Math.min(contentHeight, 父高度-40),interactive 设为 false,spacing 为 12。

排查时可以按以下顺序看:QML 未加载时检查 main.py 的 rootObjects 判断和 main.qml 路径;通知不显示时检查 MessageListView 是否被 z:100 遮盖、parent 尺寸和 position 绑定;消息不自动关闭时检查 autoDismissMs、paused、isRemoving 和 progressTimer 的 running 条件;悬停暂停异常时检查 _hoverCount 增减以及 delegate 的 paused 绑定;位置切换异常时注意当前只实现三种位置;移除动画异常时检查 _delayRemove、releaseDelayTimer 和 ListView.delayRemove。该组件适合桌面应用中的操作结果、网络错误、空间警告、版本提示等通知场景,核心优势是 QML 组件化、ListModel 管理数据和非阻塞动画提示。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-21 15:54 , Processed in 0.023640 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部