RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Gain Control of Your Desktop with WindowMover : Page 3

WindowMover demonstrates techniques that let you take ultimate control of your desktop. While it focuses on managing window positions for dual monitor systems, you can easily borrow from or extend it for general-purpose UI automation.

The ThreadMonitor Class
The real work takes place in the ThreadMonitor class, which is a subclass of threading.Thread from Python's standard threading library module. It exposes the mandatory run() method that runs in a separate thread. Here's the code:

   class MonitorThread(threading.Thread):
     def __init__(self, verbose=False, targets=None):
       self.targets = targets
       if self.targets == None:
         self.targets = default_targets
       self.verbose = verbose
       self.done = threading.Event()
The __init__ method takes two arguments (the 'self' argument is an implicit one like the 'this' pointer in C++, and is not considered an argument): verbose and targets, set to appropriate defaults, and keeps them for later reference. The verbose argument determines how verbose the monitor's output should be. The targets argument is the list of windows to monitor; if it's the default None, the method uses the default_targets module variable). The most important job of the __init__ method is to fire the done event. This synchronization object allows the main thread to stop the monitor thread (used only when running for a specific number of seconds).

The run() and stop() methods
These methods control the lifetime of the worker thread. The run() method calls the _checkTargets() method repeatedly as long as the done event is not set, and sleeps between each iteration so it doesn't monopolize machine resources. This method launches on a separate thread when the main thread calls start().

   def run(self):
       while not self.done.isSet():
       print 'Done'
The stop() method sets the done event that the run() method checks. The main thread calls it to stop the thread's operation cleanly.

   def stop(self):
The _moveWindow() Method
Finally, with all the framework code in place, here's how the program actually moves a window:

   def _moveWindow(self, hwnd, dx=-monitor_width, dy=height_difference) :   
       r = GetWindowRect(hwnd)
       # calculate new position
       x =  r[0] + dx 
       y =  r[1] + dy  
       # calculate width/height
      width  = r[2]-r[0]
       height = r[3]-r[1]
      MoveWindow(hwnd, x, y, width, height, True)  
As you saw earlier, the _checkTargets() method calls _moveWindow() when it needs to move a window. The method accepts the handle (hwnd) of the window to be moved and two values that determine how much to offset the window horizontally (dx) and vertically (dy). The default for dx is -monitor_width, which means move it from the right display to the equivalent location in the left display. The default for dy is height_difference, which moves the window vertically as needed in the secondary display if a height difference exists between the displays.

The method starts by calling the GetWindowRect() API function, which returns a tuple of the form (left, top, right, bottom). It calculates the new position by adding dx and dy to the left and top coordinates and then calculates the width and height, which remain the same. Finally, it actually moves the window by calling the MoveWindow() API function. Note the different coordinate systems (top, left, right, bottom) and (top, left, width, height). It's easy to become confused (which produces interesting bugs) by passing right and bottom instead of width and height.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date