pyWinAuto: c:\.projects\py_pywinauto\pywinauto\findwindows.py

0001# GUI Application automation and testing library
0002# Copyright (C) 2006 Mark Mc Mahon
0003#
0004# This library is free software; you can redistribute it and/or
0005# modify it under the terms of the GNU Lesser General Public License
0006# as published by the Free Software Foundation; either version 2.1
0007# of the License, or (at your option) any later version.
0008#
0009# This library is distributed in the hope that it will be useful,
0010# but WITHOUT ANY WARRANTY; without even the implied warranty of
0011# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0012# See the GNU Lesser General Public License for more details.
0013#
0014# You should have received a copy of the GNU Lesser General Public
0015# License along with this library; if not, write to the
0016#    Free Software Foundation, Inc.,
0017#    59 Temple Place,
0018#    Suite 330,
0019#    Boston, MA 02111-1307 USA
0020
0021"""Provides functions for iterating and finding windows
0022
0023"""
0024
0025__revision__ = "$Revision: 607 $"
0026
0027import re
0028
0029import ctypes
0030
0031import win32functions
0032import win32structures
0033import handleprops
0034
0035import findbestmatch
0036
0037
0038# currently commented out as finding the best match control
0039# requires that we know the Friendly class name - which is only
0040# known by the control wrappers - and I would prefer to keep
0041# this module from having that dependency.
0042#import findbestmatch
0043
0044#=========================================================================
0045class WindowNotFoundError(Exception):
0046    "No window could be found"
0047    pass
0048
0049#=========================================================================
0050class WindowAmbiguousError(Exception):
0051    "There was more then one window that matched"
0052    pass
0053
0054
0055
0056#=========================================================================
0057def find_window(**kwargs):
0058    """Call findwindows and ensure that only one window is returned
0059
0060    Calls find_windows with exactly the same arguments as it is called with
0061    so please see find_windows for a description of them."""
0062    windows = find_windows(**kwargs)
0063
0064    if not windows:
0065        raise WindowNotFoundError()
0066
0067    if len(windows) > 1:
0068        #for w in windows:
0069        #    print "ambig", handleprops.classname(w), \
0070        #    handleprops.text(w), handleprops.processid(w)
0071        exception =  WindowAmbiguousError(
0072            "There are %d windows that match the criteria %s"% (
0073            len(windows),
0074            unicode(kwargs),
0075            )
0076        )
0077
0078        exception.windows = windows
0079        raise exception
0080
0081    return windows[0]
0082
0083#=========================================================================
0084def find_windows(class_name = None,
0085                class_name_re = None,
0086                parent = None,
0087                process = None,
0088                title = None,
0089                title_re = None,
0090                top_level_only = True,
0091                visible_only = True,
0092                enabled_only = True,
0093                best_match = None,
0094                handle = None,
0095                ctrl_index = None,
0096                predicate_func = None,
0097                active_only = False,
0098    ):
0099    """Find windows based on criteria passed in
0100
0101    Possible values are:
0102
0103    * **class_name**  Windows with this window class
0104    * **class_name_re**  Windows whose class match this regular expression
0105    * **parent**    Windows that are children of this
0106    * **process**   Windows running in this process
0107    * **title**     Windows with this Text
0108    * **title_re**  Windows whose Text match this regular expression
0109    * **top_level_only** Top level windows only (default=True)
0110    * **visible_only**   Visible windows only (default=True)
0111    * **enabled_only**   Enabled windows only (default=True)
0112    * **best_match**  Windows with a title similar to this
0113    * **handle**      The handle of the window to return
0114    * **ctrl_index**  The index of the child window to return
0115    * **active_only**  Active windows only (default=False)
0116    """
0117
0118    # allow a handle to be passed in
0119    # if it is present - just return it
0120    if handle is not None:
0121        return [handle, ]
0122
0123
0124    if top_level_only:
0125        # find the top level windows
0126        windows = enum_windows()
0127
0128        # if we have been given a parent
0129        if parent:
0130            windows = [win for win in windows
0131                if handleprops.parent(win) == parent]
0132
0133    # looking for child windows
0134    else:
0135        # if not given a parent look for all children of the desktop
0136        if not parent:
0137            parent = win32functions.GetDesktopWindow()
0138
0139        # look for all children of that parent
0140        windows = enum_child_windows(parent)
0141
0142        # if the ctrl_index has been specified then just return
0143        # that control
0144        if ctrl_index is not None:
0145            return [windows[ctrl_index]]
0146
0147    if active_only:
0148        if not process:
0149            raise RuntimeError("Can only get active window of a process - "                   "please specify 'process' too")
0151
0152        gui_info = win32structures.GUITHREADINFO()
0153        gui_info.cbSize = ctypes.sizeof(gui_info)
0154
0155        # get the active windows for all windows (not just the process)
0156        ret = win32functions.GetGUIThreadInfo(0, ctypes.byref(gui_info))
0157
0158        if not ret:
0159            raise ctypes.WinError()
0160           #raise RuntimeError("GetGUIThreadInfo returned 0 (failure)")
0161
0162        if gui_info.hwndActive in windows:
0163            windows = [gui_info.hwndActive]
0164        else:
0165            windows = []
0166
0167
0168    if class_name is not None and windows:
0169        windows = [win for win in windows
0170            if class_name == handleprops.classname(win)]
0171
0172    if class_name_re is not None and windows:
0173        class_name_regex = re.compile(class_name_re)
0174        windows = [win for win in windows
0175            if class_name_regex.match(handleprops.classname(win))]
0176
0177    if process is not None and windows:
0178        windows = [win for win in windows
0179            if handleprops.processid(win) == process]
0180
0181    if title is not None and windows:
0182        windows = [win for win in windows
0183            if title == handleprops.text(win)]
0184
0185    elif title_re is not None and windows:
0186        title_regex = re.compile(title_re)
0187        windows = [win for win in windows
0188            if title_regex.match(handleprops.text(win))]
0189
0190    if visible_only and windows:
0191        windows = [win for win in windows if handleprops.isvisible(win)]
0192
0193    if enabled_only and windows:
0194        windows = [win for win in windows if handleprops.isenabled(win)]
0195
0196    if best_match is not None and windows:
0197        from controls import WrapHandle
0198        windows = [WrapHandle(win) for win in windows]
0199        windows = findbestmatch.find_best_control_matches(
0200            best_match, windows)
0201        # convert window back to handle
0202        windows = [win.handle for win in windows]
0203
0204    if predicate_func is not None and windows:
0205        windows = [win for win in windows if predicate_func(win)]
0206
0207    return windows
0208
0209#=========================================================================
0210def enum_windows():
0211    "Return a list of handles of all the top level windows"
0212    windows = []
0213
0214    # The callback function that will be called for each HWND
0215    # all we do is append the wrapped handle
0216    def EnumWindowProc(hwnd, lparam):
0217        "Called for each window - adds handles to a list"
0218        windows.append(hwnd)
0219        return True
0220
0221    # define the type of the child procedure
0222    enum_win_proc = ctypes.WINFUNCTYPE(
0223        ctypes.c_int, ctypes.c_long, ctypes.c_long)
0224
0225    # 'construct' the callback with our function
0226    proc = enum_win_proc(EnumWindowProc)
0227
0228    # loop over all the children (callback called for each)
0229    win32functions.EnumWindows(proc, 0)
0230
0231    # return the collected wrapped windows
0232    return windows
0233
0234
0235#=========================================================================
0236def enum_child_windows(handle):
0237    "Return a list of handles of the child windows of this handle"
0238
0239    # this will be filled in the callback function
0240    child_windows = []
0241
0242    # callback function for EnumChildWindows
0243    def EnumChildProc(hwnd, lparam):
0244        "Called for each child - adds child hwnd to list"
0245
0246        # append it to our list
0247        child_windows.append(hwnd)
0248
0249        # return true to keep going
0250        return True
0251
0252    # define the child proc type
0253    enum_child_proc = ctypes.WINFUNCTYPE(
0254        ctypes.c_int,                   # return type
0255        win32structures.HWND,   # the window handle
0256        win32structures.LPARAM) # extra information
0257
0258    # update the proc to the correct type
0259    proc = enum_child_proc(EnumChildProc)
0260
0261    # loop over all the children (callback called for each)
0262    win32functions.EnumChildWindows(handle, proc, 0)
0263
0264    return child_windows