【问题标题】:Add image in window tray menu在窗口托盘菜单中添加图像
【发布时间】:2017-08-16 14:46:42
【问题描述】:

我正在使用 python 为 Windows 编写简单的托盘。

我成功创建了托盘图标、菜单、子菜单。我坚持为特定托盘项目添加图像。

这是我使用的代码。 (Link) 即使这段代码也不起作用。 Windows 文档不清楚。

def addMenuItem(self, wID, title, menu):
        path = os.path.dirname(os.path.abspath(__file__))
        path += "\print_pref.ico"
        option_icon = self.prep_menu_icon(path)
        item, extras = win32gui_struct.PackMENUITEMINFO(text=title,
                                                                hbmpItem=option_icon,
                                                                wID=wID)

        win32gui.InsertMenuItem(menu, 0, 1, item)


def prep_menu_icon(self, icon):
        # First load the icon.
        ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON)
        ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON)
        hicon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, ico_x, ico_y, win32con.LR_LOADFROMFILE)

        hdcBitmap = win32gui.CreateCompatibleDC(0)
        hdcScreen = win32gui.GetDC(0)
        hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y)
        hbmOld = win32gui.SelectObject(hdcBitmap, hbm)
        # Fill the background.
        brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU)
        win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush)
        # unclear if brush needs to be feed.  Best clue I can find is:
        # "GetSysColorBrush returns a cached brush instead of allocating a new
        # one." - implies no DeleteObject
        # draw the icon
        win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL)
        win32gui.SelectObject(hdcBitmap, hbmOld)
        win32gui.DeleteDC(hdcBitmap)
        return hbm

谁能帮帮我。

编辑

self.tray = win32gui.CreatePopupMenu()
self.addMenuItem(1, "Open", self.tray)

附加图片。在“打开”旁边的小框中,我想要图像。

【问题讨论】:

  • 任何机会你可以发布一个完整的例子,我试图帮助解决这个问题,但我什至无法让菜单出现,我过去使用过这个,所以有一些经验有了它,就是有点生锈了……
  • 我给出的链接有完整的代码示例。

标签: python windows win32gui systray


【解决方案1】:

对于可能不会导致错误的类型的句柄存在问题。

我通过使用 PyCDCPyCBitMapwin32ui 类而不是句柄来完成这项工作。

尝试将prep_menu_icon改成这样:

def prep_menu_icon(self, icon):
    # First load the icon.
    ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON)
    ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON)
    hIcon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, ico_x, ico_y, win32con.LR_LOADFROMFILE)

    hwndDC = win32gui.GetWindowDC(self.hwnd)
    dc = win32ui.CreateDCFromHandle(hwndDC)
    memDC = dc.CreateCompatibleDC()
    iconBitmap = win32ui.CreateBitmap()
    iconBitmap.CreateCompatibleBitmap(dc, ico_x, ico_y)
    oldBmp = memDC.SelectObject(iconBitmap)
    brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU)

    win32gui.FillRect(memDC.GetSafeHdc(), (0, 0, ico_x, ico_y), brush)
    win32gui.DrawIconEx(memDC.GetSafeHdc(), 0, 0, hIcon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL)

    memDC.SelectObject(oldBmp)
    memDC.DeleteDC()
    win32gui.ReleaseDC(self.hwnd, hwndDC)

    return iconBitmap.GetHandle()

我得到菜单项图标:

【讨论】:

  • @obgnaw 我知道,因为我从 2000 年开始写 MFCMFCCBitmapHBITMAP 运算符来获取句柄,因为可以'在 python 中不是这样的操作员,我在PyCBitmap 中寻找类似的东西来获取句柄,那里是PyCBitmap.GetHandle
【解决方案2】:

我似乎无法在我的计算机上设置软件包,所以无法真正测试这个,但是这条线

option_icon = self.prep_menu_icon("\print_pref.ico")

让我有些担心。我不确定您是否正在阅读您认为的文件。

\ 表示转义序列。在 Windows 上,您需要将这些反斜杠加倍以防止它们像"\\print_pref.ico" 一样被转义。如果您尝试在当前目录中加载文件,您可能根本不需要它,只需给出文件名 - "print_pref.ico"。如果您尝试在驱动器的根目录中查找文件,则需要提供驱动器号"C:\\print_pref.ico"

【讨论】:

  • 我已编辑问题。尝试使用 os.path.dirname 。它转到正确的 .ico 文件。可能是.ico文件有什么问题?我在哪里可以获得示例图标文件。
  • @Durgaprasad 我认为您误解了我的观点(这可能是问题,也可能不是问题)。你不能做\print_pref.ico。 Python 会将\p 解释为一些转义序列。你需要做\\print_pref.ico。 Windows 目录不能很好地发挥作用,因为它们使用反斜杠作为路径分隔符,但这意味着代码中的转义序列,因此它们必须始终加倍(或者,您可以这样做 r"\print_pref.ico" - r 意味着解释它字面上没有转义序列)。
  • 或者只使用os.path.join()os.path.normpath(),这似乎也比dir += filename更具可读性
  • @Matthew 部分问题可能是路径和文件名以及他如何尝试转义反斜杠字符,但在此之前,创建菜单项图像的 win gui 功能不起作用。他提供的示例代码here 无法正常工作,因为在混合 win32gui 类和句柄时存在一些问题。
【解决方案3】:

将第167行代码更改为item, extras = win32gui_struct.PackMENUITEMINFO(text=title,hbmpItem=5,wID=wID),然后你会发现一个关闭图标。

但是5option_icon构造的MENUITEMINFOs没有区别。

类型不匹配是我能想象的唯一原因。 option_icon 的类型是 hgdiObjdectMENUITEMINFO.hbmpItem 需要 HBITMAP。应该有演员表。

很奇怪,我觉得hbmpitem不是句柄,可以赋值为5,所以更像是内核中某个表的索引。如果是这样,那么类型应该无关紧要。

讨论handle问题:

您可以尝试MENUITEMINFO中预定义的所有数字,然后打印item,您会发现该数字刚刚传递到结构中。而句柄是某种指针,这个数字不是内存地址,所以它是某种索引。

prep_menu_icon 是普通 C++ 函数的 Python 版本,可将 hcion 转换为 hbitmap

python 版本缺少一些类型转换,并且它不起作用。但随后GetHandle 做了一些魔术。

【讨论】:

  • hbm 是使用CreateCompatibleBitmap 创建的位图句柄,MENUITEMINFO.hbmpItem 可以采用位图句柄和一些特殊值;检查MENUITEMINFO structure 以查看这些值。 hbmpItem 将转换为 int() 因此我认为没有任何类型不匹配的问题,请参阅 PackMENUITEMINFO hbmpItem struct pack。
  • 它不仅仅是某种索引,它要么是HANDLE,要么是Predefined Constant。所有句柄类型最终都是指向某个内存位置的 4 字节指针。我要说的是hbmpItem 接受位图句柄和一些预定义的常量;你可以看到,如果你去文档 hbmpItem Type: HBITMAP;要显示的位图的句柄,所以它并不奇怪,因为hbmpItem 甚至接受句柄。 prep_menu_iconis 不翻译任何东西;它创建一个新的位图并在该位图上绘制一个灰色矩形和图标图像;然后它返回句柄。
  • 感谢您耐心的解释。@ChristosLytras
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 2022-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多