Reputation: 1951
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.
Upvotes: 19
Views: 1019
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 MENUITEMINFO
s 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.
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
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:
Upvotes: 9
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