* Replacement for Sendkeys that can handle Unicode characters and does not require any compilation
This commit is contained in:
parent
c29dd495e9
commit
cf4b41859a
685
pywinauto/SendKeysCtypes.py
Normal file
685
pywinauto/SendKeysCtypes.py
Normal file
@ -0,0 +1,685 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Check that SendInput can work the way we want it to
|
||||
|
||||
The tips and tricks at http://www.pinvoke.net/default.aspx/user32.sendinput
|
||||
is useful!
|
||||
|
||||
"""
|
||||
import time
|
||||
import ctypes
|
||||
|
||||
__all__ = ['KeySequenceError', 'SendKeys']
|
||||
|
||||
#pylint: disable-msg=R0903
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW
|
||||
SendInput = ctypes.windll.user32.SendInput
|
||||
VkKeyScan = ctypes.windll.user32.VkKeyScanW
|
||||
VkKeyScan.restype = ctypes.c_short
|
||||
VkKeyScan.argtypes = [ctypes.c_wchar]
|
||||
|
||||
DWORD = ctypes.c_ulong
|
||||
LONG = ctypes.c_long
|
||||
WORD = ctypes.c_ushort
|
||||
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4283
|
||||
class MOUSEINPUT(ctypes.Structure):
|
||||
"Needed for complete definition of INPUT structure - not used"
|
||||
_pack_ = 2
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4283
|
||||
('dx', LONG),
|
||||
('dy', LONG),
|
||||
('mouseData', DWORD),
|
||||
('dwFlags', DWORD),
|
||||
('time', DWORD),
|
||||
('dwExtraInfo', DWORD),
|
||||
]
|
||||
assert ctypes.sizeof(MOUSEINPUT) == 24, ctypes.sizeof(MOUSEINPUT)
|
||||
assert ctypes.alignment(MOUSEINPUT) == 2, ctypes.alignment(MOUSEINPUT)
|
||||
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4292
|
||||
class KEYBDINPUT(ctypes.Structure):
|
||||
"A particular keyboard event"
|
||||
_pack_ = 2
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4292
|
||||
('wVk', WORD),
|
||||
('wScan', WORD),
|
||||
('dwFlags', DWORD),
|
||||
('time', DWORD),
|
||||
('dwExtraInfo', DWORD),
|
||||
]
|
||||
assert ctypes.sizeof(KEYBDINPUT) == 16, ctypes.sizeof(KEYBDINPUT)
|
||||
assert ctypes.alignment(KEYBDINPUT) == 2, ctypes.alignment(KEYBDINPUT)
|
||||
|
||||
|
||||
class HARDWAREINPUT(ctypes.Structure):
|
||||
"Needed for complete definition of INPUT structure - not used"
|
||||
_pack_ = 2
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4300
|
||||
('uMsg', DWORD),
|
||||
('wParamL', WORD),
|
||||
('wParamH', WORD),
|
||||
]
|
||||
assert ctypes.sizeof(HARDWAREINPUT) == 8, ctypes.sizeof(HARDWAREINPUT)
|
||||
assert ctypes.alignment(HARDWAREINPUT) == 2, ctypes.alignment(HARDWAREINPUT)
|
||||
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4314
|
||||
class UNION_INPUT_STRUCTS(ctypes.Union):
|
||||
"The C Union type representing a single Event of any type"
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4314
|
||||
('mi', MOUSEINPUT),
|
||||
('ki', KEYBDINPUT),
|
||||
('hi', HARDWAREINPUT),
|
||||
]
|
||||
assert ctypes.sizeof(UNION_INPUT_STRUCTS) == 24, \
|
||||
ctypes.sizeof(UNION_INPUT_STRUCTS)
|
||||
assert ctypes.alignment(UNION_INPUT_STRUCTS) == 2, \
|
||||
ctypes.alignment(UNION_INPUT_STRUCTS)
|
||||
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4310
|
||||
class INPUT(ctypes.Structure):
|
||||
"See: http://msdn.microsoft.com/en-us/library/ms646270%28VS.85%29.aspx"
|
||||
_pack_ = 2
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4310
|
||||
('type', DWORD),
|
||||
# Unnamed field renamed to '_'
|
||||
('_', UNION_INPUT_STRUCTS),
|
||||
]
|
||||
assert ctypes.sizeof(INPUT) == 28, ctypes.sizeof(INPUT)
|
||||
assert ctypes.alignment(INPUT) == 2, ctypes.alignment(INPUT)
|
||||
|
||||
|
||||
INPUT_KEYBOARD = 1
|
||||
KEYEVENTF_EXTENDEDKEY = 1
|
||||
KEYEVENTF_KEYUP = 2
|
||||
KEYEVENTF_UNICODE = 4
|
||||
KEYEVENTF_SCANCODE = 8
|
||||
VK_SHIFT = 16
|
||||
VK_CONTROL = 17
|
||||
VK_MENU = 18
|
||||
|
||||
# 'codes' recognized as {CODE( repeat)?}
|
||||
CODES = {
|
||||
'BACK': 8,
|
||||
'BACKSPACE':8,
|
||||
'BKSP': 8,
|
||||
'BREAK': 3,
|
||||
'BS': 8,
|
||||
'CAP': 20,
|
||||
'CAPSLOCK': 20,
|
||||
'DEL': 46,
|
||||
'DELETE': 46,
|
||||
'DOWN': 40,
|
||||
'END': 35,
|
||||
'ENTER': 13,
|
||||
'ESC': 27,
|
||||
'F1': 112,
|
||||
'F2': 113,
|
||||
'F3': 114,
|
||||
'F4': 115,
|
||||
'F5': 116,
|
||||
'F6': 117,
|
||||
'F7': 118,
|
||||
'F8': 119,
|
||||
'F9': 120,
|
||||
'F10': 121,
|
||||
'F11': 122,
|
||||
'F12': 123,
|
||||
'F13': 124,
|
||||
'F14': 125,
|
||||
'F15': 126,
|
||||
'F16': 127,
|
||||
'F17': 128,
|
||||
'F18': 129,
|
||||
'F19': 130,
|
||||
'F20': 131,
|
||||
'F21': 132,
|
||||
'F22': 133,
|
||||
'F23': 134,
|
||||
'F24': 135,
|
||||
'HELP': 47,
|
||||
'HOME': 36,
|
||||
'INS': 45,
|
||||
'INSERT': 45,
|
||||
'LEFT': 37,
|
||||
'LWIN': 91,
|
||||
'NUMLOCK': 144,
|
||||
'PGDN': 34,
|
||||
'PGUP': 33,
|
||||
'PRTSC': 44,
|
||||
'RIGHT': 39,
|
||||
'RMENU': 165,
|
||||
'RWIN': 92,
|
||||
'SCROLLLOCK':145,
|
||||
'SPACE': 32,
|
||||
'TAB': 9,
|
||||
'UP': 38,
|
||||
|
||||
'VK_ACCEPT': 30,
|
||||
'VK_ADD': 107,
|
||||
'VK_APPS': 93,
|
||||
'VK_ATTN': 246,
|
||||
'VK_BACK': 8,
|
||||
'VK_CANCEL': 3,
|
||||
'VK_CAPITAL': 20,
|
||||
'VK_CLEAR': 12,
|
||||
'VK_CONTROL': 17,
|
||||
'VK_CONVERT': 28,
|
||||
'VK_CRSEL': 247,
|
||||
'VK_DECIMAL': 110,
|
||||
'VK_DELETE': 46,
|
||||
'VK_DIVIDE': 111,
|
||||
'VK_DOWN': 40,
|
||||
'VK_END': 35,
|
||||
'VK_EREOF': 249,
|
||||
'VK_ESCAPE': 27,
|
||||
'VK_EXECUTE': 43,
|
||||
'VK_EXSEL': 248,
|
||||
'VK_F1': 112,
|
||||
'VK_F2': 113,
|
||||
'VK_F3': 114,
|
||||
'VK_F4': 115,
|
||||
'VK_F5': 116,
|
||||
'VK_F6': 117,
|
||||
'VK_F7': 118,
|
||||
'VK_F8': 119,
|
||||
'VK_F9': 120,
|
||||
'VK_F10': 121,
|
||||
'VK_F11': 122,
|
||||
'VK_F12': 123,
|
||||
'VK_F13': 124,
|
||||
'VK_F14': 125,
|
||||
'VK_F15': 126,
|
||||
'VK_F16': 127,
|
||||
'VK_F17': 128,
|
||||
'VK_F18': 129,
|
||||
'VK_F19': 130,
|
||||
'VK_F20': 131,
|
||||
'VK_F21': 132,
|
||||
'VK_F22': 133,
|
||||
'VK_F23': 134,
|
||||
'VK_F24': 135,
|
||||
'VK_FINAL': 24,
|
||||
'VK_HANGEUL': 21,
|
||||
'VK_HANGUL': 21,
|
||||
'VK_HANJA': 25,
|
||||
'VK_HELP': 47,
|
||||
'VK_HOME': 36,
|
||||
'VK_INSERT': 45,
|
||||
'VK_JUNJA': 23,
|
||||
'VK_KANA': 21,
|
||||
'VK_KANJI': 25,
|
||||
'VK_LBUTTON': 1,
|
||||
'VK_LCONTROL':162,
|
||||
'VK_LEFT': 37,
|
||||
'VK_LMENU': 164,
|
||||
'VK_LSHIFT': 160,
|
||||
'VK_LWIN': 91,
|
||||
'VK_MBUTTON': 4,
|
||||
'VK_MENU': 18,
|
||||
'VK_MODECHANGE': 31,
|
||||
'VK_MULTIPLY': 106,
|
||||
'VK_NEXT': 34,
|
||||
'VK_NONAME': 252,
|
||||
'VK_NONCONVERT': 29,
|
||||
'VK_NUMLOCK': 144,
|
||||
'VK_NUMPAD0': 96,
|
||||
'VK_NUMPAD1': 97,
|
||||
'VK_NUMPAD2': 98,
|
||||
'VK_NUMPAD3': 99,
|
||||
'VK_NUMPAD4': 100,
|
||||
'VK_NUMPAD5': 101,
|
||||
'VK_NUMPAD6': 102,
|
||||
'VK_NUMPAD7': 103,
|
||||
'VK_NUMPAD8': 104,
|
||||
'VK_NUMPAD9': 105,
|
||||
'VK_OEM_CLEAR': 254,
|
||||
'VK_PA1': 253,
|
||||
'VK_PAUSE': 19,
|
||||
'VK_PLAY': 250,
|
||||
'VK_PRINT': 42,
|
||||
'VK_PRIOR': 33,
|
||||
'VK_PROCESSKEY': 229,
|
||||
'VK_RBUTTON': 2,
|
||||
'VK_RCONTROL': 163,
|
||||
'VK_RETURN': 13,
|
||||
'VK_RIGHT': 39,
|
||||
'VK_RMENU': 165,
|
||||
'VK_RSHIFT': 161,
|
||||
'VK_RWIN': 92,
|
||||
'VK_SCROLL': 145,
|
||||
'VK_SELECT': 41,
|
||||
'VK_SEPARATOR': 108,
|
||||
'VK_SHIFT': 16,
|
||||
'VK_SNAPSHOT': 44,
|
||||
'VK_SPACE': 32,
|
||||
'VK_SUBTRACT': 109,
|
||||
'VK_TAB': 9,
|
||||
'VK_UP': 38,
|
||||
'ZOOM': 251,
|
||||
}
|
||||
# reverse the CODES dict to make it easy to look up a particular code name
|
||||
CODE_NAMES = dict((entry[1], entry[0]) for entry in CODES.items())
|
||||
|
||||
# modifier keys
|
||||
MODIFIERS = {
|
||||
'+': VK_SHIFT,
|
||||
'^': VK_CONTROL,
|
||||
'%': VK_MENU,
|
||||
}
|
||||
|
||||
|
||||
class KeySequenceError(Exception):
|
||||
"""Exception raised when a key sequence string has a syntax error"""
|
||||
|
||||
def __str__(self):
|
||||
return ' '.join(self.args)
|
||||
|
||||
|
||||
class KeyAction(object):
|
||||
"""Class that represents a single 'keyboard' action
|
||||
|
||||
It represents either a PAUSE action (not reallly keyboard) or a keyboard
|
||||
action (press or release or both) of a particular key.
|
||||
"""
|
||||
|
||||
def __init__(self, key, down = True, up = True):
|
||||
self.key = key
|
||||
if isinstance(self.key, basestring):
|
||||
self.key = unicode(key)
|
||||
self.down = down
|
||||
self.up = up
|
||||
|
||||
def _get_key_info(self):
|
||||
"""Return virtual_key, scan_code, and flags for the action
|
||||
|
||||
This is one of the methods that will be overridden by sub classes"""
|
||||
return 0, ord(self.key), KEYEVENTF_UNICODE
|
||||
|
||||
def GetInput(self):
|
||||
"Build the INPUT structure for the action"
|
||||
actions = 1
|
||||
# if both up and down
|
||||
if self.up and self.down:
|
||||
actions = 2
|
||||
|
||||
inputs = (INPUT * actions)()
|
||||
|
||||
vk, scan, flags = self._get_key_info()
|
||||
|
||||
for inp in inputs:
|
||||
inp.type = INPUT_KEYBOARD
|
||||
|
||||
inp._.ki.wVk = vk
|
||||
inp._.ki.wScan = scan
|
||||
inp._.ki.dwFlags |= flags
|
||||
|
||||
# if we are releasing - then let it up
|
||||
if self.up:
|
||||
inputs[-1]._.ki.dwFlags |= KEYEVENTF_KEYUP
|
||||
|
||||
return inputs
|
||||
|
||||
def Run(self):
|
||||
"Execute the action"
|
||||
inputs = self.GetInput()
|
||||
return SendInput(
|
||||
len(inputs),
|
||||
ctypes.byref(inputs),
|
||||
ctypes.sizeof(INPUT))
|
||||
|
||||
def _get_down_up_string(self):
|
||||
"""Return a string that will show whether the string is up or down
|
||||
|
||||
return 'down' if the key is a press only
|
||||
return 'up' if the key is up only
|
||||
return '' if the key is up & down (as default)
|
||||
"""
|
||||
down_up = ""
|
||||
if not (self.down and self.up):
|
||||
if self.down:
|
||||
down_up = "down"
|
||||
elif self.up:
|
||||
down_up = "up"
|
||||
return down_up
|
||||
|
||||
def key_description(self):
|
||||
"Return a description of the key"
|
||||
vk, scan, flags = self._get_key_info()
|
||||
desc = ''
|
||||
if vk:
|
||||
if vk in CODE_NAMES:
|
||||
desc = CODE_NAMES[vk]
|
||||
else:
|
||||
desc = "VK %d"% vk
|
||||
else:
|
||||
desc = "%s"% self.key
|
||||
|
||||
return desc
|
||||
|
||||
def __str__(self):
|
||||
parts = []
|
||||
parts.append(self.key_description())
|
||||
up_down = self._get_down_up_string()
|
||||
if up_down:
|
||||
parts.append(up_down)
|
||||
|
||||
return "<%s>"% (" ".join(parts))
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class VirtualKeyAction(KeyAction):
|
||||
"""Represents a virtual key action e.g. F9 DOWN, etc
|
||||
|
||||
Overrides necessary methods of KeyAction"""
|
||||
|
||||
def _get_key_info(self):
|
||||
"Virtual keys have extended flag set"
|
||||
|
||||
# copied more or less verbatim from
|
||||
# http://www.pinvoke.net/default.aspx/user32.sendinput
|
||||
if (
|
||||
(self.key >= 33 and self.key <= 46) or
|
||||
(self.key >= 91 and self.key <= 93) ):
|
||||
flags = KEYEVENTF_EXTENDEDKEY;
|
||||
else:
|
||||
flags = 0
|
||||
# This works for %{F4} - ALT + F4
|
||||
#return self.key, 0, 0
|
||||
|
||||
# this works for Tic Tac Toe i.e. +{RIGHT} SHIFT + RIGHT
|
||||
return self.key, MapVirtualKey(self.key, 0), flags
|
||||
|
||||
|
||||
class EscapedKeyAction(KeyAction):
|
||||
"""Represents an escaped key action e.g. F9 DOWN, etc
|
||||
|
||||
Overrides necessary methods of KeyAction"""
|
||||
|
||||
def _get_key_info(self):
|
||||
"""EscapedKeyAction doesn't send it as Unicode and the vk and
|
||||
scan code are generated differently"""
|
||||
vkey_scan = LoByte(VkKeyScan(self.key))
|
||||
|
||||
return (vkey_scan, MapVirtualKey(vkey_scan, 0), 0)
|
||||
|
||||
def key_description(self):
|
||||
"Return a description of the key"
|
||||
|
||||
return "KEsc %s"% self.key
|
||||
|
||||
|
||||
class PauseAction(KeyAction):
|
||||
"Represents a pause action"
|
||||
|
||||
def __init__(self, how_long):
|
||||
self.how_long = how_long
|
||||
|
||||
def Run(self):
|
||||
"Pause for the lenght of time specified"
|
||||
time.sleep(self.how_long)
|
||||
|
||||
def __str__(self):
|
||||
return "<PAUSE %1.2f>"% (self.how_long)
|
||||
__repr__ = __str__
|
||||
|
||||
#def GetInput(self):
|
||||
# print `self.key`
|
||||
# keys = KeyAction.GetInput(self)
|
||||
#
|
||||
# shift_state = HiByte(VkKeyScan(self.key))
|
||||
#
|
||||
# shift_down = shift_state & 0x100 # 1st bit
|
||||
# ctrl_down = shift_state & 0x80 # 2nd bit
|
||||
# alt_down = shift_state & 0x40 # 3rd bit
|
||||
#
|
||||
# print bin(shift_state), shift_down, ctrl_down, alt_down
|
||||
#
|
||||
# print keys
|
||||
# keys = [k for k in keys]
|
||||
#
|
||||
# modifiers = []
|
||||
# if shift_down:
|
||||
# keys[0:0] = VirtualKeyAction(VK_SHIFT, up = False).GetInput()
|
||||
# keys.append(VirtualKeyAction(VK_SHIFT, down = False).GetInput())
|
||||
# if ctrl_down:
|
||||
# keys[0:0] = VirtualKeyAction(VK_CONTROL, up = False).GetInput()
|
||||
# keys.append(VirtualKeyAction(VK_CONTROL, down = False).GetInput())
|
||||
# if alt_down:
|
||||
# keys[0:0] = VirtualKeyAction(VK_ALT, up = False).GetInput()
|
||||
# keys.append(VirtualKeyAction(VK_ALT, down = False).GetInput())
|
||||
#
|
||||
# print keys
|
||||
# new_keys = (INPUT * len(keys)) ()
|
||||
#
|
||||
# for i, k in enumerate(keys):
|
||||
# if hasattr(k, 'type'):
|
||||
# new_keys[i] = k
|
||||
# else:
|
||||
# for sub_key in k:
|
||||
# new_keys[i] = sub_key
|
||||
#
|
||||
# return new_keys
|
||||
#
|
||||
|
||||
def handle_code(code):
|
||||
"Handle a key or sequence of keys in braces"
|
||||
|
||||
code_keys = []
|
||||
# it is a known code (e.g. {DOWN}, {ENTER}, etc)
|
||||
if code in CODES:
|
||||
code_keys.append(VirtualKeyAction(CODES[code]))
|
||||
|
||||
# it is an escaped modifier e.g. {%}, {^}, {+}
|
||||
elif len(code) == 1:
|
||||
code_keys.append(KeyAction(code))
|
||||
|
||||
# it is a repetition or a pause {DOWN 5}, {PAUSE 1.3}
|
||||
elif ' ' in code:
|
||||
to_repeat, count = code.rsplit(None, 1)
|
||||
if to_repeat == "PAUSE":
|
||||
try:
|
||||
pause_time = float(count)
|
||||
except ValueError:
|
||||
raise KeySequenceError('invalid pause time %s'% count)
|
||||
code_keys.append(PauseAction(pause_time))
|
||||
|
||||
else:
|
||||
try:
|
||||
count = int(count)
|
||||
except ValueError:
|
||||
raise KeySequenceError(
|
||||
'invalid repetition count %s'% count)
|
||||
|
||||
# If the value in to_repeat is a VK e.g. DOWN
|
||||
# we need to add the code repeated
|
||||
if to_repeat in CODES:
|
||||
code_keys.extend(
|
||||
[VirtualKeyAction(CODES[to_repeat])] * count)
|
||||
# otherwise parse the keys and we get back a KeyAction
|
||||
else:
|
||||
to_repeat = parse_keys(to_repeat)
|
||||
if isinstance(to_repeat, list):
|
||||
keys = to_repeat * count
|
||||
else:
|
||||
keys = [to_repeat] * count
|
||||
code_keys.extend(keys)
|
||||
else:
|
||||
raise RuntimeError("Unknown code: %s"% code)
|
||||
|
||||
return code_keys
|
||||
|
||||
|
||||
def parse_keys(string,
|
||||
with_spaces = False,
|
||||
with_tabs = False,
|
||||
with_newlines = False,
|
||||
modifiers = None):
|
||||
"Return the parsed keys"
|
||||
|
||||
keys = []
|
||||
if not modifiers:
|
||||
modifiers = []
|
||||
index = 0
|
||||
while index < len(string):
|
||||
|
||||
c = string[index]
|
||||
index += 1
|
||||
|
||||
# check if one of CTRL, SHIFT, ALT has been pressed
|
||||
if c in MODIFIERS.keys():
|
||||
modifier = MODIFIERS[c]
|
||||
# remember that we are currently modified
|
||||
modifiers.append(modifier)
|
||||
# hold down the modifier key
|
||||
keys.append(VirtualKeyAction(modifier, up = False))
|
||||
if DEBUG:
|
||||
print("MODS+", modifiers)
|
||||
continue
|
||||
|
||||
# Apply modifiers over a bunch of characters (not just one!)
|
||||
elif c == "(":
|
||||
# find the end of the bracketed text
|
||||
end_pos = string.find(")", index)
|
||||
if end_pos == -1:
|
||||
raise KeySequenceError('`)` not found')
|
||||
keys.extend(
|
||||
parse_keys(string[index:end_pos], modifiers = modifiers))
|
||||
index = end_pos + 1
|
||||
|
||||
# Escape or named key
|
||||
elif c == "{":
|
||||
end_pos = string.find("}", index)
|
||||
if end_pos == -1:
|
||||
raise KeySequenceError('`}` not found')
|
||||
|
||||
code = string[index:end_pos]
|
||||
index = end_pos + 1
|
||||
keys.extend(handle_code(code))
|
||||
|
||||
# unmatched ")"
|
||||
elif c == ')':
|
||||
raise KeySequenceError('`)` should be preceeded by `(`')
|
||||
|
||||
# unmatched "}"
|
||||
elif c == '}':
|
||||
raise KeySequenceError('`}` should be preceeded by `{`')
|
||||
|
||||
# so it is a normal character
|
||||
else:
|
||||
# don't output white space unless flags to output have been set
|
||||
if (c == ' ' and not with_spaces or
|
||||
c == '\t' and not with_tabs or
|
||||
c == '\n' and not with_newlines):
|
||||
continue
|
||||
|
||||
# output nuewline
|
||||
if c in ('~'):
|
||||
keys.append(KeyAction("\n"))
|
||||
else:
|
||||
if modifiers:
|
||||
keys.append(EscapedKeyAction(c))
|
||||
else:
|
||||
keys.append(KeyAction(c))
|
||||
|
||||
# as we have handled the text - release the modifiers
|
||||
while modifiers:
|
||||
if DEBUG:
|
||||
print("MODS-", modifiers)
|
||||
keys.append(VirtualKeyAction(modifiers.pop(), down = False))
|
||||
|
||||
# just in case there were any modifiers left pressed - release them
|
||||
while modifiers:
|
||||
keys.append(VirtualKeyAction(modifiers.pop(), down = False))
|
||||
|
||||
return keys
|
||||
|
||||
def LoByte(val):
|
||||
"Return the low byte of the value"
|
||||
return val & 0xff
|
||||
|
||||
def HiByte(val):
|
||||
"Return the high byte of the value"
|
||||
return (val & 0xff00) >> 8
|
||||
|
||||
def SendKeys(keys,
|
||||
pause=0.05,
|
||||
with_spaces=False,
|
||||
with_tabs=False,
|
||||
with_newlines=False,
|
||||
turn_off_numlock=True):
|
||||
"Parse the keys and type them"
|
||||
keys = parse_keys(keys, with_spaces, with_tabs, with_newlines)
|
||||
|
||||
for k in keys:
|
||||
k.Run()
|
||||
time.sleep(pause)
|
||||
|
||||
|
||||
def main():
|
||||
"Send some test strings"
|
||||
|
||||
actions = """
|
||||
{LWIN}
|
||||
{PAUSE .25}
|
||||
r
|
||||
{PAUSE .25}
|
||||
Notepad.exe{ENTER}
|
||||
{PAUSE 1}
|
||||
Hello{SPACE}World!
|
||||
{PAUSE 1}
|
||||
%{F4}
|
||||
{PAUSE .25}
|
||||
n
|
||||
"""
|
||||
SendKeys(actions, pause = .1)
|
||||
|
||||
keys = parse_keys(actions)
|
||||
for k in keys:
|
||||
print(k)
|
||||
k.Run()
|
||||
time.sleep(.1)
|
||||
|
||||
test_strings = [
|
||||
"\n"
|
||||
"(aa)some text\n",
|
||||
"(a)some{ }text\n",
|
||||
"(b)some{{}text\n",
|
||||
"(c)some{+}text\n",
|
||||
"(d)so%me{ab 4}text",
|
||||
"(e)so%me{LEFT 4}text",
|
||||
"(f)so%me{ENTER 4}text",
|
||||
"(g)so%me{^aa 4}text",
|
||||
"(h)some +(asdf)text",
|
||||
"(i)some %^+(asdf)text",
|
||||
"(j)some %^+a text+",
|
||||
"(k)some %^+a tex+{&}",
|
||||
"(l)some %^+a tex+(dsf)",
|
||||
"",
|
||||
]
|
||||
|
||||
for s in test_strings:
|
||||
print(repr(s))
|
||||
keys = parse_keys(s, with_newlines = True)
|
||||
print(keys)
|
||||
|
||||
for k in keys:
|
||||
k.Run()
|
||||
time.sleep(.1)
|
||||
print()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
main()
|
Loading…
Reference in New Issue
Block a user