1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
#!/usr/bin/python2.4
# Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""SiteCompare module for simulating keyboard input.
This module contains functions that can be used to simulate a user
pressing keys on a keyboard. Support is provided for formatted strings
including special characters to represent modifier keys like CTRL and ALT
"""
import time # for sleep
import win32api # for keybd_event and VkKeyCode
import win32con # Windows constants
# TODO(jhaas): Ask the readability guys if this would be acceptable:
#
# from win32con import VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, KEYEVENTF_KEYUP
#
# This is a violation of the style guide but having win32con. everywhere
# is just plain ugly, and win32con is a huge import for just a handful of
# constants
def PressKey(down, key):
"""Presses or unpresses a key.
Uses keybd_event to simulate either depressing or releasing
a key
Args:
down: Whether the key is to be pressed or released
key: Virtual key code of key to press or release
"""
# keybd_event injects key events at a very low level (it's the
# Windows API keyboard device drivers call) so this is a very
# reliable way of simulating user input
win32api.keybd_event(key, 0, (not down) * win32con.KEYEVENTF_KEYUP)
def TypeKey(key, keystroke_time=0):
"""Simulate a keypress of a virtual key.
Args:
key: which key to press
keystroke_time: length of time (in seconds) to "hold down" the key
Note that zero works just fine
Returns:
None
"""
# This just wraps a pair of PressKey calls with an intervening delay
PressKey(True, key)
time.sleep(keystroke_time)
PressKey(False, key)
def TypeString(string_to_type,
use_modifiers=False,
keystroke_time=0,
time_between_keystrokes=0):
"""Simulate typing a string on the keyboard.
Args:
string_to_type: the string to print
use_modifiers: specifies whether the following modifier characters
should be active:
{abc}: type characters with ALT held down
[abc]: type characters with CTRL held down
\ escapes {}[] and treats these values as literal
standard escape sequences are valid even if use_modifiers is false
\p is "pause" for one second, useful when driving menus
\1-\9 is F-key, \0 is F10
TODO(jhaas): support for explicit control of SHIFT, support for
nonprintable keys (F-keys, ESC, arrow keys, etc),
support for explicit control of left vs. right ALT or SHIFT,
support for Windows key
keystroke_time: length of time (in secondes) to "hold down" the key
time_between_keystrokes: length of time (seconds) to pause between keys
Returns:
None
"""
shift_held = win32api.GetAsyncKeyState(win32con.VK_SHIFT ) < 0
ctrl_held = win32api.GetAsyncKeyState(win32con.VK_CONTROL) < 0
alt_held = win32api.GetAsyncKeyState(win32con.VK_MENU ) < 0
next_escaped = False
escape_chars = {
'a': '\a', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', 'v': '\v'}
for char in string_to_type:
vk = None
handled = False
# Check to see if this is the start or end of a modified block (that is,
# {abc} for ALT-modified keys or [abc] for CTRL-modified keys
if use_modifiers and not next_escaped:
handled = True
if char == "{" and not alt_held:
alt_held = True
PressKey(True, win32con.VK_MENU)
elif char == "}" and alt_held:
alt_held = False
PressKey(False, win32con.VK_MENU)
elif char == "[" and not ctrl_held:
ctrl_held = True
PressKey(True, win32con.VK_CONTROL)
elif char == "]" and ctrl_held:
ctrl_held = False
PressKey(False, win32con.VK_CONTROL)
else:
handled = False
# If this is an explicitly-escaped character, replace it with the
# appropriate code
if next_escaped and char in escape_chars: char = escape_chars[char]
# If this is \p, pause for one second.
if next_escaped and char == 'p':
time.sleep(1)
next_escaped = False
handled = True
# If this is \(d), press F key
if next_escaped and char.isdigit():
fkey = int(char)
if not fkey: fkey = 10
next_escaped = False
vk = win32con.VK_F1 + fkey - 1
# If this is the backslash, the next character is escaped
if not next_escaped and char == "\\":
next_escaped = True
handled = True
# If we make it here, it's not a special character, or it's an
# escaped special character which should be treated as a literal
if not handled:
next_escaped = False
if not vk: vk = win32api.VkKeyScan(char)
# VkKeyScan() returns the scan code in the low byte. The upper
# byte specifies modifiers necessary to produce the given character
# from the given scan code. The only one we're concerned with at the
# moment is Shift. Determine the shift state and compare it to the
# current state... if it differs, press or release the shift key.
new_shift_held = bool(vk & (1<<8))
if new_shift_held != shift_held:
PressKey(new_shift_held, win32con.VK_SHIFT)
shift_held = new_shift_held
# Type the key with the specified length, then wait the specified delay
TypeKey(vk & 0xFF, keystroke_time)
time.sleep(time_between_keystrokes)
# Release the modifier keys, if held
if shift_held: PressKey(False, win32con.VK_SHIFT)
if ctrl_held: PressKey(False, win32con.VK_CONTROL)
if alt_held: PressKey(False, win32con.VK_MENU)
if __name__ == "__main__":
# We're being invoked rather than imported. Let's do some tests
# Press command-R to bring up the Run dialog
PressKey(True, win32con.VK_LWIN)
TypeKey(ord('R'))
PressKey(False, win32con.VK_LWIN)
# Wait a sec to make sure it comes up
time.sleep(1)
# Invoke Notepad through the Run dialog
TypeString("wordpad\n")
# Wait another sec, then start typing
time.sleep(1)
TypeString("This is a test of SiteCompare's Keyboard.py module.\n\n")
TypeString("There should be a blank line above and below this one.\n\n")
TypeString("This line has control characters to make "
"[b]boldface text[b] and [i]italic text[i] and normal text.\n\n",
use_modifiers=True)
TypeString(r"This line should be typed with a visible delay between "
"characters. When it ends, there should be a 3-second pause, "
"then the menu will select File/Exit, then another 3-second "
"pause, then No to exit without saving. Ready?\p\p\p{f}x\p\p\pn",
use_modifiers=True,
keystroke_time=0.05,
time_between_keystrokes=0.05)
|