如何使用wxWidgets将任务栏图标定义为放置目标?SetDropTarget在TaskBarIcon和Icon类中均不可用。我想要类似的东西:
class TextDropTarget(wx.TextDropTarget):
def __init__(self, obj):
wx.TextDropTarget.__init__(self)
self.obj = obj
def OnDropText(self, x, y, data):
self.obj.action(data)
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self):
super().__init__()
self.SetIcon(wx.Icon(wx.Bitmap(TRAY_ICON)), TRAY_TOOLTIP)
self.SetDropTarget(TextDropTarget(self))
def action(self, data):
# Do something
经过研究,我可以自己回答问题:
在Mac上,这是可能的,因为它是此OS上的常见功能。在Windows和Linux上,可以通过丑陋的解决方法来实现。我想知道为什么这些操作系统不支持此功能,因为确实有一些使用此功能的用例。这是Windows的解决方法(也称为here):
def FindSysPagerWindow():
hWnd = win32gui.FindWindowEx(win32gui.GetDesktopWindow(), 0, "Shell_TrayWnd", None)
if hWnd:
hWnd = win32gui.FindWindowEx(hWnd, None, "TrayNotifyWnd", None)
if hWnd:
hWnd = win32gui.FindWindowEx(hWnd, None, "SysPager", None)
return hWnd
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self, r):
super().__init__()
hSysPager = FindSysPagerWindow()
# Get rectangle of system area
self.region = win32gui.GetWindowRect(hSysPager)
self.frm = None
self.source = None
def callback(self, hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime):
length = user32.GetWindowTextLengthW(hwnd)
title = ctypes.create_unicode_buffer(length + 1)
user32.GetWindowTextW(hwnd, title, length + 1)
if self.frm is None and (title.value == "Drag"):
self.source = GetProcessFilename(GetProcessId(dwEventThread, hwnd))
self.frm = SystemTrayFrame(self.region, self.onDrop)
def DragDetectThread(self):
ole32.CoInitialize(0)
WinEventProc = WinEventProcType(self.callback)
user32.SetWinEventHook.restype = ctypes.wintypes.HANDLE
hookId = user32.SetWinEventHook(win32con.EVENT_OBJECT_SHOW, win32con.EVENT_OBJECT_SHOW,
0, WinEventProc, 0, 0, win32con.WINEVENT_OUTOFCONTEXT)
msg = ctypes.wintypes.MSG()
while user32.GetMessageW(ctypes.byref(msg), 0, 0, 0) != 0:
user32.TranslateMessageW(msg)
user32.DispatchMessageW(msg)
user32.UnhookWinEvent(hookId)
ole32.CoUninitialize()
这里仍然没有解决的问题是,正确的阻力源的检测工作不可靠。如果在拖动开始时鼠标移动得太快,则检测到的源可能是错误的。但这仅是该信息很重要的问题。
from pynput.mouse import Listener, Button
...
self.listener = Listener(on_click=self.onMouseButtonEvent)
self.listener.start()
def onMouseButtonEvent(self, x, y, button, pressed):
if self.frm is not None and (button == Button.left) and not pressed:
self.frm.Destroy()
self.frm = None
def onDrop(self, x, y, data):
# Do something with the dropped data
if self.frm is not None:
self.frm.Destroy()
self.frm = None
class SystemTrayFrame(wx.Frame):
def __init__(self, r, cbDrop):
super().__init__(None, wx.ID_ANY, "TransparentFrame", pos=(r[0], r[1]), size=(r[2] - r[0], r[3] - r[1]),
style=wx.STAY_ON_TOP)
dropTarget = DropTarget(cbDrop)
self.SetDropTarget(dropTarget)
self.SetTransparent(0)
self.Show()
到目前为止,如果可以将整个系统托盘区域作为您应用程序的放置目标,就可以了。但是,如果您想将放置区域限制为系统任务栏图标,则现在需要一个丑陋的解决方法:
a)将您的幻想系统任务栏图标替换为具有易于检测的形状的唯一彩色图标。
b)制作系统托盘区域的屏幕快照,将系统托盘图标替换回您喜欢的应用程序图标,然后搜索唯一彩色图标的位置:
im = ImageGrab.grab(bbox=self.region)
# Search for icon position and size (because of optional scaling by OS)
当操作系统启用缩放时,搜索操作可能会更加复杂。
c)使用此结果放置透明框架。
希望这可以帮助遇到相同问题的其他人。