/pywinauto/controls/HwndWrapper.py
https://bitbucket.org/adrianer/pywinauto · Python · 1720 lines · 1383 code · 129 blank · 208 comment · 34 complexity · 6cd38a73460f82ece9166c4d2ef85735 MD5 · raw file
Large files are truncated click here to view the full file
- # GUI Application automation and testing library
- # Copyright (C) 2006 Mark Mc Mahon
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public License
- # as published by the Free Software Foundation; either version 2.1
- # of the License, or (at your option) any later version.
- #
- # This library is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- # See the GNU Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public
- # License along with this library; if not, write to the
- # Free Software Foundation, Inc.,
- # 59 Temple Place,
- # Suite 330,
- # Boston, MA 02111-1307 USA
- "Basic wrapping of Windows controls"
- __revision__ = "$Revision$"
- # pylint: disable-msg=W0611
- import time
- import re
- import ctypes
- # 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 pywinauto import SendKeysCtypes as SendKeys
- # I leave this optional because PIL is a large dependency
- try:
- import ImageGrab
- except ImportError:
- ImageGrab = None
- from pywinauto import win32defines
- from pywinauto import win32functions
- from pywinauto import win32structures
- from pywinauto.timings import Timings
- from pywinauto import timings
- #from pywinauto import findbestmatch
- from pywinauto import handleprops
- # also import MenuItemNotEnabled so that it is
- # accessible from HwndWrapper module
- from menuwrapper import Menu #, MenuItemNotEnabled
-
- #====================================================================
- class ControlNotEnabled(RuntimeError):
- "Raised when a control is not enabled"
- pass
- #====================================================================
- class ControlNotVisible(RuntimeError):
- "Raised when a control is nto 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 0x%d is not a vaild window handle"% hwnd)
- # metaclass that will know about
- class _MetaWrapper(type):
- "Metaclass for Wrapper objects"
- re_wrappers = {}
- str_wrappers = {}
- def __init__(cls, name, bases, attrs):
- # register the class names, both the regular expression
- # or the classes directly
- #print "metaclass __init__", cls
- type.__init__(cls, name, bases, attrs)
- for win_class in cls.windowclasses:
- _MetaWrapper.re_wrappers[re.compile(win_class)] = cls
- _MetaWrapper.str_wrappers[win_class] = cls
- def FindWrapper(handle):
- """Find the correct wrapper for this handle"""
- class_name = handleprops.classname(handle)
- try:
- return _MetaWrapper.str_wrappers[class_name]
- except KeyError:
- wrapper_match = None
- for regex, wrapper in _MetaWrapper.re_wrappers.items():
- if regex.match(class_name):
- wrapper_match = wrapper
- _MetaWrapper.str_wrappers[class_name] = wrapper
- break
- # if it is a dialog then override the wrapper we found
- # and make it a DialogWrapper
- if handleprops.is_toplevel_window(handle):
- import win32_controls
- wrapper_match = win32_controls.DialogWrapper
- if wrapper_match is None:
- wrapper_match = HwndWrapper
- return wrapper_match
- #if handle in meta.wrappers:
- # return meta.wrappers[handle]
- FindWrapper = staticmethod(FindWrapper)
- #====================================================================
- class HwndWrapper(object):
- """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.
- A 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_)"""
- __metaclass__ = _MetaWrapper
- friendlyclassname = None
- windowclasses = []
- handle = None
- can_be_label = False
- has_title = True
- #-----------------------------------------------------------
- def __new__(cls, handle):
- # only use the meta class to find the wrapper for HwndWrapper
- # so allow users to force the wrapper if they want
- # thanks to Raghav for finding this.
- if cls != HwndWrapper:
- obj = object.__new__(cls)
- obj.__init__(handle)
- return obj
- new_class = cls.FindWrapper(handle)
- #super(currentclass, cls).__new__(cls[, ...])"
- obj = object.__new__(new_class)
- obj.__init__(handle)
- return obj
- #-----------------------------------------------------------
- def __init__(self, hwnd):
- """Initialize the control
- * **hwnd** is either a valid window handle or it can be an
- instance or subclass of HwndWrapper.
- If the handle is not valid then an InvalidWindowHandle error
- is raised.
- """
- # handle if hwnd is actually a HwndWrapper
- try:
- self.handle = hwnd.handle
- except AttributeError:
- self.handle = hwnd
- # verify that we have been passed in a valid windows handle
- if not win32functions.IsWindow(hwnd):
- raise InvalidWindowHandle(hwnd)
- # make it so that ctypes conversion happens correctly
- self._as_parameter_ = self.handle
- #win32functions.WaitGuiThreadIdle(self)
- # specify whether we need to grab an image of ourselves
- # when asked for properties
- self._NeedsImageProp = False
- # default to not having a reference control added
- self.ref = None
- self.appdata = None
- self._cache = {}
- # build the list of default properties to be written
- # Derived classes can either modify this list or override
- # GetProperties depending on how much control they need.
- self.writable_props = [
- 'Class',
- 'FriendlyClassName',
- 'Texts',
- 'Style',
- 'ExStyle',
- 'ControlID',
- 'UserData',
- 'ContextHelpID',
- 'Fonts',
- 'ClientRects',
- 'Rectangle',
- 'IsVisible',
- 'IsUnicode',
- 'IsEnabled',
- 'MenuItems',
- 'ControlCount',
- ]
- #-----------------------------------------------------------
- def FriendlyClassName(self):
- """Return the friendly class name for the control
- This differs from the class of the control in some cases.
- Class() is the actual 'Registered' window class of the control
- while FriendlyClassName() is hopefully something that will make
- more sense to the user.
- For example Checkboxes are implemented as Buttons - so the class
- of a CheckBox is "Button" - but the friendly class is "CheckBox"
- """
- if self.friendlyclassname is None:
- self.friendlyclassname = handleprops.classname(self)
- return self.friendlyclassname
- #-----------------------------------------------------------
- def Class(self):
- """Return the class name of the window"""
- if not self._cache.has_key("class"):
- self._cache['class'] = handleprops.classname(self)
- return self._cache['class']
- #-----------------------------------------------------------
- def WindowText(self):
- """Window text of the control
- Quite a few contorls have other text that is visible, for example
- Edit controls usually have an empty string for WindowText but still
- have text displayed in the edit window.
- """
- return handleprops.text(self)
- #-----------------------------------------------------------
- def Style(self):
- """Returns the style of window
- Return value is a long.
- Combination of WS_* and specific control specific styles.
- See HwndWrapper.HasStyle() to easily check if the window has a
- particular style.
- """
- return handleprops.style(self)
- #-----------------------------------------------------------
- def ExStyle(self):
- """Returns the Extended style of window
- Return value is a long.
- Combination of WS_* and specific control specific styles.
- See HwndWrapper.HasStyle() to easily check if the window has a
- particular style.
- """
- return handleprops.exstyle(self)
- #-----------------------------------------------------------
- def ControlID(self):
- """Return the ID of the window
- Only controls have a valid ID - dialogs usually have no ID assigned.
- The ID usually identified the control in the window - but there can
- be duplicate ID's for example lables in a dialog may have duplicate
- ID's.
- """
- return handleprops.controlid(self)
- #-----------------------------------------------------------
- def UserData(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)
- #-----------------------------------------------------------
- def ContextHelpID(self):
- "Return the Context Help ID of the window"
- return handleprops.contexthelpid(self)
- #-----------------------------------------------------------
- def IsUnicode(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)
- #-----------------------------------------------------------
- def IsVisible(self):
- """Whether the window is visible or not
- Checks that both the Top Level Parent (probably dialog) that
- owns this window and the window itself are both visible.
- If you want to wait for a control to become visible (or wait
- for it to become hidden) use ``Application.Wait('visible')`` or
- ``Application.WaitNot('visible')``.
- If you want to raise an exception immediately if a window is
- not visible then you can use the HwndWrapper.VerifyVisible().
- HwndWrapper.VerifyActionable() raises if the window is not both
- visible and enabled.
- """
- return handleprops.isvisible(self.TopLevelParent()) and \
- handleprops.isvisible(self)
- #-----------------------------------------------------------
- def IsEnabled(self):
- """Whether the window is enabled or not
- Checks that both the Top Level Parent (probably dialog) that
- owns this window and the window itself are both enabled.
- If you want to wait for a control to become enabled (or wait
- for it to become disabled) use ``Application.Wait('visible')`` or
- ``Application.WaitNot('visible')``.
- If you want to raise an exception immediately if a window is
- not enabled then you can use the HwndWrapper.VerifyEnabled().
- HwndWrapper.VerifyReady() raises if the window is not both
- visible and enabled.
- """
- return handleprops.isenabled(self.TopLevelParent()) and \
- handleprops.isenabled(self)
- #-----------------------------------------------------------
- def Rectangle(self):
- """Return the rectangle of window
- The rectangle is the rectangle of the control on the screen,
- coordinates are given from the top left of the screen.
- 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.rectangle(self)
- #-----------------------------------------------------------
- def ClientRect(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)
- #-----------------------------------------------------------
- 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)
- #-----------------------------------------------------------
- def ProcessID(self):
- """Return the ID of process that owns this window"""
- return handleprops.processid(self)
- #-----------------------------------------------------------
- def HasStyle(self, style):
- "Return True if the control has the specified sytle"
- return handleprops.has_style(self, style)
- #-----------------------------------------------------------
- def HasExStyle(self, exstyle):
- "Return True if the control has the specified extended sytle"
- return handleprops.has_exstyle(self, exstyle)
- #-----------------------------------------------------------
- def IsDialog(self):
- "Return true if the control is a top level window"
- if not self._cache.has_key("isdialog"):
- self._cache['isdialog'] = handleprops.is_toplevel_window(self)
- return self._cache['isdialog']
- #-----------------------------------------------------------
- def Parent(self):
- """Return the parent of this control
- Note that the parent of a control is not necesarily a dialog or
- other main window. A group box may be the parent of some radio
- buttons for example.
- To get the main (or top level) window then use
- HwndWrapper.TopLevelParent().
- """
- if not self._cache.has_key("parent"):
- parent_hwnd = handleprops.parent(self)
- if parent_hwnd:
- #return WrapHandle(parent_hwnd)
- self._cache["parent"] = HwndWrapper(parent_hwnd)
- else:
- self._cache["parent"] = None
- return self._cache["parent"]
- #-----------------------------------------------------------
- def TopLevelParent(self):
- """Return the top level window of this control
- The TopLevel parent is different from the parent in that the Parent
- is the window that owns this window - but it may not be a dialog/main
- window. For example most Comboboxes have an Edit. The ComboBox is the
- parent of the Edit control.
- This will always return a valid window handle (if the control has
- no top level parent then the control itself is returned - as it is
- a top level window already!)
- """
- if not self._cache.has_key("top_level_parent"):
- parent = self.Parent()
- if self.IsDialog():
- self._cache["top_level_parent"] = self
- #return self
- elif not parent:
- self._cache["top_level_parent"] = self
- #return self
- elif not parent.IsDialog():
- self._cache["top_level_parent"] = parent.TopLevelParent()
- #return parent.TopLevelParent()
- else:
- self._cache["top_level_parent"] = parent
- #return parent
- return self._cache["top_level_parent"]
- #-----------------------------------------------------------
- def Texts(self):
- """Return the text for each item of this control"
- It is a list of strings for the control. It is frequently over-ridden
- to extract all strings from a control with multiple items.
- It is always a list with one or more strings:
- * First elemtent is the window text of the control
- * Subsequent elements contain the text of any items of the
- control (e.g. items in a listbox/combobox, tabs in a tabcontrol)
- """
- texts = [self.WindowText(), ]
- return texts
- #-----------------------------------------------------------
- def ClientRects(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.ClientRect(), ]
- #-----------------------------------------------------------
- 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(), ]
- #-----------------------------------------------------------
- def Children(self):
- """Return the children of this control as a list
- It returns a list of HwndWrapper (or subclass) instances, it
- returns an empty list if there are no children.
- """
- child_windows = handleprops.children(self)
- return [HwndWrapper(hwnd) for hwnd in child_windows]
- #-----------------------------------------------------------
- def ControlCount(self):
- "Return the number of children of this control"
- return len(handleprops.children(self))
- #-----------------------------------------------------------
- def IsChild(self, parent):
- """Return True if this window is a child of 'parent'.
- A window is a child of another window when it is a direct of the
- other window. A window is a direct descendant of a given
- window if the parent window is the the chain of parent windows
- for the child window.
- """
- # Call the IsChild API funciton and convert the result
- # to True/False
- return win32functions.IsChild(parent, self.handle) != 0
- #-----------------------------------------------------------
- def SendMessage(self, message, wparam = 0 , lparam = 0):
- "Send a message to the control and wait for it to return"
- return win32functions.SendMessage(self, message, wparam, lparam)
- #result = ctypes.c_long()
- #ret = win32functions.SendMessageTimeout(self, message, wparam, lparam,
- # win32defines.SMTO_NORMAL, 400, ctypes.byref(result))
- #return result.value
- #-----------------------------------------------------------
- def SendMessageTimeout(
- 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 .4 of a second will
- be used.
- """
- if timeout is None:
- timeout = Timings.sendmessagetimeout_timeout
- result = ctypes.c_long()
- win32functions.SendMessageTimeout(self,
- message, wparam, lparam,
- timeoutflags, int(timeout * 1000),
- ctypes.byref(result))
- return result.value
- #-----------------------------------------------------------
- def PostMessage(self, message, wparam = 0 , lparam = 0):
- "Post a message to the control message queue and return"
- return win32functions.PostMessage(self, message, wparam, lparam)
- #result = ctypes.c_long()
- #ret = win32functions.SendMessageTimeout(self, message, wparam, lparam,
- # win32defines.SMTO_NORMAL, 400, ctypes.byref(result))
- #return result.value
- # #-----------------------------------------------------------
- # def NotifyMenuSelect(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.SetFocus()
- #
- # msg = win32defines.WM_COMMAND
- # return self.SendMessageTimeout(
- # msg,
- # win32functions.MakeLong(0, menu_id), #wparam
- # )
- #
- #-----------------------------------------------------------
- def NotifyParent(self, message, controlID = None):
- "Send the notification message to parent of this control"
- if controlID is None:
- controlID = self.ControlID()
- return self.Parent().PostMessage(
- win32defines.WM_COMMAND,
- win32functions.MakeLong(message, controlID),
- self)
- #-----------------------------------------------------------
- def GetProperties(self):
- "Return the properties of the control as a dictionary"
- props = {}
- # for each of the properties that can be written out
- for propname in self.writable_props:
- # set the item in the props dictionary keyed on the propname
- props[propname] = getattr(self, propname)()
- if self._NeedsImageProp:
- props["Image"] = self.CaptureAsImage()
- return props
- #-----------------------------------------------------------
- def CaptureAsImage(self):
- """Return a PIL image of the control
- See PIL documentation to know what you can do with the resulting
- image"""
-
- if not (self.Rectangle().width() and self.Rectangle().height()):
- return None
- # PIL is optional so check first
- if not ImageGrab:
- print("PIL does not seem to be installed. "
- "PIL is required for CaptureAsImage")
- return None
- # get the control rectangle in a way that PIL likes it
- box = (
- self.Rectangle().left,
- self.Rectangle().top,
- self.Rectangle().right,
- self.Rectangle().bottom)
- # grab the image and get raw data as a string
- return ImageGrab.grab(box)
- #-----------------------------------------------------------
- def __hash__(self):
- "Returns the hash value of the handle"
- return hash(self.handle)
- #-----------------------------------------------------------
- def __eq__(self, other):
- "Returns True if the handles of both controls are the same"
- if isinstance(other, HwndWrapper):
- return self.handle == other.handle
- else:
- return self.handle == other
- #-----------------------------------------------------------
- def VerifyActionable(self):
- """Verify that the control is both visible and enabled
- Raise either ControlNotEnalbed or ControlNotVisible if not
- enabled or visible respectively.
- """
- win32functions.WaitGuiThreadIdle(self)
- self.VerifyVisible()
- self.VerifyEnabled()
- #-----------------------------------------------------------
- def VerifyEnabled(self):
- """Verify that the control is enabled
- Check first if the control's parent is enabled (skip if no parent),
- then check if control itself is enabled.
- """
- # Check if the control and it's parent are enabled
- if not self.IsEnabled():
- raise ControlNotEnabled()
- #-----------------------------------------------------------
- def VerifyVisible(self):
- """Verify that the control is visible
- Check first if the control's parent is visible. (skip if no parent),
- then check if control itself is visible.
- """
- # check if the control and it's parent are visible
- if not self.IsVisible():
- raise ControlNotVisible()
- #-----------------------------------------------------------
- def Click(
- self, button = "left", pressed = "", coords = (0, 0), double = False):
- """Simulates a mouse click on the control
- This method sends WM_* messages to the control, to do a more
- 'realistic' mouse click use ClickInput() which uses SendInput() API
- to perform the click.
- This method does not require that the control be visible on the screen
- (i.e. is can be hidden beneath another window and it will still work.)
- """
- _perform_click(self, button, pressed, coords, double)
- return self
- #-----------------------------------------------------------
- def ClickInput(
- self,
- button = "left",
- coords = (None, None),
- double = False,
- wheel_dist = 0):
- """Click at the specified coordinates
- * **button** The mouse button to click. One of 'left', 'right',
- 'middle' or 'x' (Default: 'left')
- * **coords** The coordinates to click at.(Default: center of control)
- * **double** Whether to perform a double click or not (Default: False)
- * **wheel_dist** The distance to move the mouse week (default: 0)
- NOTES:
- This is different from Click in that it requires the control to
- be visible on the screen but performs a more realistic 'click'
- simulation.
- This method is also vulnerable if the mouse if moved by the user
- as that could easily move the mouse off the control before the
- Click has finished.
- """
- _perform_click_input(
- self, button, coords, double, wheel_dist = wheel_dist)
- #-----------------------------------------------------------
- def CloseClick(
- self, button = "left", pressed = "", coords = (0, 0), double = False):
- """Peform 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():
- return not (
- win32functions.IsWindow(self) or
- win32functions.IsWindow(self.Parent()))
- # Keep waiting until both this control and it's parent
- # are no longer valid controls
- timings.WaitUntil(
- Timings.closeclick_dialog_close_wait,
- Timings.closeclick_retry,
- has_closed
- )
- time.sleep(Timings.after_closeclick_wait)
- return self
- #-----------------------------------------------------------
- def DoubleClick(
- self, button = "left", pressed = "", coords = (0, 0)):
- "Perform a double click action"
- _perform_click(self, button, pressed, coords, double = True)
- return self
- #-----------------------------------------------------------
- def DoubleClickInput(self, button = "left", coords = (None, None)):
- "Double click at the specified coordinates"
- _perform_click_input(self, button, coords, double = True)
- #-----------------------------------------------------------
- def RightClick(
- 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
- #-----------------------------------------------------------
- def RightClickInput(self, coords = (None, None)):
- "Right click at the specified coords"
- _perform_click_input(self, 'right', coords)
- #-----------------------------------------------------------
- def PressMouse(self, button = "left", pressed = "", coords = (0, 0)):
- "Press the mouse button"
- #flags, click_point = _calc_flags_and_coords(pressed, coords)
- _perform_click(self, button, pressed, coords, button_up = False)
- return self
- #-----------------------------------------------------------
- def PressMouseInput(self, button = "left", coords = (None, None)):
- "Press a mouse button using SendInput"
- _perform_click_input(self, button, coords, button_up = False)
- #-----------------------------------------------------------
- def ReleaseMouse(self, button = "left", pressed = "", coords = (0, 0)):
- "Release the mouse button"
- #flags, click_point = _calc_flags_and_coords(pressed, coords)
- _perform_click(self, button, pressed, coords, button_down = False)
- return self
- #-----------------------------------------------------------
- def ReleaseMouseInput(self, button = "left", coords = (None, None)):
- "Release the mouse button"
- _perform_click_input(self, button, coords, button_down = False)
- #-----------------------------------------------------------
- def MoveMouse(self, pressed = "left", coords = (0, 0)):
- "Move the mouse"
- flags, click_point = _calc_flags_and_coords(pressed, coords)
- self.SendMessageTimeout(win32defines.WM_MOUSEMOVE, flags, click_point)
- win32functions.WaitGuiThreadIdle(self)
- return self
- #-----------------------------------------------------------
- def DragMouse(self,
- button = "left",
- pressed = "",
- press_coords = (0, 0),
- release_coords = (0, 0)):
- "Drag the mouse"
- self.PressMouse(button, pressed, press_coords)
- self.MoveMouse(pressed, press_coords)
- self.ReleaseMouse(button, pressed, release_coords)
- return self
- #-----------------------------------------------------------
- def SetWindowText(self, text, append = False):
- "Set the text of the window"
- self.VerifyActionable()
- if append:
- text = self.WindowText() + text
- text = ctypes.c_wchar_p(unicode(text))
- self.PostMessage(win32defines.WM_SETTEXT, 0, text)
- win32functions.WaitGuiThreadIdle(self)
- return self
- #-----------------------------------------------------------
- def TypeKeys(
- self,
- keys,
- pause = None,
- with_spaces = False,
- with_tabs = False,
- with_newlines = False,
- turn_off_numlock = True):
- """Type keys to the window using SendKeys
- This uses the SendKeys python module from
- http://www.rutherfurd.net/python/sendkeys/ .This is the best place
- to find documentation on what to use for the ``keys``
- """
- self.VerifyActionable()
- if pause is None:
- pause = Timings.after_sendkeys_key_wait
- self.SetFocus()
- # attach the Python process with the process that self is in
- win32functions.AttachThreadInput(
- win32functions.GetCurrentThreadId(), self.ProcessID(), 1)
- # make sure that the control is in the foreground
- win32functions.SetForegroundWindow(self)
- #win32functions.SetActiveWindow(self)
- # Play the keys to the active window
- SendKeys.SendKeys(
- keys,
- pause, with_spaces,
- with_tabs,
- with_newlines,
- turn_off_numlock)
- # detach the python process from the window's process
- win32functions.AttachThreadInput(
- win32functions.GetCurrentThreadId(), self.ProcessID(), 0)
- win32functions.WaitGuiThreadIdle(self)
- return self
- #-----------------------------------------------------------
- def DebugMessage(self, text):
- "Write some debug text over the window"
- # don't draw if dialog is not visible
- dc = win32functions.CreateDC(u"DISPLAY", None, None, None )
- if not dc:
- raise ctypes.WinError()
- rect = self.Rectangle
- #ret = win32functions.TextOut(
- # dc, rect.left, rect.top, unicode(text), len(text))
- ret = win32functions.DrawText(
- dc,
- unicode(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
- #-----------------------------------------------------------
- def DrawOutline(
- self,
- colour = 'green',
- thickness = 2,
- fill = win32defines.BS_NULL,
- rect = None):
- """Draw an outline around the window
- * **colour** can be either an integer or one of 'red', 'green', 'blue'
- (default 'green')
- * **thickness** thickness of rectangle (default 2)
- * **fill** how to fill in the rectangle (default BS_NULL)
- * **rect** the coordinates of the rectangle to draw (defaults to
- the rectangle of the control.
- """
- # don't draw if dialog is not visible
- if not self.IsVisible():
- return
- colours = {
- "green" : 0x00ff00,
- "blue" : 0xff0000,
- "red" : 0x0000ff,
- }
- # if it's a known colour
- if colour in colours:
- colour = colours[colour]
- if not rect:
- rect = self.Rectangle()
- # create the pen(outline)
- pen_handle = win32functions.CreatePen(
- win32defines.PS_SOLID, thickness, colour)
- # create the brush (inside)
- brush = win32structures.LOGBRUSH()
- brush.lbStyle = fill
- brush.lbHatch = win32defines.HS_DIAGCROSS
- brush_handle = win32functions.CreateBrushIndirect(ctypes.byref(brush))
- # get the Device Context
- dc = win32functions.CreateDC(u"DISPLAY", None, None, None )
- # push our objects into it
- win32functions.SelectObject(dc, brush_handle)
- win32functions.SelectObject(dc, pen_handle)
- # draw the rectangle to the DC
- win32functions.Rectangle(
- dc, rect.left, rect.top, rect.right, rect.bottom)
- # Delete the brush and pen we created
- win32functions.DeleteObject(brush_handle)
- win32functions.DeleteObject(pen_handle)
- # delete the Display context that we created
- win32functions.DeleteDC(dc)
- #-----------------------------------------------------------
- def PopupWindow(self):
- """Return any owned Popups
- Please do not use in production code yet - not tested fully
- """
- popup = win32functions.GetWindow(self, win32defines.GW_HWNDNEXT)
- return popup
- #-----------------------------------------------------------
- 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
- #-----------------------------------------------------------
- # def ContextMenuSelect(self, path, x = None, y = None):
- # "TODO ContextMenuSelect Not Implemented"
- # pass
- # #raise NotImplementedError(
- # # "HwndWrapper.ContextMenuSelect not implemented yet")
- #-----------------------------------------------------------
- def _menu_handle(self):
- "Simple Overridable method to get the menu handle"
- return win32functions.GetMenu(self)
- #-----------------------------------------------------------
- def Menu(self):
- "Return the menu of the control"
- menu_hwnd = self._menu_handle()
- if menu_hwnd: # and win32functions.IsMenu(menu_hwnd):
- return Menu(self, menu_hwnd)
- return None
- #-----------------------------------------------------------
- def MenuItem(self, path):
- """Return the menu item specifed 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['MenuItems']
- else:
- menu_appdata = None
- menu = self.Menu()
- if menu:
- return self.Menu().GetMenuPath(path, appdata = menu_appdata)[-1]
- raise RuntimeError("There is no menu.")
- #-----------------------------------------------------------
- def MenuItems(self):
- """Return the menu items for the dialog
- If there are no menu items then return an empty list
- """
- if self.IsDialog() and self.Menu():
- #menu_handle = win32functions.GetMenu(self)
- #self.SendMessage(win32defines.WM_INITMENU, menu_handle)
- return self.Menu().GetProperties()['MenuItems']
- #self.SendMessage(win32defines.WM_INITMENU, menu_handle)
- #return _GetMenuItems(menu_handle, self)
- else:
- return []
- # #-----------------------------------------------------------
- # def MenuClick(self, path):
- # "Select the menuitem specifed in path"
- #
- # self.VerifyActionable()
- #
- # self.SetFocus()
- #
- # menu = Menu(self, self._menu_handle())
- #
- # path_items = menu.GetMenuPath(path)
- #
- # for menu_item in path_items:
- # if not menu_item.IsEnabled():
- # raise MenuItemNotEnabled(
- # "MenuItem '%s' is disabled"% menu_item.Text())
- #
- # menu_item.Click()
- #
- # return self
- #-----------------------------------------------------------
- def MenuSelect(self, path, ):
- "Select the menuitem specifed in path"
- self.VerifyActionable()
- self.MenuItem(path).Select()
- #-----------------------------------------------------------
- def MoveWindow(
- 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)
- #-----------------------------------------------------------
- def Close(self):
- """Close the window
- Code modified from http://msdn.microsoft.com/msdnmag/issues/02/08/CQA/
- """
- # tell the window it must close
- self.PostMessage(win32defines.WM_CLOSE)
- 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.IsVisible())
- # Keep waiting until both this control and it's parent
- # are no longer valid controls
- timings.WaitUntil(
- Timings.closeclick_dialog_close_wait,
- Timings.closeclick_retry,
- has_closed
- )
- #-----------------------------------------------------------
- def Maximize(self):
- """Maximize the window"""
- win32functions.ShowWindow(self, win32defines.SW_MAXIMIZE)
- #-----------------------------------------------------------
- def Minimize(self):
- """Minimize the window"""
- win32functions.ShowWindow(self, win32defines.SW_MINIMIZE)
- #-----------------------------------------------------------
- def Restore(self):
- """Restore the window"""
- # do it twice just in case the window was minimized from being
- # maximized - because then the window would come up maximized
- # after the first ShowWindow, and Restored after the 2nd
- win32functions.ShowWindow(self, win32defines.SW_RESTORE)
- win32functions.ShowWindow(self, win32defines.SW_RESTORE)
- #-----------------------------------------------------------
- def GetShowState(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
- #-----------------------------------------------------------
- def GetFocus(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)
- ret = win32functions.GetGUIThreadInfo(
- win32functions.GetWindowThreadProcessId(self, 0),
- ctypes.byref(gui_info))
- if not ret:
- return None
- return HwndWrapper(gui_info.hwndFocus)
- #-----------------------------------------------------------
- def SetFocus(self):
- """Set the focus to this control
- Bring the window to the foreground first if necessary."""
- # find the current foreground window
- cur_foreground = win32functions.GetForegroundWindow()
- # if it is already foreground then just return
- if self.handle != cur_foreground:
- # get the thread of the window that is in the foreground
- cur_fore_thread = win32functions.GetWindowThreadProcessId(
- cur_foreground, 0)
- # get the thread of the window that we want to be in the foreground
- control_thread = win32functions.GetWindowThreadProcessId(self, 0)
- # if a different thread owns the active window
- if cur_fore_thread != control_thread:
- # Attach the two threads and set the foreground window
- win32functions.AttachThreadInput(
- cur_fore_thread, control_thread, True)
- win32functions.SetForegroundWindow(self)
- # detach the thread again
- win32functions.AttachThreadInput(
- cur_fore_thread, control_thread, False)
- else: # same threads - just set the foreground window
- win32functions.SetForegroundWindow(self)
- # 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
- #-----------------------------------------------------------
- def SetApplicationData(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 langauge
- """
- 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,
- },
- }
- #-----------------------------------------------------------
- def Scroll(self, direction, amount, count = 1):
- """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
- """
- # 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
- scroll_type = \
- HwndWrapper._scroll_types[direction.lower()][amount.lower()]
- # Scroll as often as we have been asked to
- while count > 0:
- self.SendMessage(message, scroll_type)
- count -= 1
- return self
- #
- #def MouseLeftClick():
- # pass
- #def MouseRightClick():
- # pass
- #def MouseDoubleClick():
- # pass
- #def MouseDown():
- # pass
- #def MouseUp():
- # pass
- #def MoveMouse():
- # pass
- #def DragMouse():
- # pass
- #
- #def LeftClick(x, y):
- # win32defines.MOUSEEVENTF_LEFTDOWN
- # win32defines.MOUSEEVENTF_LEFTUP
- #
- # # set the cursor position
- # win32functions.SetCursorPos(x, y)
- # time.sleep(Timings.after_setcursorpos_wait)
- #
- # inp_struct = win32structures.INPUT()
- # inp_struct.type = win32defines.INPUT_MOUSE
- # for event in (win32defines.MOUSEEVENTF_LEFTDOWN, win32defines.MOUSEEVENTF_LEFTUP):
- # inp_struct._.mi.dwFlags = event
- # win32functions.SendInput(
- # 1,
- # ctypes.pointer(inp_struct),
- # ctypes.sizeof(inp_struct))
- #
- # time.sleep(Timings.after_clickinput_wait)
- #====================================================================
- def _perform_click_input(
- ctrl = None,
- button = "left",
- coords = (None, None),
- double = False,
- button_down = True,
- button_up = True,
- absolute = False,
- wheel_dist = 0):
- """Peform a click action using SendInput
- All the *ClickInput() and *MouseInput() methods use this function.
-
- Thanks to a bug report from Tomas Walch (twalch) on sourceforge and code
- seen at http://msdn.microsoft.com/en-us/magazine/cc164126.aspx this
- function now always works the same way whether the mouse buttons are
- swapped or not.
-
- For example if you send a right click to Notepad.Edit - it will always
- bring up a popup menu rather than 'clicking' it.
- """
- # Handle if the mouse buttons are swapped
- if win32functions.GetSystemMetrics(win32defines.SM_SWAPBUTTON):
- if button.lower() == 'left':
- button = 'right'
- else:
- button = 'left'
- events = []
- if button.lower() == 'left':
- if button_down:
- events.append(win32defines.MOUSE…