Durgaprasad
Durgaprasad

Reputation: 1951

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

Upvotes: 19

Views: 1019

Answers (3)

obgnaw
obgnaw

Reputation: 3037

Change the code line 167 to item, extras = win32gui_struct.PackMENUITEMINFO(text=title,hbmpItem=5,wID=wID), then you will find a close icon.

But there is no different between the MENUITEMINFOs constructed by 5 and option_icon.

Type mismatch is the only reason I can imagine. The type of option_icon is hgdiObjdect, and the MENUITEMINFO.hbmpItem require a HBITMAP. There should be a cast.

It's weird, I don't think hbmpitem is a handle, it can be assign to 5, so it's more like an index of some table in the kernel. If that so the type shouldn't matter.

Discuss about the handle problem:

You can try all the number predefined in the MENUITEMINFO, then print the item, you will find the number just pass into the struct. And a handle is some kind of pointer, this number is not a memory address, so it's some kind of index.

The prep_menu_icon is a python version of a normal C++ function which translate the hcion to hbitmap.

The python version lack some type cast, and it doesn't work. But then GetHandle does some magic.

Upvotes: 2

Christos Lytras
Christos Lytras

Reputation: 37318

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

Upvotes: 9

Matthew
Matthew

Reputation: 7590

I can't seem to get the package set up on my computer, so can't really test this, but this line

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

gives me some concern. I'm not sure if you are reading the file that you think you are.

That \ is going to indicate an escape sequence. On Windows, you need to double those backslashes to prevent them from being escaped like "\\print_pref.ico". If you are trying to load a file in the current directory, you may not need that at all and can just give the file name - "print_pref.ico". If you are trying to locate a file in the drive's root directory, you need to give the drive letter "C:\\print_pref.ico".

Upvotes: 3

Related Questions