python - 如何使用 PyQT5 制作应用程序桌面工具栏 UI
问题描述
如何将 UI 制作/注册为应用程序桌面工具栏(Windows)以防止其他应用程序使用 UI 使用的桌面区域?需要在底部创建自定义的类似任务栏的面板,并且其余的窗口适合大小,而不是在 ui 下。
解决方案
至少我从wxappbars和appbar找到问题。经过对 PyQt5 的一些适应后,它对我有用。
"""
Registering an Application Desktop Toolbar in Windows for PyQt5 window.
Taken from https://gist.github.com/swdevbali/495f902162446b30cd567b2a44d86d79 and
https://github.com/sabren/ceomatic/blob/master/wxappbars.py,
thanks for swdevbali and sabren
Adapted by Elendiar.
"""
import ctypes, sys
from ctypes import wintypes
from ctypes import *
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QDesktopWidget, QWidget
from PyQt5.QtCore import *
from copy import deepcopy
shell32 = windll.shell32
user32 = windll.user32
import win32api, win32con, win32gui
class APPBARDATA(Structure):
_fields_= [
("cbSize", wintypes.DWORD),
("hWnd", wintypes.HWND),
("uCallbackMessage", ctypes.c_ulong),
("uEdge", c_ulong),
("rc", wintypes.RECT),
("lParam", wintypes.LPARAM),
]
PAPPBARDATA = POINTER(APPBARDATA)
class ABEdge:
Left = 0
Top = 1
Right = 2
Bottom = 3
Float = 4
class ABMsg:
ABM_NEW = 0
ABM_REMOVE = 1
ABM_QUERYPOS = 2
ABM_SETPOS = 3
ABM_GETSTATE = 4
ABM_GETTASKBARPOS = 5
ABM_ACTIVATE = 6
ABM_GETAUTOHIDEBAR = 7
ABM_SETAUTOHIDEBAR = 8
ABM_WINDOWPOSCHANGED = 9
ABM_SETSTATE = 10
class ABNotify:
ABN_STATECHANGE = 0
ABN_POSCHANGED = 1
ABN_FULLSCREENAPP = 2
ABN_WINDOWARRANGE = 3
class RegisterInfo(object):
def __init__(self):
self._window = None
self.callbackId = 0
self.isRegistered = False
self.edge = ABEdge.Float
self.originalStyle = None
self.originalPosition = None
self.originalSize = None
self.originalResizeMode = None
@property
def window(self):
return self._window
@window.setter
def window(self, window):
self._window = window
self._hWnd = window.winId().__int__()
self._oldWndProc = win32gui.SetWindowLong(self._hWnd,
win32con.GWL_WNDPROC,
self.WndProc)
# http://wiki.wxpython.org/HookingTheWndProc
def WndProc(self, hWnd, msg, wParam, lParam ):
if msg == win32con.WM_DESTROY:
self._restoreOldWndProc()
elif msg == self.callbackId:
if wParam == ABNotify.ABN_POSCHANGED:
_ABSetPos(self.edge, self.window)
else:
return win32gui.\
CallWindowProc(self._oldWndProc, hWnd, msg, wParam, lParam)
def _restoreOldWndProc(self):
win32api.SetWindowLong(self._hWnd,
win32con.GWL_WNDPROC,
self._oldWndProc)
_registeredWindowInfo = {}
def _GetRegisterInfo(appbarWindow):
geometry = appbarWindow.geometry()
reg = RegisterInfo()
reg.callBackId = 0
reg.window = appbarWindow
reg.isRegistered = False
reg.edge = ABEdge.Top
reg.originalStyle = appbarWindow.windowFlags()
reg.originalPosition = QPoint(geometry.x(), geometry.y())
reg.originalSize = QSize(geometry.width(), geometry.height())
_registeredWindowInfo[appbarWindow] = reg
return _registeredWindowInfo[appbarWindow]
def _RestoreWindow(appbarWindow):
info = _GetRegisterInfo(appbarWindow)
appbarWindow.setWindowFlags(info.originalStyle)
appbarWindow.move(info.originalPosition)
appbarWindow.resize(info.originalSize)
def SetAppBar(appbarWindow, edge, barSize):
info = _GetRegisterInfo(appbarWindow)
info.edge = edge
abd = APPBARDATA()
abd.cbSize = wintypes.DWORD(sizeof(abd))
abd.hWnd = wintypes.HWND(appbarWindow.winId().__int__())
if (edge == ABEdge.Float) and info.isRegistered:
shell32.SHAppBarMessage(ABMsg.ABM_REMOVE, PAPPBARDATA(abd))
info.isRegistered = False
_RestoreWindow(appbarWindow)
elif not info.isRegistered:
info.isRegistered = True
info.callbackId = win32api.RegisterWindowMessage("AppBarMessage")
shell32.SHAppBarMessage(ABMsg.ABM_NEW, PAPPBARDATA(abd))
appbarWindow.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
_ABSetPos(info.edge, appbarWindow, barSize)
def RemoveAppBar(appbarWindow):
print('REMOVING')
info = _GetRegisterInfo(appbarWindow)
abd = APPBARDATA()
shell32.SHAppBarMessage(ABMsg.ABM_REMOVE, PAPPBARDATA(abd))
info.isRegistered = False
#_RestoreWindow(appbarWindow)
def _DoResize(appBarWindow, rect):
appBarWindow.resize(rect.Width, rect.Height)
appBarWindow.move(rect.Left, rect.Top)
def _ABSetPos(edge, appbarWindow, barSize):
barData = APPBARDATA()
barData.cbSize = wintypes.DWORD(sizeof(barData))
barData.hWnd = appbarWindow.winId().__int__()
barData.uEdge = edge
screen = QDesktopWidget().screenGeometry()
deskW = screen.width()
deskH = screen.height()
if barData.uEdge == ABEdge.Left or barData.uEdge == ABEdge.Right:
winW = barSize
winH = deskH
barData.rc.top = 0
barData.rc.bottom = deskH
if barData.uEdge == ABEdge.Left:
barData.rc.left = 0
barData.rc.right = winW
else:
barData.rc.right = deskW
barData.rc.left = deskW - winW
else:
winW = deskW
winH = barSize
barData.rc.left = 0
barData.rc.right = deskW
if barData.uEdge == ABEdge.Top:
barData.rc.top = 0
barData.rc.bottom = winH
else:
barData.rc.bottom = deskH
barData.rc.top = deskH - winH
oldLeft = deepcopy(barData.rc.left) # deepcopy because after reopen x from 0
become w -> 222 and window isnt visible.
shell32.SHAppBarMessage(ABMsg.ABM_QUERYPOS, PAPPBARDATA(barData))
shell32.SHAppBarMessage(ABMsg.ABM_SETPOS, PAPPBARDATA(barData))
barData.rc.left = deepcopy(oldLeft)
def _resize():
x = barData.rc.left
y = barData.rc.top
w = barData.rc.right - barData.rc.left
h = barData.rc.bottom - barData.rc.top
appbarWindow.resize(w, h)
appbarWindow.move(x, y)
# This is done async, because windows will send a resize after a new appbar is
added.
# if we size right away, the windows resize comes last and overrides us.
QTimer.singleShot(300, _resize)
if __name__=="__main__":
# DPI scaling
# QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
# QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
app = QtWidgets.QApplication(sys.argv)
win = QWidget()
win.setWindowFlags(Qt.FramelessWindowHint |
Qt.WindowStaysOnTopHint |
Qt.CustomizeWindowHint |
Qt.Tool
)
btn = QtWidgets.QPushButton('Click-to-close', win)
btn.clicked.connect(QtWidgets.qApp.quit)
barSize = 222
SetAppBar(win, ABEdge.Left, barSize)
win.show()
sys.exit(app.exec_())
推荐阅读
- excel - 如何找到 2 个元素到 n 列的排列,每个行组合都是唯一的
- java - 如何在可视化代码 IDE 中调试 ANT + JAVA 项目
- angular - 无法在延迟加载的模块中动态导航 - Angular Dynamic Routing
- c - 谁提供了 C 标准库 gcc 或 glibc?
- android - 应用关闭时收不到 fcm 通知
- pyqt - 如何摆脱两个小部件之间的空白
- python - Python中ctypes.structure的序列化
- postgresql - 出现错误 - 没有函数与给定的名称和参数类型匹配。在 Postgres
- angular - 如果 src 来自 dataBindings,如何以角度方式播放 IFRAME
- android - 如何在 Android Studio 中克服这个错误,我的网络没有问题但不知道他们说的是网络?