pyWinAuto: c:\.projects\py_pywinauto\pywinauto\controls\HwndWrapper.py
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021"Basic wrapping of Windows controls"
0022
0023__revision__ = "$Revision: 607 $"
0024
0025
0026
0027import time
0028import re
0029import ctypes
0030
0031
0032
0033
0034try:
0035 import SendKeys
0036except ImportError:
0037 pass
0038
0039
0040try:
0041 import PIL.ImageGrab
0042except ImportError:
0043 pass
0044
0045from pywinauto import win32defines
0046from pywinauto import win32functions
0047from pywinauto import win32structures
0048from pywinauto.timings import Timings
0049
0050
0051from pywinauto import handleprops
0052
0053
0054
0055from menuwrapper import Menu
0056
0057
0058class ControlNotEnabled(RuntimeError):
0059 "Raised when a control is not enabled"
0060 pass
0061
0062
0063class ControlNotVisible(RuntimeError):
0064 "Raised when a control is nto visible"
0065 pass
0066
0067
0068class InvalidWindowHandle(RuntimeError):
0069 "Raised when an invalid handle is passed to HwndWrapper "
0070 def __init__(self, hwnd):
0071 "Initialise the RuntimError parent with the mesage"
0072 RuntimeError.__init__(self,
0073 "Handle 0x%d is not a vaild window handle"% hwnd)
0074
0075
0076
0077
0078
0079class _MetaWrapper(type):
0080 "Metaclass for Wrapper objects"
0081 re_wrappers = {}
0082 str_wrappers = {}
0083
0084 def __init__(cls, name, bases, attrs):
0085
0086
0087
0088
0089 type.__init__(cls, name, bases, attrs)
0090
0091 for win_class in cls.windowclasses:
0092 _MetaWrapper.re_wrappers[re.compile(win_class)] = cls
0093 _MetaWrapper.str_wrappers[win_class] = cls
0094
0095 def FindWrapper(handle):
0096 """Find the correct wrapper for this handle"""
0097 class_name = handleprops.classname(handle)
0098
0099
0100 try:
0101 return _MetaWrapper.str_wrappers[class_name]
0102 except KeyError:
0103 wrapper_match = None
0104
0105 for regex, wrapper in _MetaWrapper.re_wrappers.items():
0106 if regex.match(class_name):
0107 wrapper_match = wrapper
0108 _MetaWrapper.str_wrappers[class_name] = wrapper
0109
0110 break
0111
0112
0113
0114 if handleprops.is_toplevel_window(handle):
0115 import win32_controls
0116 wrapper_match = win32_controls.DialogWrapper
0117
0118 if wrapper_match is None:
0119 wrapper_match = HwndWrapper
0120 return wrapper_match
0121
0122
0123
0124
0125 FindWrapper = staticmethod(FindWrapper)
0126
0127
0128
0129class HwndWrapper(object):
0130 """Default wrapper for controls.
0131
0132 All other wrappers are derived from this.
0133
0134 This class wraps a lot of functionality of underlying windows API
0135 features for working with windows.
0136
0137 Most of the methods apply to every single window type. For example
0138 you can Click() on any window.
0139
0140 Most of the methods of this class are simple wrappers around
0141 API calls and as such they try do the simplest thing possible.
0142
0143 A HwndWrapper object can be passed directly to a ctypes wrapped
0144 C function - and it will get converted to a Long with the value of
0145 it's handle (see ctypes, _as_parameter_)"""
0146
0147 __metaclass__ = _MetaWrapper
0148
0149 friendlyclassname = None
0150 windowclasses = []
0151 handle = None
0152
0153
0154 def __new__(cls, handle):
0155 new_class = cls.FindWrapper(handle)
0156
0157
0158 obj = object.__new__(new_class)
0159 obj.__init__(handle)
0160 return obj
0161
0162
0163
0164 def __init__(self, hwnd):
0165 """Initialize the control
0166
0167 * **hwnd** is either a valid window handle or it can be an
0168 instance or subclass of HwndWrapper.
0169
0170 If the handle is not valid then an InvalidWindowHandle error
0171 is raised.
0172 """
0173
0174
0175 try:
0176 self.handle = hwnd.handle
0177 except AttributeError:
0178 self.handle = hwnd
0179
0180
0181 if not win32functions.IsWindow(hwnd):
0182 raise InvalidWindowHandle(hwnd)
0183
0184
0185 self._as_parameter_ = self.handle
0186
0187
0188
0189
0190
0191 self._NeedsImageProp = False
0192
0193
0194 self.ref = None
0195
0196 self.appdata = None
0197
0198 self._cache = {}
0199
0200
0201
0202
0203 self.writable_props = [
0204 'Class',
0205 'FriendlyClassName',
0206 'Texts',
0207 'Style',
0208 'ExStyle',
0209 'ControlID',
0210 'UserData',
0211 'ContextHelpID',
0212 'Fonts',
0213 'ClientRects',
0214 'Rectangle',
0215 'IsVisible',
0216 'IsUnicode',
0217 'IsEnabled',
0218 'MenuItems',
0219 'ControlCount',
0220 ]
0221
0222
0223
0224 def FriendlyClassName(self):
0225 """Return the friendly class name for the control
0226
0227 This differs from the class of the control in some cases.
0228 Class() is the actual 'Registered' window class of the control
0229 while FriendlyClassName() is hopefully something that will make
0230 more sense to the user.
0231
0232 For example Checkboxes are implemented as Buttons - so the class
0233 of a CheckBox is "Button" - but the friendly class is "CheckBox"
0234 """
0235 if self.friendlyclassname is None:
0236 self.friendlyclassname = handleprops.classname(self)
0237 return self.friendlyclassname
0238
0239
0240 def Class(self):
0241 """Return the class name of the window"""
0242 if not self._cache.has_key("class"):
0243 self._cache['class'] = handleprops.classname(self)
0244 return self._cache['class']
0245
0246
0247 def WindowText(self):
0248 """Window text of the control
0249
0250 Quite a few contorls have other text that is visible, for example
0251 Edit controls usually have an empty string for WindowText but still
0252 have text displayed in the edit window.
0253 """
0254 return handleprops.text(self)
0255
0256
0257 def Style(self):
0258 """Returns the style of window
0259
0260 Return value is a long.
0261
0262 Combination of WS_* and specific control specific styles.
0263 See HwndWrapper.HasStyle() to easily check if the window has a
0264 particular style.
0265 """
0266 return handleprops.style(self)
0267
0268
0269 def ExStyle(self):
0270 """Returns the Extended style of window
0271
0272 Return value is a long.
0273
0274 Combination of WS_* and specific control specific styles.
0275 See HwndWrapper.HasStyle() to easily check if the window has a
0276 particular style.
0277 """
0278 return handleprops.exstyle(self)
0279
0280
0281 def ControlID(self):
0282 """Return the ID of the window
0283
0284 Only controls have a valid ID - dialogs usually have no ID assigned.
0285
0286 The ID usually identified the control in the window - but there can
0287 be duplicate ID's for example lables in a dialog may have duplicate
0288 ID's.
0289 """
0290 return handleprops.controlid(self)
0291
0292
0293 def UserData(self):
0294 """Extra data associted with the window
0295
0296 This value is a long value that has been associated with the window
0297 and rarely has useful data (or at least data that you know the use
0298 of).
0299 """
0300 return handleprops.userdata(self)
0301
0302
0303 def ContextHelpID(self):
0304 "Return the Context Help ID of the window"
0305 return handleprops.contexthelpid(self)
0306
0307
0308 def IsUnicode(self):
0309 """Whether the window is unicode or not
0310
0311 A window is Unicode if it was registered by the Wide char version
0312 of RegisterClass(Ex).
0313 """
0314 return handleprops.isunicode(self)
0315
0316
0317 def IsVisible(self):
0318 """Whether the window is visible or not
0319
0320 Checks that both the Top Level Parent (probably dialog) that
0321 owns this window and the window itself are both visible.
0322
0323 If you want to wait for a control to become visible (or wait
0324 for it to become hidden) use ``Application.Wait('visible')`` or
0325 ``Application.WaitNot('visible')``.
0326
0327 If you want to raise an exception immediately if a window is
0328 not visible then you can use the HwndWrapper.VerifyVisible().
0329 HwndWrapper.VerifyActionable() raises if the window is not both
0330 visible and enabled.
0331 """
0332
0333 return handleprops.isvisible(self.TopLevelParent()) and handleprops.isvisible(self)
0335
0336
0337 def IsEnabled(self):
0338 """Whether the window is enabled or not
0339
0340 Checks that both the Top Level Parent (probably dialog) that
0341 owns this window and the window itself are both enabled.
0342
0343 If you want to wait for a control to become enabled (or wait
0344 for it to become disabled) use ``Application.Wait('visible')`` or
0345 ``Application.WaitNot('visible')``.
0346
0347 If you want to raise an exception immediately if a window is
0348 not enabled then you can use the HwndWrapper.VerifyEnabled().
0349 HwndWrapper.VerifyReady() raises if the window is not both
0350 visible and enabled.
0351 """
0352 return handleprops.isenabled(self.TopLevelParent()) and handleprops.isenabled(self)
0354
0355
0356 def Rectangle(self):
0357 """Return the rectangle of window
0358
0359 The rectangle is the rectangle of the control on the screen,
0360 coordinates are given from the top left of the screen.
0361
0362 This method returns a RECT structure, Which has attributes - top,
0363 left, right, bottom. and has methods width() and height().
0364 See win32structures.RECT for more information.
0365 """
0366 return handleprops.rectangle(self)
0367
0368
0369 def ClientRect(self):
0370 """Returns the client rectangle of window
0371
0372 The client rectangle is the window rectangle minus any borders that
0373 are not available to the control for drawing.
0374
0375 Both top and left are always 0 for this method.
0376
0377 This method returns a RECT structure, Which has attributes - top,
0378 left, right, bottom. and has methods width() and height().
0379 See win32structures.RECT for more information.
0380 """
0381 return handleprops.clientrect(self)
0382
0383
0384 def Font(self):
0385 """Return the font of the window
0386
0387 The font of the window is used to draw the text of that window.
0388 It is a structure which has attributes for Font name, height, width
0389 etc.
0390
0391 See win32structures.LOGFONTW for more information.
0392 """
0393 return handleprops.font(self)
0394
0395
0396 def ProcessID(self):
0397 """Return the ID of process that owns this window"""
0398 return handleprops.processid(self)
0399
0400
0401 def HasStyle(self, style):
0402 "Return True if the control has the specified sytle"
0403 return handleprops.has_style(self, style)
0404
0405
0406 def HasExStyle(self, exstyle):
0407 "Return True if the control has the specified extended sytle"
0408 return handleprops.has_exstyle(self, exstyle)
0409
0410
0411 def IsDialog(self):
0412 "Return true if the control is a top level window"
0413
0414 if not self._cache.has_key("isdialog"):
0415 self._cache['isdialog'] = handleprops.is_toplevel_window(self)
0416
0417 return self._cache['isdialog']
0418
0419
0420 def Parent(self):
0421 """Return the parent of this control
0422
0423 Note that the parent of a control is not necesarily a dialog or
0424 other main window. A group box may be the parent of some radio
0425 buttons for example.
0426
0427 To get the main (or top level) window then use
0428 HwndWrapper.TopLevelParent().
0429 """
0430
0431 if not self._cache.has_key("parent"):
0432
0433 parent_hwnd = handleprops.parent(self)
0434
0435 if parent_hwnd:
0436
0437
0438 self._cache["parent"] = HwndWrapper(parent_hwnd)
0439 else:
0440 self._cache["parent"] = None
0441
0442 return self._cache["parent"]
0443
0444
0445 def TopLevelParent(self):
0446 """Return the top level window of this control
0447
0448 The TopLevel parent is different from the parent in that the Parent
0449 is the window that owns this window - but it may not be a dialog/main
0450 window. For example most Comboboxes have an Edit. The ComboBox is the
0451 parent of the Edit control.
0452
0453 This will always return a valid window handle (if the control has
0454 no top level parent then the control itself is returned - as it is
0455 a top level window already!)
0456 """
0457
0458 if not self._cache.has_key("top_level_parent"):
0459
0460 parent = self.Parent()
0461
0462 if self.IsDialog():
0463 self._cache["top_level_parent"] = self
0464
0465
0466 elif not parent:
0467 self._cache["top_level_parent"] = self
0468
0469
0470 elif not parent.IsDialog():
0471 self._cache["top_level_parent"] = parent.TopLevelParent()
0472
0473 else:
0474 self._cache["top_level_parent"] = parent
0475
0476
0477 return self._cache["top_level_parent"]
0478
0479
0480 def Texts(self):
0481 """Return the text for each item of this control"
0482
0483 It is a list of strings for the control. It is frequently over-ridden
0484 to extract all strings from a control with multiple items.
0485
0486 It is always a list with one or more strings:
0487
0488 * First elemtent is the window text of the control
0489 * Subsequent elements contain the text of any items of the
0490 control (e.g. items in a listbox/combobox, tabs in a tabcontrol)
0491 """
0492 texts = [self.WindowText(), ]
0493 return texts
0494
0495
0496 def ClientRects(self):
0497 """Return the client rect for each item in this control
0498
0499 It is a list of rectangles for the control. It is frequently over-ridden
0500 to extract all rectangles from a control with multiple items.
0501
0502 It is always a list with one or more rectangles:
0503
0504 * First elemtent is the client rectangle of the control
0505 * Subsequent elements contain the client rectangle of any items of
0506 the control (e.g. items in a listbox/combobox, tabs in a
0507 tabcontrol)
0508 """
0509
0510 return [self.ClientRect(), ]
0511
0512
0513 def Fonts(self):
0514 """Return the font for each item in this control
0515
0516 It is a list of fonts for the control. It is frequently over-ridden
0517 to extract all fonts from a control with multiple items.
0518
0519 It is always a list with one or more fonts:
0520
0521 * First elemtent is the control font
0522 * Subsequent elements contain the font of any items of
0523 the control (e.g. items in a listbox/combobox, tabs in a
0524 tabcontrol)
0525 """
0526 return [self.Font(), ]
0527
0528
0529 def Children(self):
0530 """Return the children of this control as a list
0531
0532 It returns a list of HwndWrapper (or subclass) instances, it
0533 returns an empty list if there are no children.
0534 """
0535
0536 child_windows = handleprops.children(self)
0537 return [HwndWrapper(hwnd) for hwnd in child_windows]
0538
0539
0540 def ControlCount(self):
0541 "Return the number of children of this control"
0542
0543 return len(handleprops.children(self))
0544
0545
0546 def IsChild(self, parent):
0547 """Return True if this window is a child of 'parent'.
0548
0549 A window is a child of another window when it is a direct of the
0550 other window. A window is a direct descendant of a given
0551 window if the parent window is the the chain of parent windows
0552 for the child window.
0553 """
0554
0555
0556
0557 return win32functions.IsChild(parent, self.handle) != 0
0558
0559
0560 def SendMessage(self, message, wparam = 0 , lparam = 0):
0561 "Send a message to the control and wait for it to return"
0562 return win32functions.SendMessage(self, message, wparam, lparam)
0563
0564
0565
0566
0567
0568
0569
0570
0571
0572 def SendMessageTimeout(
0573 self,
0574 message,
0575 wparam = 0 ,
0576 lparam = 0,
0577 timeout = None,
0578 timeoutflags = win32defines.SMTO_NORMAL):
0579 """Send a message to the control and wait for it to return or to timeout
0580
0581 If no timeout is given then a default timeout of .4 of a second will
0582 be used.
0583 """
0584
0585 if timeout is None:
0586 timeout = Timings.sendmessagetimeout_timeout
0587
0588 result = ctypes.c_long()
0589 win32functions.SendMessageTimeout(self,
0590 message, wparam, lparam,
0591 timeoutflags, int(timeout * 1000),
0592 ctypes.byref(result))
0593
0594 return result.value
0595
0596
0597
0598 def PostMessage(self, message, wparam = 0 , lparam = 0):
0599 "Post a message to the control message queue and return"
0600 return win32functions.PostMessage(self, message, wparam, lparam)
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621
0622
0623
0624
0625