wxWidgets TaskBarIcon作为放置目标

问题描述 投票:0回答:1

如何使用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
wxwidgets
1个回答
0
投票

经过研究,我可以自己回答问题:

在Mac上,这是可能的,因为它是此OS上的常见功能。在Windows和Linux上,可以通过丑陋的解决方法来实现。我想知道为什么这些操作系统不支持此功能,因为确实有一些使用此功能的用例。这是Windows的解决方法(也称为here):

  1. 获取系统托盘区域:

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
  1. [创建一个检测拖动开始事件并在系统托盘上方创建透明框架的线程:
    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()

这里仍然没有解决的问题是,正确的阻力源的检测工作不可靠。如果在拖动开始时鼠标移动得太快,则检测到的源可能是错误的。但这仅是该信息很重要的问题。

  1. 使用Pynput为鼠标按钮事件创建侦听器,以检测鼠标左键向上事件,该事件被解释为拖动结束事件。侦听器以及onDrop方法都会破坏透明框架:
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
  1. 透明框的类看起来像这样:
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)使用此结果放置透明框架。

希望这可以帮助遇到相同问题的其他人。

© www.soinside.com 2019 - 2024. All rights reserved.