[wxpython-users] location (position) of tray icon in windows from python
by Michael Haimes other posts by this author
May 29 2009 9:33PM messages near this date
Re: [wxpython-users] Mouse events outside of frame
|
[wxpython-users] OpenGL and Mesa3D
I saw this question posted to the list a few times, and just wrote a
solution. There are certainly no API calls / sane way to do this in Windows,
but where there is a will there is a way.
The basic idea is, turn your icon black, scan the screen for a black square,
and then turn it back to your icon. In practice, this can be done quick
enough for the user to not notice. If the loops were in C, it would
definitely be too fast to notice ;) It would be nice to just hunt for the
icon itself, but unfortunately, various versions of windows (win2k
especially) play with the coloring too heavily for this method to be at all
reliable.
the ico file i use as the "black" icon while I'm searching can be downloaded
from here: http://dl.getdropbox.com/u/100516/blank1.ico
this implementation uses pywin32. If you are not using pywin32 and don't
want to add a dependency on it, it should be relatively easy to use ctypes
to make the win32 api calls. Note that YOU need to fill in the code that
sets/unsets the black icon!
I've used this successfully on win2k, xp, vista, and windows 7 (when the bar
is showing). If your icon isn't showing, it's not gonna work :) In that
case, you could fall back to using the rect of the whole notification area
(see the first line of the function get_screen_rect. note that win32's
GetWindowRect returns (left, top, right, bottom) NOT (left, top, width,
height)). The rect returned assumes your icon is 16x16, if the user has "big
icons" turned on, then you may have to detect that in a more fancy way!!
################
import win32gui
def get_notification_hwnd():
tray_window_class = 'Shell_TrayWnd'
tray_hwnd = win32gui.FindWindowEx(0, 0,
tray_window_class, "")
assert tray_hwnd, "Couldn't get tray hwnd"
notification_window_class = 'TrayNotifyWnd'
notification_hwnd = win32gui.FindWindowEx(tray_hwnd, 0,
notification_window_class, "")
return notification_hwnd
# This function tries its best to return a rect similar to the GetScreenRect
function from wx.Window
# the rect returned is (left, top, width, height)
# Returns None if there were problems, excepts if you don't have a
notification area at all
def get_screen_rect():
notification_rect = win32gui.GetWindowRect(get_notification_hwnd())
screendc = win32gui.GetDC(None)
# be lenient about what we call "black", windows messes with colors
sometimes
potential_blacks = set()
for r in xrange(4):
for g in xrange(4):
for b in xrange(4):
potential_blacks.add(r | (g << 8) | (b << 16))
!!! SET YOUR ICON TO BLACK.ICO HERE !!!
try:
the_rect = None
for y in xrange(notification_rect[3]-3, notification_rect[1],
-1):
num_consecutive_black = 0
for x in xrange(notification_rect[0], notification_rect[2],
1):
if all((win32gui.GetPixel(screendc,
x,
y+yoff) in potential_blacks)
for yoff in (-2, 0, 2)):
num_consecutive_black += 1
else:
num_consecutive_black = 0
if num_consecutive_black > 8: # p sure this is us
the_rect = (x-8, y-13, 16, 16)
break
if the_rect is not None:
break
return the_rect
finally:
!!! RESTORE YOUR ICON TO WHATEVER IT WAS HERE !!!
################
Note: this is a dirty, dirty hack. Don't blame me if it doesn't work. This
is /certainly/ not the "right" way to do it, but it just might be the /only/
way. :) There is an even more fun hack that works up to xp, involving
allocating memory in the notifcation process itself, but Vista's UIPI has
ended that as a possibility. WOOOO.
Happy to answer any questions you may have.
Hope this helps! Happy hacking,
Mike
|