PyQt5/6 PySide2/6 在系统底部任务栏编程,用于显示文字(图片)信息
PyQt5/6 PySide2/6 在系统底部任务栏编程,用于显示文字(图片)信息
本文使用PyQt5
演示,其他库如PySide2/6
,稍微改改就能用,因为其核心使用的是Pywin32
中的 Win32gui
来获取一些系统信息
代码结构
本文中全部代码全在test_taskbar.py
这一个文件中编码,步骤中有变动的地方会注释标注,无改动的不会重复显示出来,需要看完整代码的,可直接移步到末尾。
一. 创建测试页面
需要安装PyQt5,pywin32
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_())
运行后,可以得到下面这样的窗口,窗口大小可自由改变
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(80, 30) # 默认任务栏高是 40,于是我们设置窗口最大是 30
self.setWindowFlags(Qt.FramelessWindowHint) # 设置窗口为无边框
... # 忽略省略
此时再次运行,发现窗口变的很小,且没法移动,没法改变大小。但其实我们本身就不需要用鼠标来拖动
二. 将窗口设置到任务栏中
这一步,就需要用到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”的窗口句柄
# 子窗口“ReBarWindow32”的窗口句柄
self.m_h_bar = win32gui.FindWindowEx(self.m_h_taskbar, 0, "ReBarWindow32", None)
self.m_h_min = win32gui.FindWindowEx(self.m_h_bar, 0, "MSTaskSwWClass", None) # 子窗口“MSTaskSwWClass”的窗口句柄
self.b = win32gui.GetWindowRect(self.m_h_bar) # 获取m_hBar窗口尺寸b为[左,上,右,下]的数组
self.move_window()
def move_window(self):
# 调整m_hMin的窗口大小,为我们的程序预留出位置
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) # 将我们自己的窗口设置为m_hBar的子窗口
运行后,如下,我们的窗口已经被设置到了 任务栏中,并使其固定显示在最右侧,靠近托盘区域
三. 解决移动的问题
问题:一旦我们启动程序,位置就是被固定的,没法手动拖动,当我们右侧托盘区域图标增加或减少时,我们的窗口要么会被遮挡,要么会在右侧留个巨大的空隙,所以为了解决这个问题,我这里提供了两种解决办法。
但是我认为这并不是最好的方案,所以如果你有更好的方案,欢迎评论留言。
方案1:定时检查任务栏的尺寸(推荐)
方案原理:定时检查任务栏尺寸变化,此尺寸不包含托盘区域,故可以根据这个尺寸,移动我们的窗口到相应位置
#!/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.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_taskbar_size)
self.time_set_taskbar.start() # 启动
def get_taskbar_size(self):
"""获取任务栏尺寸"""
self.b_new = win32gui.GetWindowRect(self.m_h_bar)
if self.b_new == self.b: # 尺寸没变化,则直接返回
return
self.b = self.b_new
self.move_window()
def closeEvent(self, event):
self.time_set_taskbar.stop()
super(TaskbarWidget, self).closeEvent(event)
运行效果如下:
方案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.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_taskbar_size) # 方案1
self.time_set_taskbar.timeout.connect(self.get_tray_icon_count) # 方案2
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
self.move_window()
def closeEvent(self, event):
self.time_set_taskbar.stop()
super(TaskbarWidget, self).closeEvent(event)
运行效果如下:
四. 相关项目
基于本片提到的内容,开发一个项目,GitHub 地址见这里,Gitee 地址见这里
五. 兼容性问题
Windows 11
,经过测试,暂时只能使用方案1
运行,方案2会卡死
。
六. 完整代码
本文来自博客园作者:星尘的博客,转载请注明出处:https://www.cnblogs.com/yqbaowo/p/18459853