首页 > 编程语言 >PyQt5/6 PySide2/6 在任务栏托盘区域编程,用于显示文字(图片)信息

PyQt5/6 PySide2/6 在任务栏托盘区域编程,用于显示文字(图片)信息

时间:2024-10-12 11:11:37浏览次数:8  
标签:count __ self PyQt5 PySide2 win32gui taskbar 任务栏 icon

PyQt5/6 PySide2/6 在任务栏编程,用于显示文字(图片)信息

本文使用PyQt5演示,其他库如PySide2/6,稍微改改就能用,因为其核心使用的是Win32gui来获取一些系统信息

代码结构

本文中全部代码全在test_taskbar.py这一个文件中编码,步骤中有变动的地方会注释标注,无改动的不会重复显示出来,需要看完整代码的,可直接移步到末尾。

一. 创建测试页面

1. 创建一个用于显示的窗口

首先我们需要创建一个窗口,用于存放可能需要显示的文字,图片信息

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_taskbar.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 任务栏编程
"""
import commctrl
import win32gui
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtWidgets import QWidget, QLabel, QGridLayout


class TaskbarWidget(QWidget):
    def __init__(self):
        super().__init__()

        self.gridLayout = QGridLayout(self)
        self.gridLayout.setContentsMargins(0, -1, 0, -1)  # 内边距
        self.label = QLabel('Hello!')
        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication

    app = QApplication(sys.argv)
    rc = TaskbarWidget()
    rc.show()
    sys.exit(app.exec_())

运行后,可以得到下面这样的窗口,窗口大小可自由改变
image

2. 给窗口增加一些限制

任务栏区域很小,所以适当的增加限制。

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_taskbar.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 任务栏编程
"""
class TaskbarWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.setMaximumSize(100, 30)  # 默认任务栏高是 40,于是我们设置窗口最大是 30
        self.setWindowFlags(Qt.FramelessWindowHint)  # 设置窗口为无边框

        ... # 忽略省略

此时再次运行,发现窗口变的很小,且没法移动,没法改变大小。但其实我们本身就不需要用鼠标来拖动
image

二. 将窗口设置到任务托盘区域中

这一步,就需要用到win32gui中提供的 API 了,用户获取系统页面的信息,供我们使用。其实你会发现,系统任务栏分为多个窗口嵌套,就好比 WEB 中的 多个div标签嵌套,所以我们必须一层层的找到我们需要的那个子窗口

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_taskbar.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 任务栏编程
"""
class TaskbarWidget(QWidget):
    def __init__(self):
        super().__init__()
        ... # 忽略省略

        self.init_set_taskbar()  # 初始化设置

    def init_set_taskbar(self):
        """初始化设置任务栏"""
        self.m_h_taskbar = win32gui.FindWindow("Shell_TrayWnd", None)  # 任务栏“Shell_TaryWnd”的窗口句柄
        self.m_h_bar = m_h_bar = win32gui.FindWindowEx(self.m_h_taskbar, 0, "ReBarWindow32",
                                                       None)  # 子窗口“ReBarWindow32”的窗口句柄
        self.m_h_min = m_h_min = win32gui.FindWindowEx(m_h_bar, 0, "MSTaskSwWClass", None)  # 子窗口“MSTaskSwWClass”的窗口句柄
        self.b = b = win32gui.GetWindowRect(m_h_bar)  # 获取m_hBar窗口尺寸b为[左,上,右,下]的数组
        win32gui.MoveWindow(m_h_min, 0, 0, b[2] - b[0] - 75, b[3] - b[1], True)  # 调整m_hMin的窗口大小,为我们的程序预留出位置

        self.setGeometry(b[2] - b[0] - 75, 5, 75, b[3] - b[1])  # 调整我们自己的窗口到预留位置的大小
        win32gui.SetParent(int(self.winId()), m_h_bar)  # 将我们自己的窗口设置为m_hBar的子窗口

运行后,如下,我们的窗口已经被设置到了 任务栏中,并使其固定显示在最右侧,靠近托盘区域
image

三. 解决没法移动的问题

有个问题,那就是一旦我们启动程序,位置就是被固定的,没法手动拖动,当我们右侧托盘区域图标增加或减少时,我们的窗口要么会被遮挡,要么会在右侧留个巨大的空隙,所以为了解决这个问题,我这里提供了一种解决办法。
但是我认为这并不是最好的方案,所以如果你有更好的方案,欢迎评论留言。

方案原理:定时检查右侧托盘区域图标的数量,每个托盘图标的尺寸是固定的,所以通过数量可算出偏移量,最后应用偏移量,实现移动。

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_taskbar.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 任务栏编程
"""
class TaskbarWidget(QWidget):
    def __init__(self):
        super().__init__()
        ... # 忽略省略

        self.init_set_taskbar()  # 初始化设置

        self.icon_count = 0  # 默认图标数量
        self.icon_status = True  # 获取初始数量时的状态
        self.timer_set_taskbar()  # 定时设置任务栏

    def timer_set_taskbar(self, interval: int = 200):  # 200ms 检查一次
        """定时设置任务栏"""
        self.time_set_taskbar = QTimer(self)
        self.time_set_taskbar.setInterval(interval)
        self.time_set_taskbar.timeout.connect(self.get_tray_icon_count)
        self.time_set_taskbar.start()  # 启动

    def get_tray_icon_count(self):
        # 获取托盘区域的窗口句柄
        tray_notify_handle = win32gui.FindWindowEx(self.m_h_taskbar, 0, "TrayNotifyWnd", None)
        sys_pager_handle = win32gui.FindWindowEx(tray_notify_handle, 0, "SysPager", None)
        notification_area_handle = win32gui.FindWindowEx(sys_pager_handle, 0, "ToolbarWindow32", None)
        # 获取托盘图标的数量
        count = win32gui.SendMessage(notification_area_handle, commctrl.TB_BUTTONCOUNT, 0, 0)
        if self.icon_status:
            self.icon_count = count  # 初始化
            self.icon_status = False
        if self.icon_count != count:
            self.dynamic_set_taskbar(icon_count=count)

    def dynamic_set_taskbar(self, icon_count=0):
        """动态设置任务栏"""
        if icon_count and self.icon_count != 0:
            x = self.icon_count - icon_count
            self.b = (self.b[0], self.b[1], self.b[2] + (x * 24), self.b[3],)
            self.icon_count = icon_count
        win32gui.MoveWindow(self.m_h_min, 0, 0, self.b[2] - self.b[0] - 75, self.b[3] - self.b[1], True)

        self.setGeometry(self.b[2] - self.b[0] - 75, 5, 75, self.b[3] - self.b[1])
        win32gui.SetParent(int(self.winId()), self.m_h_bar)

    def closeEvent(self, event):
        self.time_set_taskbar.stop()
        super(TaskbarWidget, self).closeEvent(event)


运行效果如下:
image

四. 使用此方案的项目

基于上述方案,诞生了此项目,项目地址见这里

五. 完整代码

本文所展示的功能,并不完善,你其实还可以增加很多功能,比如鼠标右键呼出设置菜单

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_taskbar.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 任务栏编程
"""
import commctrl
import win32gui
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtWidgets import QWidget, QLabel, QGridLayout


class TaskbarWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.setMaximumSize(100, 30)  # 默认任务栏高是 40,于是我们设置窗口最大是 30
        self.setWindowFlags(Qt.FramelessWindowHint)  # 设置窗口为无边框

        self.gridLayout = QGridLayout(self)
        self.gridLayout.setContentsMargins(0, -1, 0, -1)  # 内边距
        self.label = QLabel('Hello!')
        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)

        self.init_set_taskbar()  # 初始化设置

        self.icon_count = 0  # 默认图标数量
        self.icon_status = True  # 获取初始数量时的状态
        self.timer_set_taskbar()  # 定时设置任务栏

    def init_set_taskbar(self):
        """设置任务栏"""
        self.m_h_taskbar = win32gui.FindWindow("Shell_TrayWnd", None)  # 任务栏“Shell_TaryWnd”的窗口句柄
        self.m_h_bar = m_h_bar = win32gui.FindWindowEx(self.m_h_taskbar, 0, "ReBarWindow32",
                                                       None)  # 子窗口“ReBarWindow32”的窗口句柄
        self.m_h_min = m_h_min = win32gui.FindWindowEx(m_h_bar, 0, "MSTaskSwWClass", None)  # 子窗口“MSTaskSwWClass”的窗口句柄
        self.b = b = win32gui.GetWindowRect(m_h_bar)  # 获取m_hBar窗口尺寸b为[左,上,右,下]的数组
        win32gui.MoveWindow(m_h_min, 0, 0, b[2] - b[0] - 75, b[3] - b[1], True)  # 调整m_hMin的窗口大小,为我们的程序预留出位置

        self.setGeometry(b[2] - b[0] - 75, 5, 75, b[3] - b[1])  # 调整我们自己的窗口到预留位置的大小
        win32gui.SetParent(int(self.winId()), m_h_bar)  # 将我们自己的窗口设置为m_hBar的子窗口

    def timer_set_taskbar(self, interval: int = 200):  # 500ms
        """定时设置任务栏"""
        self.time_set_taskbar = QTimer(self)
        self.time_set_taskbar.setInterval(interval)
        self.time_set_taskbar.timeout.connect(self.get_tray_icon_count)
        self.time_set_taskbar.start()  # 启动

    def get_tray_icon_count(self):
        # 获取托盘区域的窗口句柄
        tray_notify_handle = win32gui.FindWindowEx(self.m_h_taskbar, 0, "TrayNotifyWnd", None)
        sys_pager_handle = win32gui.FindWindowEx(tray_notify_handle, 0, "SysPager", None)
        notification_area_handle = win32gui.FindWindowEx(sys_pager_handle, 0, "ToolbarWindow32", None)
        # 获取托盘图标的数量
        count = win32gui.SendMessage(notification_area_handle, commctrl.TB_BUTTONCOUNT, 0, 0)
        if self.icon_status:
            self.icon_count = count  # 初始化
            self.icon_status = False
        if self.icon_count != count:
            self.dynamic_set_taskbar(icon_count=count)

    def dynamic_set_taskbar(self, icon_count=0):
        """动态设置任务栏"""
        if icon_count and self.icon_count != 0:
            x = self.icon_count - icon_count
            self.b = (self.b[0], self.b[1], self.b[2] + (x * 24), self.b[3],)
            self.icon_count = icon_count
        win32gui.MoveWindow(self.m_h_min, 0, 0, self.b[2] - self.b[0] - 75, self.b[3] - self.b[1], True)

        self.setGeometry(self.b[2] - self.b[0] - 75, 5, 75, self.b[3] - self.b[1])
        win32gui.SetParent(int(self.winId()), self.m_h_bar)

    def closeEvent(self, event):
        self.time_set_taskbar.stop()
        super(TaskbarWidget, self).closeEvent(event)


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication

    app = QApplication(sys.argv)
    rc = TaskbarWidget()
    rc.show()
    sys.exit(app.exec_())

使用此方案的项目,见这里
本文章的原文地址
GitHub主页

标签:count,__,self,PyQt5,PySide2,win32gui,taskbar,任务栏,icon
From: https://www.cnblogs.com/yqbaowo/p/18459853

相关文章

  • 基于YOLO11/v10/v8/v5深度学习的安检X光危险品检测与识别系统设计与实现【python源码+
    《博主简介》小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~......
  • 如何用PyQt5创建多个窗口,同时获取多个U盘内的文件的名称,并分别在对应窗口打印文件名,要
    在PyQt5中,你可以使用QThread创建多个线程来并行处理每个U盘的文件名获取任务。每个线程负责扫描一个U盘的文件,同时在主窗口显示结果。以下是一个示例代码,用来创建多个窗口,同时在每个窗口中显示各自的U盘文件名:每个窗口使用QWidget。使用QThread创建后台线程获取U......
  • 深度学习实战人脸表情识别【源码+模型+PyQt5界面】
    @目录研究背景与意义代码下载链接一、效果演示1.1图像识别1.2视频识别1.3摄像头识别二、技术原理2.1整体技术流程2.2常见七种人脸表情2.3传统人脸定位2.4深度学习人脸定位2.4.1MTCNN2.4.2RetinaFace2.4.3CenterFace2.4.4BlazeFace2.4.5YOLO2.4.6SSD2.4.7CascadeCNN......
  • 基于深度学习的多焦点图像融合系统【数据集+深度学习模型+源码+PyQt5界面】
    @目录研究背景代码下载链接一、效果演示1.1界面设计1.2图像融合演示11.3图像融合演示21.4图像融合演示3二、技术原理2.1引言2.2融合策略2.3深度特征的提取2.4融合策略2.4.1利用深度特征计算模糊度2.4.2去噪与平滑2.4.3图像融合三、实验分析3.1实验设置3.1.1硬件环境3......
  • PyQt5 使用 QLabel 实现图像 360度 不间断旋转
    PyQt5使用QLabel实现图像360度不间断旋转当我们需要实现让一个图像360度旋转时,比如:音乐播放器中播放时,歌曲封面的旋转效果,你可以尝试使用下面的方法代码结构本文中全部代码全在test_QLabel_whirling.py这一个文件中编码,步骤中有变动的地方会注释标注,无改动的不会重复显......
  • Pyqt5+SQLite
    通常在做业务逻辑时会遇到,如何在界面上对数据库信息进行操作的问题。这里我们选用SQLite,它是一种嵌入式数据库,以单个独立的文件形式存储数据,适用于Pyqt5的开发。下面做一个小案例,将数据库信息显示在界面中。1.创建数据库代码案例如下(创建一个用户信息表):conn=sqlite3.con......
  • 基于PyQt5和SQLite的数据库操作程序
    基于PyQt5和SQLite的数据库操作程序:功能解析在现代办公和数据处理中,数据库操作是不可或缺的一部分。然而,传统的数据库管理工具往往界面复杂,操作繁琐,对于非专业人士来说存在一定的学习曲线。为了解决这个问题,我们开发了一款基于PyQt5和SQLite的数据库操作程序。该程序提供了一......
  • Win10任务栏卡死,无响应,点不动
    Win10任务栏卡死,无响应,点不动解决方法集锦https://blog.csdn.net/qq_41489540/article/details/121022117https://baijiahao.baidu.com/s?id=1786664205571224199&wfr=spider&for=pc指令是这个:输入并执行以下代码【Get-AppXPackage-AllUsers|Foreach{Add-AppxPackage......
  • PyQt5 使用 QFrame 实现页面类抽屉式的进入与退出的动画
    PyQt5使用QFrame实现页面类抽屉式的进入与退出的动画当多个页面切换,但是又不想每个页面里的内容只是简单的出现与消失,则可以使用这个QPropertyAnimation动画代码结构本文中全部代码全在test_QFrame_Animation.py这一个文件中编码,步骤中有变动的地方会注释标注,无改动的不会重......
  • PyQt项目实战-(Pyqt5+mysql制作一个TODO清单 第一部分)
    PyQt项目实战Pyqt5实现todolist工作待办的增、改、删、查功能,连接mysql存储数据(待办事项)。目录目录        1.界面设计    2.功能实现    3.界面各Button点击事件和槽函数的链接    4.mysql数据库操作工具类    5.槽函数实现......