March 20, 2003
Driving win32 GUIs with Python, part 2

Finding the target control

See Driving win32 GUIs with Python, part 1

So, we have our list of top level windows. You'll need to choose which one to burrow into. Now, the window's text may be all you need to make this decision. Often it is. But another useful criterion can be the window's class. Whether or not you need to know a window's class now, it will certainly come in handy later.

Windows supplies a GetClassName() API, but unfortunately Mark Hammond's win32 extensions don't wrap this. So, we'll use Thomas Heller's ctypes module, instead. I'm using version 0.4.

A couple of warnings here. Once you start using stuff like ctypes and the win32 extensions, you can crash Python, hard. I mean really, really, time to re-boot, hard. After all, you are basically poking the operating system with a stick, as my colleague Dan memorably put it. This will come as no surprise to C and C++ users, but Python usually protects you from this sort of thing. Secondly, I lifted the code for class name retrieval from c.l.py, and I basically have no idea how it works. ;-)

Anyway, here is how we get a window's class name:

import ctypes

def getClassName(hwnd):
resultString = ctypes.c_string("\000" * 32)
ctypes.windll.user32.GetClassNameA(hwnd, resultString, len(resultString))
return resultString.value

We'll alter our window enumeration callback thing to get use this:

def windowEnumerationHandler(hwnd, resultList):
'''Pass to win32gui.EnumWindows() to generate list of window handle, window text, window class tuples.'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd), getClassName(hwnd)))

Now we'll get the class name in our list of windows, too.

This is the function that I use to find the top level window that I want. You may want something a little different - you may want an exact text match rather than a starts-with match, for example - but I'll leave the details to you.

def findTopWindow(wantedText=None, wantedClass=None):
topWindows = []
win32gui.EnumWindows(windowEnumerationHandler, topWindows)
for hwnd, windowText, windowClass in topWindows:
if wantedText and not windowText.startswith(wantedText):
continue
if wantedClass and not windowClass == wantedClass:
continue
return hwnd

OK, so, you can do something like findTopWindow(wantedText="ACE") to get the window handle of the required to window. Once you have this, you can burrow into its child windows using the win32gui.EnumChildWindows() function. This works pretty much the same as the win32gui.EnumWindows(), with an additional argument specifying which window's children to process. Also, for reasons obscure to me, it occasionally throws a win32gui.error exception. I think that this happens if the window is unable to have child windows (as opposed to just not actually having any, when you just get an empty list), but that's pretty much a guess really. Since I never get this exception for windows that have children that I'm interested in, it's not a problem.

The other complication is that the child windows can have children of their own, so we need to be able to to recurse through the hierarchy to find the window we want.

Here's my function for locating a control. For maximum flexibility, it takes a selection function, which should be able to spot when the required control has been found.

def findControl(topHwnd, selectionFunction):
def searchChildWindows(currentHwnd):
childWindows = []
try:
win32gui.EnumChildWindows(currentHwnd, windowEnumerationHandler, childWindows)
except win32gui.error, exception:
# This seems to mean that the control does *cannot* have child windows
return
for childHwnd, windowText, windowClass in childWindows:
# print "Found ", childHwnd, windowText, windowClass
if selectionFunction(childHwnd, windowText, windowClass):
return childHwnd
else:
descendentMatchingHwnd = searchChildWindows(childHwnd)
if descendentMatchingHwnd:
return descendentMatchingHwnd
return

return searchChildWindows(topHwnd)



And here we use it:

optDialog = findTopWindow(wantedText="Options")
print optDialog
def findAButtonCalledOK(hwnd, windowText, windowClass):
return windowClass == "Button" and windowText == "OK"
okButton = findControl(optDialog, findAButtonCalledOK)

There - so now we can find the control we want to do something with. Next, I'll actually do something to it...

Update: See the whole series: Driving win32 GUIs with Python, part 1, Driving win32 GUIs with Python, part 2, Driving win32 GUIs with Python, part 3, Driving win32 GUIs with Python, part 4 and 7 hours, one line of code.

Posted to Python by Simon Brunning at March 20, 2003 05:03 PM
Comments

Cool! Keep writing - these are very helpful.

Posted by: Babu on March 20, 2003 08:53 PM

To find window handle you could also use win32gui.FindWindow(className, WindowName)

Posted by: Janno on August 23, 2003 06:22 PM

win32gui has GetClassName() now -- I checked the CVS logs and it looks like it was added 2003-04-07.

Posted by: Commenter on September 24, 2003 07:37 AM

Ah, I see that the win32 extensions author himself commented on the addition of GetClassName in
'Driving win32 GUIs with Python, part 4'...

Posted by: Commenter on September 24, 2003 07:43 AM

ctypes.c_string has been removed for .6x versions. You'll need to replace c_string with c_buffer to get the above to work.

Posted by: Mr. Fixit on October 22, 2003 01:55 PM

Very usefull article. However shouldn't the code in "windowEnumerationHandler"

resultList.append((hwnd, win32gui.GetWindowText(hwnd), getClassName(hwnd)))

be :-

resultList.append((hwnd, win32gui.GetWindowText(hwnd), win32gui.GetClassName(hwnd)))

Posted by: Shamsul Azhar on October 29, 2003 08:39 AM

Very exciting - cant stop reading.
Greetings to Dan - he made me laugh:

After all, you are basically poking the operating system with a stick...

Posted by: gardsted on November 9, 2005 07:14 PM

mighty unendingness inflammableness cue implicitness ulonata watchword ravinate
qpreccw dznpgx
http://vsswoxw.com
ebodmi lkkkze
http://lngnupg.com
cdkwamg durtng
http://apzgup.com
avtpzl xqerof
http://ckugviqbbtjs.com

Posted by: Erin Barron on September 1, 2008 01:11 PM

mighty unendingness inflammableness cue implicitness ulonata watchword ravinate
iitwv uedi
http://qbdrnz.com
ksyxws yzdsne
http://dcrdhzve.com
cvyicma jkcby
http://kwetichiygs.com
ggonue picjlu
http://sjviqitwpz.com

Posted by: Oliver Larson on September 2, 2008 04:35 PM

mighty unendingness inflammableness cue implicitness ulonata watchword ravinate
addlj rjcwesgs
http://yotyierdknpi.com
ejikno gzyzkim
http://mnsmyme.com
gnelo visb
http://zyznckmzxm.com
wzrfo amwo
http://hafxhpdwlh.com

Posted by: Margaret Best on September 2, 2008 08:27 PM

mighty unendingness inflammableness cue implicitness ulonata watchword ravinate
xtwgf yyelgtgc
http://zkrbxsulbo.com
lobqay vfjkho
http://nrgtbutvnjnz.com
pcxtofq xxcpub
http://jnncwhaoyk.com
bpxhjru vqpx
http://crgofy.com

Posted by: Rolando Rasmussen on September 4, 2008 02:20 AM

mighty unendingness inflammableness cue implicitness ulonata watchword ravinate
odpwcon nkgmot
http://lvpoqe.com
vzxqhwq cqtzhd
http://drydkamcy.com
gkzil yrtpfp
http://lpwvsuves.com
brkqw kzjnsd
http://nubjcszq.com

Posted by: Kristi Wilkinson on September 4, 2008 09:14 AM
Post a comment
Name:


Email Address:


URL:



Comments:


Remember info?