pyqt5 windows窗口固定到任务栏

效果展示

任务栏整体展示

任务栏简介

首先,大致了解以下 Windows 的任务栏,任务栏实质上是一个容器(类名为“Shell_TaryWnd”),在这个容器中有个二级容器(类名为“ReBarWindow32”),在这个二级容器中还有一个类名为“MSTaskSwWClass”的窗口,用来存放最小化后的窗口,他的窗口和“ReBarWindow32”的大小相同。

为了更加直观的理解,如图所示,红色部分为“Shell_TaryWnd”,黄色部分为“ReBarWindow32”,蓝色部分为“MSTaskSwWClass”。

任务栏描述

将窗口嵌入任务栏

想要把窗口嵌入到任务栏中,其实就是将窗口放入“ReBarWindow32”容器中,但是为了不遮挡“MSTaskSwWClass”中的图标,我们需要重新调整“MSTaskSwWClass”容器的大小,为我们自己的窗口留出位置。

在 python 中,我们可以使用 win32gui 来获取这三个容器的句柄,然后通过句柄获得窗口的位置及尺寸。

首先,需要找到任务栏“Shell_TaryWnd”的窗口句柄(win32gui.FindWindow 用来返回窗口句柄):

m_hTaskbar = win32gui.FindWindow("Shell_TrayWnd", None)

其次,找到他的子窗口“ReBarWindow32”的窗口句柄(win32gui.FindWindowEx 用来找窗口中的子窗口):

m_hBar = win32gui.FindWindowEx(m_hTaskbar, 0, "ReBarWindow32", None)

然后,找到 m_hBar 的子窗口“MSTaskSwWClass”的窗口句柄:

m_hMin = win32gui.FindWindowEx(m_hBar, 0, "MSTaskSwWClass", None)

这样,三个容器的句柄都拿到了,此时 m_hBar 和 m_hMin 的窗口位置和大小是相同的,我们通过 win32gui.MoveWindow 需要调整 m_hMin 的窗口大小,为我们的程序预留出位置:

b = win32gui.GetWindowRect(m_hBar)  # 获取m_hBar窗口尺寸b为[左,上,右,下]的数组
win32gui.MoveWindow(m_hMin, 0, 0, b[2] - b[0] - 200, b[3] - b[1], True)

其中,我们将 m_hMin 的有边界向左调整了,在右侧留出了 200 像素的空间用来存放我们自己的窗口。

接下了,我们需要调整我们自己的窗口到预留位置的大小,以 PyQt5 为例:

self.setGeometry(b[2] - b[0] - 200, 0, 200, b[3] - b[1])

然后,将我们自己的窗口设置为 m_hBar 的子窗口:

win32gui.SetParent(int(self.winId()), m_hBar)  # 设置任务栏为此窗口的父窗口

这样就完成了将自己的窗口嵌入到任务栏的所有步骤了。

完整代码

window.py(UI 文件)

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'window.ui'
#
# Created by: PyQt5 UI code generator 5.14.2
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(200, 50)
        MainWindow.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
        self.centralwidget.setStyleSheet("QWidget#centralwidget{background:#1D1D1D;}")
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setSpacing(0)
        self.gridLayout.setObjectName("gridLayout")
        self.widget_mem = QtWidgets.QWidget(self.centralwidget)
        self.widget_mem.setObjectName("widget_mem")
        self.gridLayout_5 = QtWidgets.QGridLayout(self.widget_mem)
        self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_5.setSpacing(0)
        self.gridLayout_5.setObjectName("gridLayout_5")
        self.pushButton_mem = QtWidgets.QPushButton(self.widget_mem)
        self.pushButton_mem.setEnabled(True)
        self.pushButton_mem.setMinimumSize(QtCore.QSize(74, 0))
        self.pushButton_mem.setMaximumSize(QtCore.QSize(74, 16777215))
        font = QtGui.QFont()
        font.setFamily("微软雅黑")
        self.pushButton_mem.setFont(font)
        self.pushButton_mem.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        self.pushButton_mem.setStyleSheet("QPushButton{background:Transparent;color:white;text-align:left;padding:0}\n"
"QPushButton:hover{background:Transparent;color:white;text-align:left;padding:0}")
        self.pushButton_mem.setText("")
        self.pushButton_mem.setObjectName("pushButton_mem")
        self.gridLayout_5.addWidget(self.pushButton_mem, 0, 0, 1, 1)
        self.gridLayout.addWidget(self.widget_mem, 1, 1, 1, 1)
        self.widget_cpu = QtWidgets.QWidget(self.centralwidget)
        self.widget_cpu.setEnabled(True)
        self.widget_cpu.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        self.widget_cpu.setObjectName("widget_cpu")
        self.gridLayout_4 = QtWidgets.QGridLayout(self.widget_cpu)
        self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_4.setSpacing(0)
        self.gridLayout_4.setObjectName("gridLayout_4")
        self.pushButton_cpu = QtWidgets.QPushButton(self.widget_cpu)
        self.pushButton_cpu.setEnabled(True)
        self.pushButton_cpu.setMinimumSize(QtCore.QSize(74, 0))
        self.pushButton_cpu.setMaximumSize(QtCore.QSize(74, 16777215))
        font = QtGui.QFont()
        font.setFamily("微软雅黑")
        self.pushButton_cpu.setFont(font)
        self.pushButton_cpu.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        self.pushButton_cpu.setStyleSheet("QPushButton{background:Transparent;color:white;text-align:left;padding:0}\n"
"QPushButton:hover{background:Transparent;color:white;text-align:left;padding:0}")
        self.pushButton_cpu.setText("")
        self.pushButton_cpu.setObjectName("pushButton_cpu")
        self.gridLayout_4.addWidget(self.pushButton_cpu, 1, 0, 1, 1)
        self.gridLayout.addWidget(self.widget_cpu, 1, 0, 1, 1)
        self.widget_down = QtWidgets.QWidget(self.centralwidget)
        self.widget_down.setObjectName("widget_down")
        self.gridLayout_3 = QtWidgets.QGridLayout(self.widget_down)
        self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_3.setSpacing(0)
        self.gridLayout_3.setObjectName("gridLayout_3")
        self.pushButton_down = QtWidgets.QPushButton(self.widget_down)
        self.pushButton_down.setEnabled(True)
        self.pushButton_down.setMinimumSize(QtCore.QSize(74, 0))
        self.pushButton_down.setMaximumSize(QtCore.QSize(74, 16777215))
        font = QtGui.QFont()
        font.setFamily("微软雅黑")
        self.pushButton_down.setFont(font)
        self.pushButton_down.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        self.pushButton_down.setStyleSheet("QPushButton{background:Transparent;color:white;text-align:left;padding:0}\n"
"QPushButton:hover{background:Transparent;color:white;text-align:left;padding:0}")
        self.pushButton_down.setText("")
        self.pushButton_down.setObjectName("pushButton_down")
        self.gridLayout_3.addWidget(self.pushButton_down, 0, 0, 1, 1)
        self.gridLayout.addWidget(self.widget_down, 0, 1, 1, 1)
        self.widget_up = QtWidgets.QWidget(self.centralwidget)
        self.widget_up.setObjectName("widget_up")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.widget_up)
        self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_2.setSpacing(0)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.pushButton_up = QtWidgets.QPushButton(self.widget_up)
        self.pushButton_up.setEnabled(True)
        self.pushButton_up.setMinimumSize(QtCore.QSize(74, 0))
        self.pushButton_up.setMaximumSize(QtCore.QSize(74, 16777215))
        font = QtGui.QFont()
        font.setFamily("微软雅黑")
        self.pushButton_up.setFont(font)
        self.pushButton_up.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        self.pushButton_up.setStyleSheet("QPushButton{background:Transparent;color:white;text-align:left;padding:0}\n"
"QPushButton:hover{background:Transparent;color:white;text-align:left;padding:0}")
        self.pushButton_up.setText("")
        self.pushButton_up.setObjectName("pushButton_up")
        self.gridLayout_2.addWidget(self.pushButton_up, 0, 0, 1, 1)
        self.gridLayout.addWidget(self.widget_up, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        MainWindow.setTabOrder(self.pushButton_up, self.pushButton_cpu)
        MainWindow.setTabOrder(self.pushButton_cpu, self.pushButton_mem)
        MainWindow.setTabOrder(self.pushButton_mem, self.pushButton_down)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

main.py(主程序)

from PyQt5 import QtWidgets
from PyQt5.QtGui import QCursor
from win32 import win32gui
import pywintypes
import psutil
import qtawesome
import sys
import time
from PyQt5.QtWidgets import QApplication, QMenu, QAction, qApp, QMessageBox
from window import Ui_MainWindow
from PyQt5.QtCore import Qt, QThread, pyqtSignal


class taskTool(Ui_MainWindow, QtWidgets.QMainWindow):

    def __init__(self):
        super(taskTool, self).__init__()
        self.setupUi(self)
        self.widthx = 150
        self.shiftx = 40
        # 获取任务栏实例并重置任务栏大小
        m_hTaskbar = win32gui.FindWindow("Shell_TrayWnd", None)
        m_hBar = win32gui.FindWindowEx(m_hTaskbar, 0, "ReBarWindow32", None)
        m_hMin = win32gui.FindWindowEx(m_hBar, 0, "MSTaskSwWClass", None)
        b = win32gui.GetWindowRect(m_hBar)
        win32gui.MoveWindow(m_hMin, 0, 0, b[2] - b[0] - self.widthx - self.shiftx, b[3] - b[1], True)  # 将MSTaskSwWClass缩小,预留窗口位置
        a = win32gui.GetWindowRect(m_hMin)
        # win32gui.MoveWindow(m_hMin, 0, 0, b[2] - b[0], b[3] - b[1], True)  # 程序退出请执行此句,调整任务栏为原始大小
        win32gui.SetParent(int(self.winId()), m_hBar)  # 设置任务栏为此窗口的父窗口
        # self.setGeometry(a[2] - a[0], 0, self.widthx, b[3] - b[1])
        self.setGeometry(b[2] - b[0] - self.widthx - self.shiftx, 0, self.widthx, b[3] - b[1])
        # 适配小任务栏
        if self.height() < 40:
            self.widget_cpu.hide()
            self.widget_mem.hide()
        else:
            self.widget_cpu.show()
            self.widget_mem.show()
        # self.setWindowFlag(Qt.WindowStaysOnTopHint)  # 置顶
        self.setWindowFlag(Qt.FramelessWindowHint)  # 无边框
        # self.setAttribute(QtCore.Qt.WA_TranslucentBackground)  # 透明背景
        self.iconOnButton()
        self.mainThread()
        # self.pushButton_mem.clicked.connect(self.rightMenuShow)
        # self.pushButton_cpu.clicked.connect(self.rightMenuShow)
        # self.pushButton_up.clicked.connect(self.rightMenuShow)
        # self.pushButton_down.clicked.connect(self.rightMenuShow)
        self.pushButton_mem.setContextMenuPolicy(Qt.CustomContextMenu)
        self.pushButton_mem.customContextMenuRequested.connect(self.rightMenuShow)
        self.pushButton_cpu.setContextMenuPolicy(Qt.CustomContextMenu)
        self.pushButton_cpu.customContextMenuRequested.connect(self.rightMenuShow)
        self.pushButton_up.setContextMenuPolicy(Qt.CustomContextMenu)
        self.pushButton_up.customContextMenuRequested.connect(self.rightMenuShow)
        self.pushButton_down.setContextMenuPolicy(Qt.CustomContextMenu)
        self.pushButton_down.customContextMenuRequested.connect(self.rightMenuShow)

    def iconOnButton(self):
        self.pushButton_cpu.setIcon(qtawesome.icon('fa.microchip', color='white'))
        self.pushButton_up.setIcon(qtawesome.icon('fa.long-arrow-up', color='white'))
        self.pushButton_down.setIcon(qtawesome.icon('fa.long-arrow-down', color='white'))
        self.pushButton_mem.setIcon(qtawesome.icon('fa.meetup', color='white'))

    def rightMenuShow(self, pos):  # 添加右键菜单
        menu = QMenu(self)
        menu.setStyleSheet("""\
                     QMenu {\
                     background-color:rgb(89,87,87); /*整个背景*/\
                     }\
                 QMenu::item {\
                     font-size: 10pt; \
                     color: rgb(225,225,225);  /*字体颜色*/\
                     background-color:rgb(89,87,87);\
                     padding:8px 10px; /*设置菜单项文字上下和左右的内边距,效果就是菜单中的条目左右上下有了间隔*/\
                     margin:2px 2px;/*设置菜单项的外边距*/\
                      }\
                 QMenu::item:selected { \
                     background-color:rgba(255,255,255,0.3);/*选中的样式*/\
                     }\
                 QMenu::item:pressed {/*菜单项按下效果*/\
                                           border: 1px solid rgb(60,60,61); \
                                           background-color: rgba(89,87,87, 0.5); \
                                       }\
                    """)
        quitAction = QAction("退出", self)
        quitAction.triggered.connect(self.appClose)
        self.hsnet = QAction("隐藏/显示网速", self)
        self.hsnet.triggered.connect(self.hsNetAction)
        self.hssys = QAction("隐藏/显示CPU、内存", self)
        self.hssys.triggered.connect(self.hsSysAction)
        menu.addAction(self.hsnet)
        menu.addAction(self.hssys)
        menu.addAction(quitAction)
        menu.exec_(QCursor.pos())

    def hsNetAction(self):
        if self.widget_up.isHidden():
            self.widget_down.show()
            self.widget_up.show()
        elif self.widget_cpu.isVisible():
            self.widget_down.hide()
            self.widget_up.hide()
        else:
            QMessageBox.information(self, '提示', '不能将信息全部隐藏')

    def hsSysAction(self):
        if self.widget_mem.isHidden():
            self.widget_mem.show()
            self.widget_cpu.show()
        elif self.widget_up.isVisible():
            self.widget_mem.hide()
            self.widget_cpu.hide()
        else:
            QMessageBox.information(self, '提示', '不能将信息全部隐藏')

    def mainThread(self):
        try:
            self.main_thread = Main_Thread()
            self.main_thread.thread_signal.connect(self.Update_UI)
            self.main_thread.start()
        except Exception as e:
            print(e)

    def Update_UI(self, msm):
        # import random
        self.pushButton_mem.setText(f"{msm['mem']}")
        self.pushButton_down.setText(f"{msm['down']}")
        self.pushButton_up.setText(f"{msm['up']}")
        self.pushButton_cpu.setText(f"{msm['cpu']}")
        # self.setToolTip("qqq")

    def appClose(self):
        # 将任务栏调整回原来大小
        m_hTaskbar = win32gui.FindWindow("Shell_TrayWnd", None)
        m_hBar = win32gui.FindWindowEx(m_hTaskbar, 0, "ReBarWindow32", None)
        m_hMin = win32gui.FindWindowEx(m_hBar, 0, "MSTaskSwWClass", None)
        b = win32gui.GetWindowRect(m_hBar)
        win32gui.MoveWindow(m_hMin, 0, 0, b[2] - b[0], b[3] - b[1], True)
        a = win32gui.GetWindowRect(m_hMin)
        qApp.quit()


class Main_Thread(QThread):
    thread_signal = pyqtSignal(dict)

    def __init__(self):
        super().__init__()
        self.info = {
            "cpu": "",
            "mem": "",
            "up": "",
            "down": ""
        }

    def run(self):
        def formatNum(size):
            ds = ['', 'K', 'M', 'G', 'T']
            for d in ds:
                if size < 1000:
                    if d:
                        return str(size) + d + "/s"
                    else:
                        return f'{round(size/1024, 1)}K/s'
                size = round(size / 1024, 1)
            return '0b/s'
        while True:
            self.info['cpu'] = f"{psutil.cpu_percent()}%"
            self.info['mem'] = f"{psutil.virtual_memory().percent}%"
            net0 = psutil.net_io_counters()
            time.sleep(1)
            net1 = psutil.net_io_counters()
            self.info['up'] = f"{formatNum((net1.bytes_sent - net0.bytes_sent) * 1)}"
            self.info['down'] = f"{formatNum((net1.bytes_recv - net0.bytes_recv) * 1)}"
            self.thread_signal.emit(self.info)


if __name__ == '__main__':
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    app = QApplication(sys.argv)
    ex = taskTool()
    ex.show()
    sys.exit(app.exec_())

results matching ""

    No results matching ""