1#!/usr/bin/env python 2# Copyright (c) 2011 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""SiteCompare module for simulating mouse input. 7 8This module contains functions that can be used to simulate a user 9navigating using a pointing device. This includes mouse movement, 10clicking with any button, and dragging. 11""" 12 13import time # for sleep 14 15import win32api # for mouse_event 16import win32con # Windows constants 17import win32gui # for window functions 18 19 20def ScreenToMouse(pt): 21 """Convert a value in screen coordinates to mouse coordinates. 22 23 Mouse coordinates are specified as a percentage of screen dimensions, 24 normalized to 16 bits. 0 represents the far left/top of the screen, 25 65535 represents the far right/bottom. This function assumes that 26 the size of the screen is fixed at module load time and does not change 27 28 Args: 29 pt: the point of the coords to convert 30 31 Returns: 32 the converted point 33 """ 34 35 # Initialize the screen dimensions on first execution. Note that this 36 # function assumes that the screen dimensions do not change during run. 37 if not ScreenToMouse._SCREEN_DIMENSIONS: 38 desktop = win32gui.GetClientRect(win32gui.GetDesktopWindow()) 39 ScreenToMouse._SCREEN_DIMENSIONS = (desktop[2], desktop[3]) 40 41 return ((65535 * pt[0]) / ScreenToMouse._SCREEN_DIMENSIONS[0], 42 (65535 * pt[1]) / ScreenToMouse._SCREEN_DIMENSIONS[1]) 43 44ScreenToMouse._SCREEN_DIMENSIONS = None 45 46 47def PressButton(down, button='left'): 48 """Simulate a mouse button press or release at the current mouse location. 49 50 Args: 51 down: whether the button is pressed or released 52 button: which button is pressed 53 54 Returns: 55 None 56 """ 57 58 # Put the mouse_event flags in a convenient dictionary by button 59 flags = { 60 'left': (win32con.MOUSEEVENTF_LEFTUP, win32con.MOUSEEVENTF_LEFTDOWN), 61 'middle': (win32con.MOUSEEVENTF_MIDDLEUP, win32con.MOUSEEVENTF_MIDDLEDOWN), 62 'right': (win32con.MOUSEEVENTF_RIGHTUP, win32con.MOUSEEVENTF_RIGHTDOWN) 63 } 64 65 # hit the button 66 win32api.mouse_event(flags[button][down], 0, 0) 67 68 69def ClickButton(button='left', click_time=0): 70 """Press and release a mouse button at the current mouse location. 71 72 Args: 73 button: which button to click 74 click_time: duration between press and release 75 76 Returns: 77 None 78 """ 79 PressButton(True, button) 80 time.sleep(click_time) 81 PressButton(False, button) 82 83 84def DoubleClickButton(button='left', click_time=0, time_between_clicks=0): 85 """Double-click a mouse button at the current mouse location. 86 87 Args: 88 button: which button to click 89 click_time: duration between press and release 90 time_between_clicks: time to pause between clicks 91 92 Returns: 93 None 94 """ 95 ClickButton(button, click_time) 96 time.sleep(time_between_clicks) 97 ClickButton(button, click_time) 98 99 100def MoveToLocation(pos, duration=0, tick=0.01): 101 """Move the mouse cursor to a specified location, taking the specified time. 102 103 Args: 104 pos: position (in screen coordinates) to move to 105 duration: amount of time the move should take 106 tick: amount of time between successive moves of the mouse 107 108 Returns: 109 None 110 """ 111 # calculate the number of moves to reach the destination 112 num_steps = (duration/tick)+1 113 114 # get the current and final mouse position in mouse coords 115 current_location = ScreenToMouse(win32gui.GetCursorPos()) 116 end_location = ScreenToMouse(pos) 117 118 # Calculate the step size 119 step_size = ((end_location[0]-current_location[0])/num_steps, 120 (end_location[1]-current_location[1])/num_steps) 121 step = 0 122 123 while step < num_steps: 124 # Move the mouse one step 125 current_location = (current_location[0]+step_size[0], 126 current_location[1]+step_size[1]) 127 128 # Coerce the coords to int to avoid a warning from pywin32 129 win32api.mouse_event( 130 win32con.MOUSEEVENTF_MOVE|win32con.MOUSEEVENTF_ABSOLUTE, 131 int(current_location[0]), int(current_location[1])) 132 133 step += 1 134 time.sleep(tick) 135 136 137def ClickAtLocation(pos, button='left', click_time=0): 138 """Simulate a mouse click in a particular location, in screen coordinates. 139 140 Args: 141 pos: position in screen coordinates (x,y) 142 button: which button to click 143 click_time: duration of the click 144 145 Returns: 146 None 147 """ 148 MoveToLocation(pos) 149 ClickButton(button, click_time) 150 151 152def ClickInWindow(hwnd, offset=None, button='left', click_time=0): 153 """Simulate a user mouse click in the center of a window. 154 155 Args: 156 hwnd: handle of the window to click in 157 offset: where to click, defaults to dead center 158 button: which button to click 159 click_time: duration of the click 160 161 Returns: 162 Nothing 163 """ 164 165 rect = win32gui.GetClientRect(hwnd) 166 if offset is None: offset = (rect[2]/2, rect[3]/2) 167 168 # get the screen coordinates of the window's center 169 pos = win32gui.ClientToScreen(hwnd, offset) 170 171 ClickAtLocation(pos, button, click_time) 172 173 174def DoubleClickInWindow( 175 hwnd, offset=None, button='left', click_time=0, time_between_clicks=0.1): 176 """Simulate a user mouse double click in the center of a window. 177 178 Args: 179 hwnd: handle of the window to click in 180 offset: where to click, defaults to dead center 181 button: which button to click 182 click_time: duration of the clicks 183 time_between_clicks: length of time to pause between clicks 184 185 Returns: 186 Nothing 187 """ 188 ClickInWindow(hwnd, offset, button, click_time) 189 time.sleep(time_between_clicks) 190 ClickInWindow(hwnd, offset, button, click_time) 191 192 193def main(): 194 # We're being invoked rather than imported. Let's do some tests 195 196 screen_size = win32gui.GetClientRect(win32gui.GetDesktopWindow()) 197 screen_size = (screen_size[2], screen_size[3]) 198 199 # move the mouse (instantly) to the upper right corner 200 MoveToLocation((screen_size[0], 0)) 201 202 # move the mouse (over five seconds) to the lower left corner 203 MoveToLocation((0, screen_size[1]), 5) 204 205 # click the left mouse button. This will open up the Start menu 206 # if the taskbar is at the bottom 207 208 ClickButton() 209 210 # wait a bit, then click the right button to open the context menu 211 time.sleep(3) 212 ClickButton('right') 213 214 # move the mouse away and then click the left button to dismiss the 215 # context menu 216 MoveToLocation((screen_size[0]/2, screen_size[1]/2), 3) 217 MoveToLocation((0, 0), 3) 218 ClickButton() 219 220 221if __name__ == "__main__": 222 sys.exit(main()) 223