ASPN ActiveState Programmer Network
ActiveState
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups


Recent Messages
List Archives
About the List
List Leaders
Subscription Options

View Subscriptions
Help

View by Topic
ActiveState
.NET Framework
Open Source
Perl
PHP
Python
Tcl
Web Services
XML & XSLT

View by Category
Database
General
SOAP
System Administration
Tools
User Interfaces
Web Programming
XML Programming


MyASPN >> Mail Archive >> wxpython-users
wxpython-users
[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

Privacy Policy | Email Opt-out | Feedback | Syndication
© ActiveState Software Inc. All rights reserved