You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Sources/pyOpenRPA/Resources/Web/pywinauto/controls/hwndwrapper.py

1823 lines
66 KiB

# GUI Application automation and testing library
# Copyright (C) 2006-2018 Mark Mc Mahon and Contributors
# https://github.com/pywinauto/pywinauto/graphs/contributors
# http://pywinauto.readthedocs.io/en/latest/credits.html
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of pywinauto nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Basic wrapping of Windows controls"""
from __future__ import unicode_literals
from __future__ import print_function
# pylint: disable-msg=W0611
# import sys
import copy
import time
import re
import ctypes
import win32api
import win32gui
import win32con
import win32process
import win32event
import six
import pywintypes
import warnings
# the wrappers may be used in an environment that does not need
# the actions - as such I don't want to require sendkeys - so
# the following makes the import optional.
from .. import win32functions
from .. import win32defines
from .. import win32structures
from .. import controlproperties
from ..actionlogger import ActionLogger
from .. import keyboard
from .. import mouse
from ..timings import Timings
from .. import timings
from .. import handleprops
from ..win32_element_info import HwndElementInfo
from .. import backend
# I leave this optional because PIL is a large dependency
try:
from PIL import ImageGrab
except ImportError:
ImageGrab = None
# also import MenuItemNotEnabled so that it is
# accessible from HwndWrapper module
from .menuwrapper import Menu #, MenuItemNotEnabled
from ..base_wrapper import BaseWrapper
from ..base_wrapper import BaseMeta
from .. import deprecated
#====================================================================
class ControlNotEnabled(RuntimeError):
"""Raised when a control is not enabled"""
pass
#====================================================================
class ControlNotVisible(RuntimeError):
"""Raised when a control is not visible"""
pass
#====================================================================
class InvalidWindowHandle(RuntimeError):
"""Raised when an invalid handle is passed to HwndWrapper"""
def __init__(self, hwnd):
"""Initialise the RuntimError parent with the mesage"""
RuntimeError.__init__(self,
"Handle {0} is not a vaild window handle".format(hwnd))
#=========================================================================
class HwndMeta(BaseMeta):
"""Metaclass for HwndWrapper objects"""
re_wrappers = {}
str_wrappers = {}
def __init__(cls, name, bases, attrs):
"""
Register the class names
Both the regular expression or the classes directly are registered.
"""
BaseMeta.__init__(cls, name, bases, attrs)
for win_class in cls.windowclasses:
HwndMeta.re_wrappers[re.compile(win_class)] = cls
HwndMeta.str_wrappers[win_class] = cls
@staticmethod
def find_wrapper(element):
"""Find the correct wrapper for this native element"""
if isinstance(element, six.integer_types):
element = HwndElementInfo(element)
class_name = element.class_name
try:
return HwndMeta.str_wrappers[class_name]
except KeyError:
wrapper_match = None
for regex, wrapper in HwndMeta.re_wrappers.items():
if regex.match(class_name):
wrapper_match = wrapper
HwndMeta.str_wrappers[class_name] = wrapper
return wrapper
# if it is a dialog then override the wrapper we found
# and make it a DialogWrapper
if handleprops.is_toplevel_window(element.handle):
wrapper_match = DialogWrapper
if wrapper_match is None:
wrapper_match = HwndWrapper
return wrapper_match
#====================================================================
@six.add_metaclass(HwndMeta)
class HwndWrapper(BaseWrapper):
"""
Default wrapper for controls.
All other wrappers are derived from this.
This class wraps a lot of functionality of underlying windows API
features for working with windows.
Most of the methods apply to every single window type. For example
you can click() on any window.
Most of the methods of this class are simple wrappers around
API calls and as such they try do the simplest thing possible.
An HwndWrapper object can be passed directly to a ctypes wrapped
C function - and it will get converted to a Long with the value of
it's handle (see ctypes, _as_parameter_).
"""
handle = None
# -----------------------------------------------------------
def __new__(cls, element):
"""Construct the control wrapper"""
return super(HwndWrapper, cls)._create_wrapper(cls, element, HwndWrapper)
# -----------------------------------------------------------
def __init__(self, element_info):
"""
Initialize the control
* **element_info** is either a valid HwndElementInfo or it can be an
instance or subclass of HwndWrapper.
If the handle is not valid then an InvalidWindowHandle error
is raised.
"""
if isinstance(element_info, six.integer_types):
element_info = HwndElementInfo(element_info)
if hasattr(element_info, "element_info"):
element_info = element_info.element_info
BaseWrapper.__init__(self, element_info, backend.registry.backends['win32'])
# verify that we have been passed in a valid windows handle
if not win32functions.IsWindow(self.handle):
raise InvalidWindowHandle(self.handle)
# make it so that ctypes conversion happens correctly
self._as_parameter_ = self.handle
@property
def writable_props(self):
"""Extend default properties list."""
props = super(HwndWrapper, self).writable_props
props.extend(['style',
'exstyle',
'user_data',
'context_help_id',
'fonts',
'client_rects',
'is_unicode',
'menu_items',
'automation_id',
])
return props
# -----------------------------------------------------------
def style(self):
"""
Returns the style of window
Return value is a long.
Combination of WS_* and specific control specific styles.
See HwndWrapper.has_style() to easily check if the window has a
particular style.
"""
return handleprops.style(self)
# Non PEP-8 alias
Style = deprecated(style)
# -----------------------------------------------------------
def exstyle(self):
"""
Returns the Extended style of window
Return value is a long.
Combination of WS_* and specific control specific styles.
See HwndWrapper.has_style() to easily check if the window has a
particular style.
"""
return handleprops.exstyle(self)
# Non PEP-8 alias
ExStyle = deprecated(exstyle, deprecated_name='ExStyle')
#------------------------------------------------------------
def automation_id(self):
"""Return the .NET name of the control"""
return self.element_info.automation_id
#------------------------------------------------------------
def control_type(self):
"""Return the .NET type of the control"""
return self.element_info.control_type
#------------------------------------------------------------
def full_control_type(self):
"""Return the .NET type of the control (full, uncut)"""
return self.element_info.full_control_type
# -----------------------------------------------------------
def user_data(self):
"""
Extra data associted with the window
This value is a long value that has been associated with the window
and rarely has useful data (or at least data that you know the use
of).
"""
return handleprops.userdata(self)
# Non PEP-8 alias
UserData = deprecated(user_data)
# -----------------------------------------------------------
def context_help_id(self):
"""Return the Context Help ID of the window"""
return handleprops.contexthelpid(self)
# Non PEP-8 alias
ContextHelpID = deprecated(context_help_id, deprecated_name='ContextHelpID')
# -----------------------------------------------------------
def is_active(self):
"""Whether the window is active or not"""
return self.top_level_parent() == self.get_active()
# Non PEP-8 alias
IsActive = deprecated(is_active)
# -----------------------------------------------------------
def is_unicode(self):
"""
Whether the window is unicode or not
A window is Unicode if it was registered by the Wide char version
of RegisterClass(Ex).
"""
return handleprops.isunicode(self)
# Non PEP-8 alias
IsUnicode = deprecated(is_unicode)
# -----------------------------------------------------------
def client_rect(self):
"""
Returns the client rectangle of window
The client rectangle is the window rectangle minus any borders that
are not available to the control for drawing.
Both top and left are always 0 for this method.
This method returns a RECT structure, Which has attributes - top,
left, right, bottom. and has methods width() and height().
See win32structures.RECT for more information.
"""
return handleprops.clientrect(self)
# Non PEP-8 alias
ClientRect = deprecated(client_rect)
# -----------------------------------------------------------
#def client_to_screen(self, client_point):
# """Maps point from client to screen coordinates"""
# point = win32structures.POINT()
# if isinstance(client_point, win32structures.POINT):
# point.x = client_point.x
# point.y = client_point.y
# else:
# point.x = client_point[0]
# point.y = client_point[1]
# win32functions.client_to_screen(self, ctypes.byref(point))
#
# # return tuple in any case because
# # coords param is always expected to be tuple
# return point.x, point.y
# -----------------------------------------------------------
def font(self):
"""
Return the font of the window
The font of the window is used to draw the text of that window.
It is a structure which has attributes for font name, height, width
etc.
See win32structures.LOGFONTW for more information.
"""
return handleprops.font(self)
# Non PEP-8 alias
Font = deprecated(font)
# -----------------------------------------------------------
def has_style(self, style):
"""Return True if the control has the specified style"""
return handleprops.has_style(self, style)
# Non PEP-8 alias
HasStyle = deprecated(has_style)
# -----------------------------------------------------------
def has_exstyle(self, exstyle):
"""Return True if the control has the specified extended style"""
return handleprops.has_exstyle(self, exstyle)
# Non PEP-8 alias
HasExStyle = deprecated(has_exstyle, deprecated_name='HasExStyle')
# -----------------------------------------------------------
def is_dialog(self):
"""Return true if the control is a top level window"""
if not ("isdialog" in self._cache.keys()):
self._cache['isdialog'] = handleprops.is_toplevel_window(self)
return self._cache['isdialog']
# -----------------------------------------------------------
def client_rects(self):
"""
Return the client rect for each item in this control
It is a list of rectangles for the control. It is frequently over-ridden
to extract all rectangles from a control with multiple items.
It is always a list with one or more rectangles:
* First elemtent is the client rectangle of the control
* Subsequent elements contain the client rectangle of any items of
the control (e.g. items in a listbox/combobox, tabs in a
tabcontrol)
"""
return [self.client_rect(), ]
# Non PEP-8 alias
ClientRects = deprecated(client_rects)
# -----------------------------------------------------------
def fonts(self):
"""
Return the font for each item in this control
It is a list of fonts for the control. It is frequently over-ridden
to extract all fonts from a control with multiple items.
It is always a list with one or more fonts:
* First elemtent is the control font
* Subsequent elements contain the font of any items of
the control (e.g. items in a listbox/combobox, tabs in a
tabcontrol)
"""
return [self.font(), ]
# Non PEP-8 alias
Fonts = deprecated(fonts)
# -----------------------------------------------------------
def send_command(self, commandID):
return self.send_message(win32defines.WM_COMMAND, commandID)
# Non PEP-8 alias
SendCommand = deprecated(send_command)
# -----------------------------------------------------------
def post_command(self, commandID):
return self.post_message(win32defines.WM_COMMAND, commandID)
# Non PEP-8 alias
PostCommand = deprecated(post_command)
# -----------------------------------------------------------
#def notify(self, code):
# "Send a notification to the parent (not tested yet)"
# # now we need to notify the parent that the state has changed
# nmhdr = win32structures.NMHDR()
# nmhdr.hwndFrom = self.handle
# nmhdr.idFrom = self.control_id()
# nmhdr.code = code
# from ..remote_memory_block import RemoteMemoryBlock
# remote_mem = RemoteMemoryBlock(self, size=ctypes.sizeof(nmhdr))
# remote_mem.Write(nmhdr, size=ctypes.sizeof(nmhdr))
# retval = self.parent().send_message(
# win32defines.WM_NOTIFY,
# self.handle,
# remote_mem)
# #if retval != win32defines.TRUE:
# # print('retval = ' + str(retval))
# # raise ctypes.WinError()
# del remote_mem
# return retval
# Non PEP-8 alias
#Notify = deprecated(notify)
# -----------------------------------------------------------
def _ensure_enough_privileges(self, message_name):
"""Ensure the Python process has enough rights to send some window messages"""
pid = handleprops.processid(self.handle)
if not handleprops.has_enough_privileges(pid):
raise RuntimeError('Not enough rights to use {} message/function for target process ' \
'(to resolve it run the script as Administrator)'.format(message_name))
# -----------------------------------------------------------
def send_message(self, message, wparam = 0, lparam = 0):
"""Send a message to the control and wait for it to return"""
wParamAddress = wparam
if hasattr(wparam, 'mem_address'):
wParamAddress = wparam.mem_address
lParamAddress = lparam
if hasattr(lparam, 'mem_address'):
lParamAddress = lparam.mem_address
CArgObject = type(ctypes.byref(ctypes.c_int(0)))
if isinstance(wparam, CArgObject):
wParamAddress = ctypes.addressof(wparam._obj)
if isinstance(lparam, CArgObject):
lParamAddress = ctypes.addressof(lparam._obj)
return win32gui.SendMessage(self.handle, message, wParamAddress, lParamAddress)
# Non PEP-8 alias
SendMessage = deprecated(send_message)
# -----------------------------------------------------------
def send_chars(self,
chars,
with_spaces=True,
with_tabs=True,
with_newlines=True):
"""
Silently send a character string to the control in an inactive window
If a virtual key with no corresponding character is encountered
(e.g. VK_LEFT, VK_DELETE), a KeySequenceError is raised. Consider using
the method send_keystrokes for such input.
"""
input_locale_id = ctypes.windll.User32.GetKeyboardLayout(0)
keys = keyboard.parse_keys(chars, with_spaces, with_tabs, with_newlines)
for key in keys:
key_info = key.get_key_info()
flags = key_info[2]
unicode_char = (
flags & keyboard.KEYEVENTF_UNICODE ==
keyboard.KEYEVENTF_UNICODE)
if unicode_char:
_, char = key_info[:2]
vk = ctypes.windll.User32.VkKeyScanExW(char, input_locale_id) & 0xFF
scan = keyboard.MapVirtualKey(vk, 0)
else:
vk, scan = key_info[:2]
char = keyboard.MapVirtualKey(vk, 2)
if char > 0:
lparam = 1 << 0 | scan << 16 | (flags & 1) << 24
win32api.SendMessage(self.handle, win32con.WM_CHAR, char, lparam)
else:
raise keyboard.KeySequenceError(
'no WM_CHAR code for {key}, use method send_keystrokes instead'.
format(key=key))
# -----------------------------------------------------------
def send_keystrokes(self,
keystrokes,
with_spaces=True,
with_tabs=True,
with_newlines=True):
"""
Silently send keystrokes to the control in an inactive window
Parses modifiers Shift(+), Control(^), Menu(%) and Sequences like "{TAB}", "{ENTER}"
For more information about Sequences and Modifiers navigate to keyboard.py
Due to the fact that each application handles input differently and this method
is meant to be used on inactive windows, it may work only partially depending
on the target app. If the window being inactive is not essential, use the robust
type_keys method.
"""
user32 = ctypes.windll.User32
PBYTE256 = ctypes.c_ubyte * 256
win32gui.SendMessage(self.handle, win32con.WM_ACTIVATE,
win32con.WA_ACTIVE, 0)
target_thread_id = user32.GetWindowThreadProcessId(self.handle, None)
current_thread_id = win32api.GetCurrentThreadId()
attach_success = user32.AttachThreadInput(target_thread_id, current_thread_id, True) != 0
if not attach_success:
warnings.warn('Failed to attach app\'s thread to the current thread\'s message queue',
UserWarning, stacklevel=2)
keyboard_state_stack = [PBYTE256()]
user32.GetKeyboardState(ctypes.byref(keyboard_state_stack[-1]))
input_locale_id = ctypes.windll.User32.GetKeyboardLayout(0)
context_code = 0
keys = keyboard.parse_keys(keystrokes, with_spaces, with_tabs, with_newlines)
key_combos_present = any([isinstance(k, keyboard.EscapedKeyAction) for k in keys])
if key_combos_present:
warnings.warn('Key combinations may or may not work depending on the target app',
UserWarning, stacklevel=2)
try:
for key in keys:
vk, scan, flags = key.get_key_info()
if vk == keyboard.VK_MENU or context_code == 1:
down_msg, up_msg = win32con.WM_SYSKEYDOWN, win32con.WM_SYSKEYUP
else:
down_msg, up_msg = win32con.WM_KEYDOWN, win32con.WM_KEYUP
repeat = 1
shift_state = 0
unicode_codepoint = flags & keyboard.KEYEVENTF_UNICODE != 0
if unicode_codepoint:
char = scan
vk_with_flags = user32.VkKeyScanExW(char, input_locale_id)
vk = vk_with_flags & 0xFF
shift_state = (vk_with_flags & 0xFF00) >> 8
scan = keyboard.MapVirtualKey(vk, 0)
if key.down and vk > 0:
new_keyboard_state = copy.deepcopy(keyboard_state_stack[-1])
new_keyboard_state[vk] |= 128
if shift_state & 1 == 1:
new_keyboard_state[keyboard.VK_SHIFT] |= 128
# NOTE: if there are characters with CTRL or ALT in the shift
# state, make sure to add these keys to new_keyboard_state
keyboard_state_stack.append(new_keyboard_state)
lparam = (
repeat << 0 |
scan << 16 |
(flags & 1) << 24 |
context_code << 29 |
0 << 31)
user32.SetKeyboardState(ctypes.byref(keyboard_state_stack[-1]))
win32api.PostMessage(self.handle, down_msg, vk, lparam)
if vk == keyboard.VK_MENU:
context_code = 1
# a delay for keyboard state to take effect
time.sleep(0.01)
if key.up and vk > 0:
keyboard_state_stack.pop()
lparam = (
repeat << 0 |
scan << 16 |
(flags & 1) << 24 |
context_code << 29 |
1 << 30 |
1 << 31)
win32api.PostMessage(self.handle, up_msg, vk, lparam)
user32.SetKeyboardState(ctypes.byref(keyboard_state_stack[-1]))
if vk == keyboard.VK_MENU:
context_code = 0
# a delay for keyboard state to take effect
time.sleep(0.01)
except pywintypes.error as e:
if e.winerror == 1400:
warnings.warn('Application exited before the end of keystrokes',
UserWarning, stacklevel=2)
else:
warnings.warn(e.strerror, UserWarning, stacklevel=2)
user32.SetKeyboardState(ctypes.byref(keyboard_state_stack[0]))
if attach_success:
user32.AttachThreadInput(target_thread_id, current_thread_id, False)
# -----------------------------------------------------------
def send_message_timeout(
self,
message,
wparam = 0,
lparam = 0,
timeout = None,
timeoutflags = win32defines.SMTO_NORMAL):
"""
Send a message to the control and wait for it to return or to timeout
If no timeout is given then a default timeout of .01 of a second will
be used.
"""
if timeout is None:
timeout = Timings.sendmessagetimeout_timeout
result = -1
try:
(_, result) = win32gui.SendMessageTimeout(
int(self.handle),
message,
wparam,
lparam,
timeoutflags,
int(timeout * 1000)
)
except Exception as exc:
#import traceback
#print('____________________________________________________________')
#print('self.handle =', int(self.handle), ', message =', message,
# ', wparam =', wparam, ', lparam =', lparam, ', timeout =', timeout)
#print('Exception: ', exc)
#print(traceback.format_exc())
result = str(exc)
return result #result.value
# Non PEP-8 alias
SendMessageTimeout = deprecated(send_message_timeout)
# -----------------------------------------------------------
def post_message(self, message, wparam = 0, lparam = 0):
"""Post a message to the control message queue and return"""
return win32functions.PostMessage(self, message, wparam, lparam)
# Non PEP-8 alias
PostMessage = deprecated(post_message)
# # -----------------------------------------------------------
# def notify_menu_select(self, menu_id):
# """Notify the dialog that one of it's menu items was selected
#
# **This method is Deprecated**
# """
#
# import warnings
# warning_msg = "HwndWrapper.NotifyMenuSelect() is deprecated - " \
# "equivalent functionality is being moved to the MenuWrapper class."
# warnings.warn(warning_msg, DeprecationWarning)
#
# self.set_focus()
#
# msg = win32defines.WM_COMMAND
# return self.send_message_timeout(
# msg,
# win32functions.MakeLong(0, menu_id), #wparam
# )
# Non PEP-8 alias
# NotifyMenuSelect = deprecated(notify_menu_select)
# -----------------------------------------------------------
def notify_parent(self, message, controlID = None):
"""Send the notification message to parent of this control"""
if controlID is None:
controlID = self.control_id()
return self.parent().post_message(
win32defines.WM_COMMAND,
win32functions.MakeLong(message, controlID),
self)
# Non PEP-8 alias
NotifyParent = deprecated(notify_parent)
# -----------------------------------------------------------
def __hash__(self):
"""Returns the hash value of the handle"""
return hash(self.handle)
#-----------------------------------------------------------
def wait_for_idle(self):
"""Backend specific function to wait for idle state of a thread or a window"""
win32functions.WaitGuiThreadIdle(self)
# -----------------------------------------------------------
def click(
self, button = "left", pressed = "", coords = (0, 0), double = False, absolute = False):
"""
Simulates a mouse click on the control
This method sends WM_* messages to the control, to do a more
'realistic' mouse click use click_input() which uses mouse_event() API
to perform the click.
This method does not require that the control be visible on the screen
(i.e. it can be hidden beneath another window and it will still work).
"""
self.verify_actionable()
self._ensure_enough_privileges('WM_*BUTTONDOWN/UP')
_perform_click(self, button, pressed, coords, double, absolute=absolute)
return self
# Non PEP-8 alias
Click = deprecated(click)
# -----------------------------------------------------------
def close_click(
self, button = "left", pressed = "", coords = (0, 0), double = False):
"""
Perform a click action that should make the window go away
The only difference from click is that there are extra delays
before and after the click action.
"""
time.sleep(Timings.before_closeclick_wait)
_perform_click(self, button, pressed, coords, double)
def has_closed():
closed = not (
win32functions.IsWindow(self) or
win32functions.IsWindow(self.parent()))
if not closed:
# try closing again
try:
_perform_click(self, button, pressed, coords, double)
except Exception:
return True # already closed
return closed
# Keep waiting until both this control and it's parent
# are no longer valid controls
timings.wait_until(
Timings.closeclick_dialog_close_wait,
Timings.closeclick_retry,
has_closed
)
time.sleep(Timings.after_closeclick_wait)
return self
# Non PEP-8 alias
CloseClick = deprecated(close_click)
# -----------------------------------------------------------
def close_alt_f4(self):
"""Close the window by pressing Alt+F4 keys."""
time.sleep(Timings.before_closeclick_wait)
self.type_keys('%{F4}')
time.sleep(Timings.after_closeclick_wait)
return self
# Non PEP-8 alias
CloseAltF4 = deprecated(close_alt_f4)
# -----------------------------------------------------------
def double_click(
self, button = "left", pressed = "", coords = (0, 0)):
"""Perform a double click action"""
_perform_click(self, button, pressed, coords, double = True)
return self
# Non PEP-8 alias
DoubleClick = deprecated(double_click)
# -----------------------------------------------------------
def right_click(
self, pressed = "", coords = (0, 0)):
"""Perform a right click action"""
_perform_click(
self, "right", "right " + pressed, coords, button_up = False)
_perform_click(self, "right", pressed, coords, button_down = False)
return self
# Non PEP-8 alias
RightClick = deprecated(right_click)
# -----------------------------------------------------------
def press_mouse(self, button ="left", coords = (0, 0), pressed =""):
"""Press the mouse button"""
#flags, click_point = _calc_flags_and_coords(pressed, coords)
_perform_click(self, button, pressed, coords, button_down=True, button_up=False)
return self
# Non PEP-8 alias
PressMouse = deprecated(press_mouse)
# -----------------------------------------------------------
def release_mouse(self, button ="left", coords = (0, 0), pressed =""):
"""Release the mouse button"""
#flags, click_point = _calc_flags_and_coords(pressed, coords)
_perform_click(self, button, pressed, coords, button_down=False, button_up=True)
return self
# Non PEP-8 alias
ReleaseMouse = deprecated(release_mouse)
# -----------------------------------------------------------
def move_mouse(self, coords = (0, 0), pressed ="", absolute = False):
"""Move the mouse by WM_MOUSEMOVE"""
if not absolute:
self.actions.log('Moving mouse to relative (client) coordinates ' + str(coords).replace('\n', ', '))
_perform_click(self, button='move', coords=coords, absolute=absolute, pressed=pressed)
win32functions.WaitGuiThreadIdle(self)
return self
# Non PEP-8 alias
MoveMouse = deprecated(move_mouse)
# -----------------------------------------------------------
def drag_mouse(self, button ="left",
press_coords = (0, 0),
release_coords = (0, 0),
pressed = ""):
"""Drag the mouse"""
if isinstance(press_coords, win32structures.POINT):
press_coords = (press_coords.x, press_coords.y)
if isinstance(release_coords, win32structures.POINT):
release_coords = (release_coords.x, release_coords.y)
_pressed = pressed
if not _pressed:
_pressed = "left"
self.press_mouse(button, press_coords, pressed=pressed)
for i in range(5):
self.move_mouse((press_coords[0] + i, press_coords[1]), pressed=_pressed)
time.sleep(Timings.drag_n_drop_move_mouse_wait)
self.move_mouse(release_coords, pressed=_pressed)
time.sleep(Timings.before_drop_wait)
self.release_mouse(button, release_coords, pressed=pressed)
time.sleep(Timings.after_drag_n_drop_wait)
return self
# Non PEP-8 alias
DragMouse = deprecated(drag_mouse)
# -----------------------------------------------------------
def set_window_text(self, text, append = False):
"""Set the text of the window"""
self.verify_actionable()
if append:
text = self.window_text() + text
text = ctypes.c_wchar_p(six.text_type(text))
self.post_message(win32defines.WM_SETTEXT, 0, text)
win32functions.WaitGuiThreadIdle(self)
self.actions.log('Set text to the ' + self.friendly_class_name() + ': ' + str(text))
return self
# Non PEP-8 alias
SetWindowText = deprecated(set_window_text)
# -----------------------------------------------------------
def debug_message(self, text):
"""Write some debug text over the window"""
# don't draw if dialog is not visible
dc = win32functions.CreateDC("DISPLAY", None, None, None)
if not dc:
raise ctypes.WinError()
rect = self.rectangle()
#ret = win32functions.TextOut(
# dc, rect.left, rect.top, six.text_type(text), len(text))
ret = win32functions.DrawText(
dc,
six.text_type(text),
len(text),
ctypes.byref(rect),
win32defines.DT_SINGLELINE)
# delete the Display context that we created
win32functions.DeleteDC(dc)
if not ret:
raise ctypes.WinError()
return self
# Non PEP-8 alias
DebugMessage = deprecated(debug_message)
# -----------------------------------------------------------
def set_transparency(self, alpha = 120):
"""Set the window transparency from 0 to 255 by alpha attribute"""
if not (0 <= alpha <= 255):
raise ValueError('alpha should be in [0, 255] interval!')
# TODO: implement SetExStyle method
win32gui.SetWindowLong(self.handle, win32defines.GWL_EXSTYLE, self.exstyle() | win32con.WS_EX_LAYERED)
win32gui.SetLayeredWindowAttributes(self.handle, win32api.RGB(0,0,0), alpha, win32con.LWA_ALPHA)
# Non PEP-8 alias
SetTransparency = deprecated(set_transparency)
# -----------------------------------------------------------
def popup_window(self):
"""Return owned enabled Popup window wrapper if shown.
If there is no enabled popups at that time, it returns **self**.
See MSDN reference:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633515.aspx
Please do not use in production code yet - not tested fully
"""
popup = win32functions.GetWindow(self, win32defines.GW_ENABLEDPOPUP)
return popup
# Non PEP-8 alias
PopupWindow = deprecated(popup_window)
# -----------------------------------------------------------
def owner(self):
"""Return the owner window for the window if it exists
Returns None if there is no owner"""
owner = win32functions.GetWindow(self, win32defines.GW_OWNER)
if owner:
return HwndWrapper(owner)
else:
return None
# Non PEP-8 alias
Owner = deprecated(owner)
# -----------------------------------------------------------
# def context_menu_select(self, path, x = None, y = None):
# "TODO context_menu_select Not Implemented"
# pass
# #raise NotImplementedError(
# # "HwndWrapper.ContextMenuSelect not implemented yet")
# # Non PEP-8 alias
# ContextMenuSelect = deprecated(context_menu_select)
# -----------------------------------------------------------
def _menu_handle(self):
"""Simple overridable method to get the menu handle"""
hMenu = win32gui.GetMenu(self.handle)
is_main_menu = True
if not hMenu:
self._ensure_enough_privileges('MN_GETHMENU')
hMenu = self.send_message(self.handle, win32defines.MN_GETHMENU);
is_main_menu = False
return (hMenu, is_main_menu)
# -----------------------------------------------------------
def menu(self):
"Return the menu of the control"
hMenu, is_main_menu = self._menu_handle()
if hMenu: # and win32functions.IsMenu(menu_hwnd):
return Menu(self, hMenu, is_main_menu=is_main_menu)
return None
# Non PEP-8 alias
Menu = deprecated(menu)
# -----------------------------------------------------------
def menu_item(self, path, exact = False):
"""Return the menu item specified by path
Path can be a string in the form "MenuItem->MenuItem->MenuItem..."
where each MenuItem is the text of an item at that level of the menu.
E.g. ::
File->Export->ExportAsPNG
spaces are not important so you could also have written... ::
File -> Export -> Export As PNG
"""
if self.appdata is not None:
menu_appdata = self.appdata['menu_items']
else:
menu_appdata = None
menu = self.menu()
if menu:
return self.menu().get_menu_path(path, appdata = menu_appdata, exact=exact)[-1]
raise RuntimeError("There is no menu.")
# Non PEP-8 alias
MenuItem = deprecated(menu_item)
# -----------------------------------------------------------
def menu_items(self):
"""Return the menu items for the dialog
If there are no menu items then return an empty list
"""
if self.is_dialog() and self.menu():
#menu_handle = win32functions.GetMenu(self)
#self.send_message(win32defines.WM_INITMENU, menu_handle)
return self.menu().get_properties()['menu_items']
#self.send_message(win32defines.WM_INITMENU, menu_handle)
#return _GetMenuItems(menu_handle, self)
else:
return []
# Non PEP-8 alias
MenuItems = deprecated(menu_items)
# # -----------------------------------------------------------
# def menu_click(self, path):
# "Select the MenuItem specifed in path"
#
# self.verify_actionable()
#
# self.set_focus()
#
# menu = Menu(self, self._menu_handle())
#
# path_items = menu.get_menu_path(path)
#
# for menu_item in path_items:
# if not menu_item.is_enabled():
# raise MenuItemNotEnabled(
# "MenuItem '%s' is disabled"% menu_item.text())
#
# menu_item.click()
#
# return self
# # Non PEP-8 alias
# MenuClick = deprecated(menu_click)
# -----------------------------------------------------------
def menu_select(self, path, exact=False, ):
"""Find a menu item specified by the path
The full path syntax is specified in:
:py:meth:`.controls.menuwrapper.Menu.get_menu_path`
"""
self.verify_actionable()
self.menu_item(path, exact=exact).select()
# Non PEP-8 alias
MenuSelect = deprecated(menu_select)
# -----------------------------------------------------------
def move_window(
self,
x = None,
y = None,
width = None,
height = None,
repaint = True):
"""Move the window to the new coordinates
* **x** Specifies the new left position of the window.
Defaults to the current left position of the window.
* **y** Specifies the new top position of the window.
Defaults to the current top position of the window.
* **width** Specifies the new width of the window. Defaults to the
current width of the window.
* **height** Specifies the new height of the window. Default to the
current height of the window.
* **repaint** Whether the window should be repainted or not.
Defaults to True
"""
cur_rect = self.rectangle()
# if no X is specified - so use current coordinate
if x is None:
x = cur_rect.left
else:
try:
y = x.top
width = x.width()
height = x.height()
x = x.left
except AttributeError:
pass
# if no Y is specified - so use current coordinate
if y is None:
y = cur_rect.top
# if no width is specified - so use current width
if width is None:
width = cur_rect.width()
# if no height is specified - so use current height
if height is None:
height = cur_rect.height()
# ask for the window to be moved
ret = win32functions.MoveWindow(self, x, y, width, height, repaint)
# check that it worked correctly
if not ret:
raise ctypes.WinError()
win32functions.WaitGuiThreadIdle(self)
time.sleep(Timings.after_movewindow_wait)
# Non PEP-8 alias
MoveWindow = deprecated(move_window)
# -----------------------------------------------------------
def close(self, wait_time = 0):
"""Close the window
Code modified from http://msdn.microsoft.com/msdnmag/issues/02/08/CQA/
"""
window_text = self.window_text()
# tell the window it must close
self.post_message(win32defines.WM_CLOSE)
#unused var: start = time.time()
# Keeps trying while
# we have not timed out and
# window is still a valid handle and
# window is still visible
# any one of these conditions evaluates to false means the window is
# closed or we have timed out
def has_closed():
return not (win32functions.IsWindow(self) and self.is_visible())
if not wait_time:
wait_time = Timings.closeclick_dialog_close_wait
# Keep waiting until both this control and it's parent
# are no longer valid controls
timings.wait_until(
wait_time,
Timings.closeclick_retry,
has_closed
)
self.actions.log('Closed window "{0}"'.format(window_text))
# Non PEP-8 alias
Close = deprecated(close)
# -----------------------------------------------------------
def maximize(self):
"""Maximize the window"""
win32functions.ShowWindow(self, win32defines.SW_MAXIMIZE)
self.actions.log('Maximized window "{0}"'.format(self.window_text()))
return self
# Non PEP-8 alias
Maximize = deprecated(maximize)
# -----------------------------------------------------------
def minimize(self):
"""Minimize the window"""
win32functions.ShowWindow(self, win32defines.SW_MINIMIZE)
self.actions.log('Minimized window "{0}"'.format(self.window_text()))
return self
# Non PEP-8 alias
Minimize = deprecated(minimize)
# -----------------------------------------------------------
def restore(self):
"""Restore the window to its previous state (normal or maximized)"""
win32functions.ShowWindow(self, win32defines.SW_RESTORE)
self.actions.log('Restored window "{0}"'.format(self.window_text()))
return self
# Non PEP-8 alias
Restore = deprecated(restore)
# -----------------------------------------------------------
def get_show_state(self):
"""Get the show state and Maximized/minimzed/restored state
Returns a value that is a union of the following
* SW_HIDE the window is hidden.
* SW_MAXIMIZE the window is maximized
* SW_MINIMIZE the window is minimized
* SW_RESTORE the window is in the 'restored'
state (neither minimized or maximized)
* SW_SHOW The window is not hidden
"""
wp = win32structures.WINDOWPLACEMENT()
wp.lenght = ctypes.sizeof(wp)
ret = win32functions.GetWindowPlacement(self, ctypes.byref(wp))
if not ret:
raise ctypes.WinError()
return wp.showCmd
# Non PEP-8 alias
GetShowState = deprecated(get_show_state)
# -----------------------------------------------------------
def is_minimized(self):
"""Indicate whether the window is minimized or not"""
return self.get_show_state() == win32defines.SW_SHOWMINIMIZED
# -----------------------------------------------------------
def is_maximized(self):
"""Indicate whether the window is maximized or not"""
return self.get_show_state() == win32defines.SW_SHOWMAXIMIZED
# -----------------------------------------------------------
def is_normal(self):
"""Indicate whether the window is normal (i.e. not minimized and not maximized)"""
return self.get_show_state() == win32defines.SW_SHOWNORMAL
# -----------------------------------------------------------
def get_active(self):
"""Return a handle to the active window within the process"""
gui_info = win32structures.GUITHREADINFO()
gui_info.cbSize = ctypes.sizeof(gui_info)
window_thread_id, _ = win32process.GetWindowThreadProcessId(int(self.handle))
ret = win32functions.GetGUIThreadInfo(
window_thread_id,
ctypes.byref(gui_info))
if not ret:
raise ctypes.WinError()
hwndActive = gui_info.hwndActive
if hwndActive:
return HwndWrapper(hwndActive)
else:
return None
# Non PEP-8 alias
GetActive = deprecated(get_active)
# -----------------------------------------------------------
def get_focus(self):
"""Return the control in the process of this window that has the Focus
"""
gui_info = win32structures.GUITHREADINFO()
gui_info.cbSize = ctypes.sizeof(gui_info)
window_thread_id, _ = win32process.GetWindowThreadProcessId(self.handle)
ret = win32functions.GetGUIThreadInfo(
window_thread_id,
ctypes.byref(gui_info))
if not ret:
return None
return HwndWrapper(gui_info.hwndFocus)
# Non PEP-8 alias
GetFocus = deprecated(get_focus)
# -----------------------------------------------------------
def set_focus(self):
"""
Set the focus to this control.
Bring the window to the foreground first.
The system restricts which processes can set the foreground window
(https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx)
so the mouse cursor is removed from the screen to prevent any side effects.
"""
# "steal the focus" if there is another active window
# otherwise it is already into the foreground and no action required
if not self.has_focus():
# Notice that we need to move the mouse out of the screen
# but we don't use the built-in methods of the class:
# self.mouse_move doesn't do the job well even with absolute=True
# self.move_mouse_input can't be used as it calls click_input->set_focus
mouse.move(coords=(-10000, 500)) # move the mouse out of screen to the left
# change active window
if self.is_minimized():
if self.was_maximized():
self.maximize()
else:
self.restore()
else:
win32gui.ShowWindow(self.handle, win32con.SW_SHOW)
win32gui.SetForegroundWindow(self.handle)
# make sure that we are idle before returning
win32functions.WaitGuiThreadIdle(self)
# only sleep if we had to change something!
time.sleep(Timings.after_setfocus_wait)
return self
# Non PEP-8 alias
SetFocus = deprecated(set_focus)
def has_focus(self):
"""Check the window is in focus (foreground)"""
return self.handle == win32gui.GetForegroundWindow()
def has_keyboard_focus(self):
"""Check the keyboard focus on this control."""
control_thread = win32process.GetWindowThreadProcessId(self.handle)[0]
win32process.AttachThreadInput(control_thread, win32api.GetCurrentThreadId(), 1)
focused = win32gui.GetFocus()
win32process.AttachThreadInput(control_thread, win32api.GetCurrentThreadId(), 0)
win32functions.WaitGuiThreadIdle(self)
return self.handle == focused
def set_keyboard_focus(self):
"""Set the keyboard focus to this control."""
control_thread = win32process.GetWindowThreadProcessId(self.handle)[0]
win32process.AttachThreadInput(control_thread, win32api.GetCurrentThreadId(), 1)
win32gui.SetFocus(self.handle)
win32process.AttachThreadInput(control_thread, win32api.GetCurrentThreadId(), 0)
win32functions.WaitGuiThreadIdle(self)
time.sleep(Timings.after_setfocus_wait)
return self
# -----------------------------------------------------------
def set_application_data(self, appdata):
"""Application data is data from a previous run of the software
It is essential for running scripts written for one spoke language
on a different spoken language
"""
self.appdata = appdata
_scroll_types = {"left": {
"line" : win32defines.SB_LINELEFT,
"page" : win32defines.SB_PAGELEFT,
"end" : win32defines.SB_LEFT,
},
"right": {
"line" : win32defines.SB_LINERIGHT,
"page" : win32defines.SB_PAGERIGHT,
"end" : win32defines.SB_RIGHT,
},
"up": {
"line" : win32defines.SB_LINEUP,
"page" : win32defines.SB_PAGEUP,
"end" : win32defines.SB_TOP,
},
"down": {
"line" : win32defines.SB_LINEDOWN,
"page" : win32defines.SB_PAGEDOWN,
"end" : win32defines.SB_BOTTOM,
},
}
# Non PEP-8 alias
SetApplicationData = deprecated(set_application_data)
# -----------------------------------------------------------
def scroll(self, direction, amount, count = 1, retry_interval = None):
"""Ask the control to scroll itself
**direction** can be any of "up", "down", "left", "right"
**amount** can be one of "line", "page", "end"
**count** (optional) the number of times to scroll
"""
self._ensure_enough_privileges('WM_HSCROLL/WM_VSCROLL')
# check which message we want to send
if direction.lower() in ("left", "right"):
message = win32defines.WM_HSCROLL
elif direction.lower() in ("up", "down"):
message = win32defines.WM_VSCROLL
# the constant that matches direction, and how much
try:
scroll_type = \
self._scroll_types[direction.lower()][amount.lower()]
except KeyError:
raise ValueError("""Wrong arguments:
direction can be any of "up", "down", "left", "right"
amount can be any of "line", "page", "end"
""")
# Scroll as often as we have been asked to
if retry_interval is None:
retry_interval = Timings.scroll_step_wait
while count > 0:
self.send_message(message, scroll_type)
time.sleep(retry_interval)
count -= 1
return self
# Non PEP-8 alias
Scroll = deprecated(scroll)
# -----------------------------------------------------------
def get_toolbar(self):
"""Get the first child toolbar if it exists"""
for child in self.children():
if child.__class__.__name__ == 'ToolbarWrapper':
return child
return None
# Non PEP-8 alias
GetToolbar = deprecated(get_toolbar)
# Non PEP-8 aliases for BaseWrapper methods
# We keep them for the backward compatibility in legacy scripts
ClickInput = deprecated(BaseWrapper.click_input)
DoubleClickInput = deprecated(BaseWrapper.double_click_input)
RightClickInput = deprecated(BaseWrapper.right_click_input)
VerifyVisible = deprecated(BaseWrapper.verify_visible)
_NeedsImageProp = deprecated(BaseWrapper._needs_image_prop, deprecated_name='_NeedsImageProp')
FriendlyClassName = deprecated(BaseWrapper.friendly_class_name)
Class = deprecated(BaseWrapper.class_name, deprecated_name='Class')
WindowText = deprecated(BaseWrapper.window_text)
ControlID = deprecated(BaseWrapper.control_id, deprecated_name='ControlID')
IsVisible = deprecated(BaseWrapper.is_visible)
IsEnabled = deprecated(BaseWrapper.is_enabled)
Rectangle = deprecated(BaseWrapper.rectangle)
ClientToScreen = deprecated(BaseWrapper.client_to_screen)
ProcessID = deprecated(BaseWrapper.process_id, deprecated_name='ProcessID')
IsDialog = deprecated(BaseWrapper.is_dialog)
Parent = deprecated(BaseWrapper.parent)
TopLevelParent = deprecated(BaseWrapper.top_level_parent)
Texts = deprecated(BaseWrapper.texts)
Children = deprecated(BaseWrapper.children)
CaptureAsImage = deprecated(BaseWrapper.capture_as_image)
GetProperties = deprecated(BaseWrapper.get_properties)
DrawOutline = deprecated(BaseWrapper.draw_outline)
IsChild = deprecated(BaseWrapper.is_child)
VerifyActionable = deprecated(BaseWrapper.verify_actionable)
VerifyEnabled = deprecated(BaseWrapper.verify_enabled)
PressMouseInput = deprecated(BaseWrapper.press_mouse_input)
ReleaseMouseInput = deprecated(BaseWrapper.release_mouse_input)
MoveMouseInput = deprecated(BaseWrapper.move_mouse_input)
DragMouseInput = deprecated(BaseWrapper.drag_mouse_input)
WheelMouseInput = deprecated(BaseWrapper.wheel_mouse_input)
TypeKeys = deprecated(BaseWrapper.type_keys)
#====================================================================
# the main reason for this is just to make sure that
# a Dialog is a known class - and we don't need to take
# an image of it (as an unknown control class)
class DialogWrapper(HwndWrapper):
"""Wrap a dialog"""
friendlyclassname = "Dialog"
#windowclasses = ["#32770", ]
can_be_label = True
#-----------------------------------------------------------
def __init__(self, hwnd):
"""Initialize the DialogWrapper
The only extra functionality here is to modify self.friendlyclassname
to make it "Dialog" if the class is "#32770" otherwise to leave it
the same as the window class.
"""
HwndWrapper.__init__(self, hwnd)
if self.class_name() == "#32770":
self.friendlyclassname = "Dialog"
else:
self.friendlyclassname = self.class_name()
#-----------------------------------------------------------
def run_tests(self, tests_to_run = None, ref_controls = None):
"""Run the tests on dialog"""
# the tests package is imported only when running unittests
from .. import tests
# get all the controls
controls = [self] + self.children()
# add the reference controls
if ref_controls is not None:
matched_flags = controlproperties.SetReferenceControls(
controls, ref_controls)
# todo: allow some checking of how well the controls matched
# matched_flags says how well they matched
# 1 = same number of controls
# 2 = ID's matched
# 4 = control classes matched
# i.e. 1 + 2 + 4 = perfect match
return tests.run_tests(controls, tests_to_run)
# Non PEP-8 alias
RunTests = deprecated(run_tests)
#-----------------------------------------------------------
def write_to_xml(self, filename):
"""Write the dialog an XML file (requires elementtree)"""
controls = [self] + self.children()
props = [ctrl.get_properties() for ctrl in controls]
from .. import xml_helpers
xml_helpers.WriteDialogToFile(filename, props)
# Non PEP-8 alias
WriteToXML = deprecated(write_to_xml)
#-----------------------------------------------------------
def client_area_rect(self):
"""Return the client area rectangle
From MSDN:
The client area of a control is the bounds of the control, minus the
nonclient elements such as scroll bars, borders, title bars, and
menus.
"""
rect = win32structures.RECT(self.rectangle())
self.send_message(win32defines.WM_NCCALCSIZE, 0, ctypes.byref(rect))
return rect
# Non PEP-8 alias
ClientAreaRect = deprecated(client_area_rect)
#-----------------------------------------------------------
def hide_from_taskbar(self):
"""Hide the dialog from the Windows taskbar"""
win32functions.ShowWindow(self, win32defines.SW_HIDE)
win32functions.SetWindowLongPtr(self, win32defines.GWL_EXSTYLE, self.exstyle() | win32defines.WS_EX_TOOLWINDOW)
win32functions.ShowWindow(self, win32defines.SW_SHOW)
# Non PEP-8 alias
HideFromTaskbar = deprecated(hide_from_taskbar)
#-----------------------------------------------------------
def show_in_taskbar(self):
"""Show the dialog in the Windows taskbar"""
win32functions.ShowWindow(self, win32defines.SW_HIDE)
win32functions.SetWindowLongPtr(self, win32defines.GWL_EXSTYLE,
self.exstyle() | win32defines.WS_EX_APPWINDOW)
win32functions.ShowWindow(self, win32defines.SW_SHOW)
# Non PEP-8 alias
ShowInTaskbar = deprecated(show_in_taskbar)
#-----------------------------------------------------------
def is_in_taskbar(self):
"""Check whether the dialog is shown in the Windows taskbar
Thanks to David Heffernan for the idea:
http://stackoverflow.com/questions/30933219/hide-window-from-taskbar-without-using-ws-ex-toolwindow
A window is represented in the taskbar if:
It has no owner and it does not have the WS_EX_TOOLWINDOW extended style,
or it has the WS_EX_APPWINDOW extended style.
"""
return self.has_exstyle(win32defines.WS_EX_APPWINDOW) or \
(self.owner() is None and not self.has_exstyle(win32defines.WS_EX_TOOLWINDOW))
# Non PEP-8 alias
IsInTaskbar = deprecated(is_in_taskbar)
#-----------------------------------------------------------
def force_close(self):
"""Close the dialog forcefully using WM_QUERYENDSESSION and return the result
Window has let us know that it doesn't want to die - so we abort
this means that the app is not hung - but knows it doesn't want
to close yet - e.g. it is asking the user if they want to save.
"""
self.send_message_timeout(
win32defines.WM_QUERYENDSESSION,
timeout = .5,
timeoutflags = (win32defines.SMTO_ABORTIFHUNG)) # |
#win32defines.SMTO_NOTIMEOUTIFNOTHUNG)) # |
#win32defines.SMTO_BLOCK)
# get a handle we can wait on
_, pid = win32process.GetWindowThreadProcessId(int(self.handle))
try:
process_wait_handle = win32api.OpenProcess(
win32con.SYNCHRONIZE | win32con.PROCESS_TERMINATE,
0,
pid)
except win32gui.error:
return True # already closed
result = win32event.WaitForSingleObject(
process_wait_handle,
int(Timings.after_windowclose_timeout * 1000))
return result != win32con.WAIT_TIMEOUT
# #-----------------------------------------------------------
# def read_controls_from_xml(self, filename):
# from pywinauto import xml_helpers
# [controlproperties.ControlProps(ctrl) for
# ctrl in xml_helpers.ReadPropertiesFromFile(handle)]
# # Non PEP-8 alias
# ReadControlsFromXML = deprecated(read_controls_from_xml)
# #-----------------------------------------------------------
# def add_reference(self, reference):
#
# if len(self.children() != len(reference)):
# raise "different number of reference controls"
#
# for i, ctrl in enumerate(reference):
# # loop over each of the controls
# # and set the reference
# if isinstance(ctrl, dict):
# ctrl = CtrlProps(ctrl)
#
# self.
# if ctrl.class_name() != self.children()[i+1].class_name():
# print "different classes"
# # Non PEP-8 alias
# AddReference = deprecated(add_reference)
#====================================================================
def _perform_click(
ctrl,
button = "left",
pressed = "",
coords = (0, 0),
double = False,
button_down = True,
button_up = True,
absolute = False,
):
"""Low level method for performing click operations"""
if ctrl is None:
ctrl = HwndWrapper(win32functions.GetDesktopWindow())
ctrl.verify_actionable()
ctrl_text = ctrl.window_text()
if ctrl_text is None:
ctrl_text = six.text_type(ctrl_text)
ctrl_friendly_class_name = ctrl.friendly_class_name()
if isinstance(coords, win32structures.RECT):
coords = coords.mid_point()
# allow points objects to be passed as the coords
elif isinstance(coords, win32structures.POINT):
coords = [coords.x, coords.y]
else:
coords = list(coords)
if absolute:
coords = ctrl.client_to_screen(coords)
# figure out the messages for click/press
msgs = []
if not double:
if button.lower() == 'left':
if button_down:
msgs.append(win32defines.WM_LBUTTONDOWN)
if button_up:
msgs.append(win32defines.WM_LBUTTONUP)
elif button.lower() == 'middle':
if button_down:
msgs.append(win32defines.WM_MBUTTONDOWN)
if button_up:
msgs.append(win32defines.WM_MBUTTONUP)
elif button.lower() == 'right':
if button_down:
msgs.append(win32defines.WM_RBUTTONDOWN)
if button_up:
msgs.append(win32defines.WM_RBUTTONUP)
elif button.lower() == 'move':
msgs.append(win32defines.WM_MOUSEMOVE)
# figure out the messages for double clicking
else:
if button.lower() == 'left':
msgs = (
win32defines.WM_LBUTTONDOWN,
win32defines.WM_LBUTTONUP,
win32defines.WM_LBUTTONDBLCLK,
win32defines.WM_LBUTTONUP)
elif button.lower() == 'middle':
msgs = (
win32defines.WM_MBUTTONDOWN,
win32defines.WM_MBUTTONUP,
win32defines.WM_MBUTTONDBLCLK,
win32defines.WM_MBUTTONUP)
elif button.lower() == 'right':
msgs = (
win32defines.WM_RBUTTONDOWN,
win32defines.WM_RBUTTONUP,
win32defines.WM_RBUTTONDBLCLK,
win32defines.WM_RBUTTONUP)
elif button.lower() == 'move':
msgs.append(win32defines.WM_MOUSEMOVE)
# figure out the flags and pack coordinates
flags, click_point = _calc_flags_and_coords(pressed, coords)
#control_thread = win32functions.GetWindowThreadProcessId(ctrl, 0)
#win32functions.AttachThreadInput(win32functions.GetCurrentThreadId(), control_thread, win32defines.TRUE)
# TODO: check return value of AttachThreadInput properly
# send each message
for msg in msgs:
win32functions.PostMessage(ctrl, msg, win32structures.WPARAM(flags), win32structures.LPARAM(click_point))
#ctrl.post_message(msg, flags, click_point)
#flags = 0
time.sleep(Timings.sendmessagetimeout_timeout)
# wait until the thread can accept another message
win32functions.WaitGuiThreadIdle(ctrl)
# detach the Python process with the process that self is in
#win32functions.AttachThreadInput(win32functions.GetCurrentThreadId(), control_thread, win32defines.FALSE)
# TODO: check return value of AttachThreadInput properly
# wait a certain(short) time after the click
time.sleep(Timings.after_click_wait)
if button.lower() == 'move':
message = 'Moved mouse over ' + ctrl_friendly_class_name + ' "' + ctrl_text + \
'" to screen point ' + str(tuple(coords)) + ' by WM_MOUSEMOVE'
else:
message = 'Clicked ' + ctrl_friendly_class_name + ' "' + ctrl_text + \
'" by ' + str(button) + ' button event ' + str(tuple(coords))
if double:
message = 'Double-c' + message[1:]
ActionLogger().log(message)
_mouse_flags = {
"left": win32defines.MK_LBUTTON,
"right": win32defines.MK_RBUTTON,
"middle": win32defines.MK_MBUTTON,
"shift": win32defines.MK_SHIFT,
"control": win32defines.MK_CONTROL,
}
#====================================================================
def _calc_flags_and_coords(pressed, coords):
"""Calculate the flags to use and the coordinates for mouse actions"""
flags = 0
for key in pressed.split():
flags |= _mouse_flags[key.lower()]
click_point = win32functions.MakeLong(coords[1], coords[0])
return flags, click_point
#====================================================================
class _DummyControl(dict):
"""A subclass of dict so that we can assign attributes"""
pass
#====================================================================
def get_dialog_props_from_handle(hwnd):
"""Get the properties of all the controls as a list of dictionaries"""
# wrap the dialog handle and start a new list for the
# controls on the dialog
try:
controls = [hwnd, ]
controls.extend(hwnd.children())
except AttributeError:
controls = [HwndWrapper(hwnd), ]
# add all the children of the dialog
controls.extend(controls[0].children())
props = []
# Add each control to the properties for this dialog
for ctrl in controls:
# Get properties for each control and wrap them in
# _DummyControl so that we can assign handle
ctrl_props = _DummyControl(ctrl.get_properties())
# assign the handle
ctrl_props.handle = ctrl.handle
# offset the rectangle from the dialog rectangle
ctrl_props['rectangle'] -= controls[0].rectangle()
props.append(ctrl_props)
return props
# Non PEP-8 alias
GetDialogPropsFromHandle = deprecated(get_dialog_props_from_handle)
backend.register('win32', HwndElementInfo, HwndWrapper)
backend.registry.backends['win32'].dialog_class = DialogWrapper
backend.activate('win32') # default