Thursday, March 09, 2006

Setting the foreground window

Ever found it annoying when you start an application that takes a long time to start up , start working on other things and the application decides to popup and annoy you after/while it starts up?

Well you can now do the same with pywinauto :-)

I had tried to get this right before and hadn't succeeded - but I managed it today. HwndWrapper.SetFocus will now make the window the foreground window - even if it is not the current foreground window.

This was easy enough really - and I had thought that I had tried this already - but obviously hadn't :-(

Here is the new SetFocus method...

    def SetFocus(self):
"""Set the focus to this control

Activate the window if necessary"""

# find the current foreground window
cur_foreground = win32functions.GetForegroundWindow()

# if it is already foreground then just return
if self.handle != cur_foreground:

# get the thread of the window that is in the foreground
cur_fore_thread = win32functions.GetWindowThreadProcessId(
cur_foreground, 0)

# get the thread of the window that we want to be in the foreground
control_thread = win32functions.GetWindowThreadProcessId(self, 0)

# if a different thread owns the active window
if cur_fore_thread != control_thread:
# Attach the two threads and set the foreground window
win32functions.AttachThreadInput(
cur_fore_thread, control_thread, True)

win32functions.SetForegroundWindow(self)

# detach the thread again
win32functions.AttachThreadInput(
cur_fore_thread, control_thread, False)

else: # same threads - just set the foreground window
win32functions.SetForegroundWindow(self)

# make sure that we are idle before returning
win32functions.WaitGuiThreadIdle(self)

# only sleep if we had to change something!
time.sleep(.06)

return self


And here is a short bit of code you can test it with
from pywinauto.application import Application
import time

app = Application.start("Notepad.exe")
app.UntitledNotepad.MenuSelect("Format->Font")

for i in range(4):
time.sleep(2)
app.Font.Edit1.SetFocus()


If you run that code and work with other windows - then the Notepad Font dialog will popup (annoyingly :-) ) every 2 seconds.

All is left now is to add a call to this method in those other methods that REQUIRE the window to have focus (TypeKeys() for sure, maybe the ...Input() methods - but they probably require a WindowFromPoint/RealChildWindowFromPoint/ChildWindowFromPoint call first to ensure it's necessary).

(all happy with myself for getting that off the todo list - except it wasn't on the todo list :-D )

7 comments:

  1. Are you familiar with, or basing your work on, winguiauto?

    http://www.brunningonline.net/simon/blog/archives/winGuiAuto.py.html

    ReplyDelete
  2. Are you familiar with, or basing your work on, winguiauto?

    http://www.brunningonline.net/simon/blog/archives/winGuiAuto.py.html

    ReplyDelete
  3. Hi,

    Yup - I had looked at winguiauto and Watsup (which is based on winguiauto) but didn't like them too much. I did learn some stuff from them - but pywinauto is not based on them.

    Thinking about it recently I am considering a pywinauto-lite that might be a single file more usable for quick and dirty stuff (similar to winguiauto).

    pywinauto at the moment falls into a different category - it tries to do more in a more complete way (for example you shouldn't have to use time.sleep() yourself unless you have a very slow running operation.

    ReplyDelete
  4. Just found your algorithm which could be what I have been looking for.
    can you elabrate what this line mean?

    # only sleep if we had to change something! time.sleep(.06)

    I am trying to send a string text and that's the end of it. Do I need to way.
    Thanks

    ReplyDelete
  5. Great article,
    I just found this blog and looks like this is what I've been looking for.

    I have a question about the following line you said

    # only sleep if we had to change something! time.sleep(.06)

    I am sending a string text to the target window, do I need to Sleep().

    If I know the purpose of the Sleep, then I can determine on my own.

    Thanks.

    ReplyDelete
  6. The sleep is to allow the window enough time to come to the foreground. For example if you try to do something immediately wihtout the sleep - then it is just possible the window hasn't had a chance to make itself the foreground window yet.

    As the sleep command is within the block executed if the current foreground window is not the window you want to make foreground we only sleep if we had to change the foreground window (i.e. no sleep if we didn't have to do anything :-).

    Hope this answers your question.

    ReplyDelete
  7. FWIW you *must* check the return value of WaitGuiThreadIdle. Only return value 0 means that the GUI thread is idle, otherwise you either timed out waiting or got an error.

    ReplyDelete