Swatting Some Odd Bugs
One day, after WindowMover had been running successfully, an urgent call informed me that WindowMover had gone crazy. All the windows, including the Task Manager, were flickering all over the place. The experimenters couldn't even shut down WindowMover itself. To make matters worse I had set up WindowMover to auto-start, so even restarting the computer didn't help. After several reboots they finally managed to click on the WindowMover close button and shut the program down before it could begin its damage.
I couldn't figure out what could make WindowMover behave like that. Even if there were some error in the code, it should have perhaps just moved a few windows it wasn't supposed to move, or kept some unnecessary windows on the primary display. But eventually, I realized what happened. You can rearrange the monitor position using Windows' display settings dialog (see Figure 3
and Figure 4
|Figure 3. Specifying Primary Monitor: You can specify which monitor is the primary monitor and which is the secondary monitor using Windows' display settings.||
|Figure 4. Specifying Secondary Monitor: Here, the secondary monitor is on the left, where WindowMover expected it to be.||
And someone had done exactly that—moved the primary display to the left-hand side—so WindowMover's expected horizontal coordinates were completely invalid. The primary display horizontal coordinates still ranged from 0 to monitor_width-1
, but, with the secondary monitor on the right, the secondary display now ranges from monitor_width
. See Figure 5
and Figure 6
for the before and after state as they relate to the horizontal coordinates.
|Figure 5. Primary Display on Right:The primary display's left co-ordinate is always 0 so the secondary display's right coordinate is -1 (immediately to the left of the primary display).||
|Figure 6. Primary Display on Left: Here, the primary display's right co-ordinate is 1279, so the secondary display's left coordinate is 1280 (immediately to the right of the primary display).||
Obviously, the algorithm is sensitive to the relative locations of the primary display and the secondary display. The change to the display settings meant that when WindowMover moved the windows, they ended up in the wrong places.
WindowMover has a very specific goal, but some of its code can be useful in a more general context for other UI automation tasks. In addition, while WindowMover operates solely on the main desktop, Windows actually has multiple desktops. For example, the Windows login screen runs on a different desktop. Unfortunately WindowMover can't run at login time. That's important because for the eye-tracker project the primary display should eventually be located in a separate room from the secondary display. The keyboard would be with the secondary display. That arrangement means that if the login window pops up on the primary display there is no easy way for experimenters to log in; they would have to log in blindly from the secondary display, or send someone to the room with the primary display and coordinate over the phone. The correct solution is to make the login window show up on the secondary display, but doing that requires making WindowMover a windows service. Windows services can be executed very early and can run without a logged-in user.
As an example of how code from WindowMover can serve as the basis of other UI automation tasks, I used the main loop logic that checks all the top-level windows in another mini-project, called "clicker." The intent behind clicker was to automatically dispose of the error dialog boxes that showed up during test-program runs. The errors were logged, but the modal dialog boxes prevented the test from continuing. Clicker took care of the problem by detecting these specific dialog boxes, determining the appropriate button, and then clicking it programmatically. Your imagination is the limit. The win32gui and other Win32 extension modules provide excellent coverage of the Win32 API, and Python makes it easy to experiment interactively.