Search code examples
pythonwindowswin32guisystray

Add image in window tray menu


I am writing simple tray for windows using python.

I succeeded in creating a tray icon, menu, sub menu. I stucked at adding image for particular tray item.

here is code I used. (Link) Even this code did not work. Windows documentation is not clear.

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

Can someone help me.

Edit

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

Attaching image. In small box beside "Open" I want image to come. enter image description here


Solution

  • There are issues with the handles against types that may not result in errors.

    I got this working by using the win32ui classes like PyCDC and PyCBitMap instead of handles.

    Try to change prep_menu_icon to this:

    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()
    

    And I get the menu item icons:

    Popup menu with icons