pyWinAuto: c:\.projects\py_pywinauto\pywinauto\controls\menuwrapper.py
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021"""Wrapper around Menu's and Menu items
0022
0023These wrappers allow you to work easily with menu items.
0024You can select or click on items and check if they are
0025checked or unchecked.
0026"""
0027
0028__revision__ = "$Revision: 330 $"
0029
0030import ctypes
0031import time
0032
0033from pywinauto import win32structures
0034from pywinauto import win32functions
0035from pywinauto import win32defines
0036from pywinauto import findbestmatch
0037from pywinauto.timings import Timings
0038
0039
0040class MenuItemNotEnabled(RuntimeError):
0041 "Raised when a menuitem is not enabled"
0042 pass
0043
0044
0045class MenuItem(object):
0046 """Wrap a menu item"""
0047
0048 def __init__(self, ctrl, menu, index, on_main_menu = False):
0049 """Initalize the menu item
0050
0051 * **ctrl** The dialog or control that owns this menu
0052 * **menu** The menu that this item is on
0053 * **index** The Index of this menuitem on the menu
0054 * **on_main_menu** True if the item is on the main menu
0055
0056 """
0057 self.index = index
0058 self.menu = menu
0059 self.ctrl = ctrl
0060 self.on_main_menu = on_main_menu
0061
0062
0063 def _read_item(self):
0064 """Read the menu item info
0065
0066 See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/resources/menus/menureference/menufunctions/getmenuiteminfo.asp
0067 for more information."""
0068 menu_info = win32structures.MENUITEMINFOW()
0069 menu_info.cbSize = ctypes.sizeof (menu_info)
0070 menu_info.fMask = win32defines.MIIM_CHECKMARKS | win32defines.MIIM_ID | win32defines.MIIM_STATE | win32defines.MIIM_SUBMENU | win32defines.MIIM_TYPE
0076
0077
0078
0079
0080 ret = win32functions.GetMenuItemInfo (
0081 self.menu,
0082 self.index,
0083 True,
0084 ctypes.byref(menu_info))
0085
0086 if not ret:
0087 raise ctypes.WinError()
0088
0089 return menu_info
0090
0091
0092 def Rectangle(self):
0093 "Get the rectangle of the menu item"
0094 rect = win32structures.RECT()
0095
0096 if self.on_main_menu:
0097 ctrl = self.ctrl
0098 else:
0099 ctrl = 0
0100
0101 win32functions.GetMenuItemRect(
0102 ctrl,
0103 self.menu,
0104 self.index,
0105 ctypes.byref(rect))
0106
0107 return rect
0108
0109 def Index(self):
0110 "Return the index of this menu item"
0111 return self.index
0112
0113 def State(self):
0114 "Return the state of this menu item"
0115 return self._read_item().fState
0116
0117 def ID(self):
0118 "Return the ID of this menu item"
0119 return self._read_item().wID
0120
0121 def Type(self):
0122 """Return the Type of this menu item
0123
0124 Main types are MF_STRING, MF_BITMAP, MF_SEPARATOR.
0125
0126 See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/resources/menus/menureference/menustructures/menuiteminfo.asp
0127 for further information."""
0128 return self._read_item().fType
0129
0130 def Text(self):
0131 "Return the state of this menu item"
0132
0133 info = self._read_item()
0134
0135 if info.cch:
0136
0137 buffer_size = info.cch+1
0138 text = ctypes.create_unicode_buffer(buffer_size)
0139
0140
0141 info.dwTypeData = ctypes.addressof(text)
0142 info.cch = buffer_size
0143
0144 win32functions.GetMenuItemInfo (
0145 self.menu,
0146 self.index,
0147 True,
0148 ctypes.byref(info))
0149
0150 text = text.value
0151 else:
0152 text = ''
0153
0154 return text
0155
0156 def SubMenu(self):
0157 "Return the SubMenu or None if no submenu"
0158 submenu_handle = self._read_item().hSubMenu
0159
0160 if submenu_handle:
0161 self.ctrl.SendMessageTimeout(
0162 win32defines.WM_INITMENUPOPUP,
0163 submenu_handle,
0164 self.index)
0165
0166 return Menu(self.ctrl, submenu_handle, False, self)
0167
0168 return None
0169
0170 def IsEnabled(self):
0171 "Return True if the item is enabled."
0172 return not (
0173 self.State() & win32defines.MF_DISABLED or
0174 self.State() & win32defines.MF_GRAYED)
0175
0176 def IsChecked(self):
0177 "Return True if the item is checked."
0178 return bool(self.State() & win32defines.MF_CHECKED)
0179
0180
0181 def Click(self):
0182 """Click on the menu item
0183
0184 If the menu is open this it will click with the mouse on the item.
0185 If the menu is not open each of it's parent's will be opened
0186 until the item is visible.
0187
0188 """
0189
0190 self.ctrl.VerifyEnabled()
0191
0192 rect = self.Rectangle()
0193
0194 if not self.IsEnabled():
0195 raise MenuItemNotEnabled(
0196 "MenuItem '%s' is disabled"% self.Text())
0197
0198
0199
0200 if rect == (0, 0, 0, 0):
0201 if self.menu.owner_item:
0202 self.menu.owner_item.Click()
0203
0204 rect = self.Rectangle()
0205
0206 x_pt = (rect.left + rect.right) /2
0207 y_pt = (rect.top + rect.bottom) /2
0208
0209 from HwndWrapper import _perform_click_input
0210
0211 _perform_click_input(
0212 None,
0213 coords = (x_pt, y_pt),
0214 absolute = True)
0215
0216 win32functions.WaitGuiThreadIdle(self.ctrl)
0217
0218
0219
0220
0221
0222 def Select(self):
0223 """Select the menu item
0224
0225 This will send a message to the parent window that the
0226 item was picked
0227 """
0228
0229 if not self.IsEnabled():
0230 raise MenuItemNotEnabled(
0231 "MenuItem '%s' is disabled"% self.Text())
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242 command_id = self.ID()
0243
0244
0245 self.ctrl.SetFocus()
0246 self.ctrl.SendMessageTimeout(
0247 win32defines.WM_COMMAND,
0248 win32functions.MakeLong(0, command_id))
0249
0250
0251 win32functions.WaitGuiThreadIdle(self.ctrl)
0252 time.sleep(Timings.after_menu_wait)
0253
0254 def GetProperties(self):
0255 """Return the properties for the item as a dict
0256
0257 If this item opens a sub menu then call Menu.GetProperties()
0258 to return the list of items in the sub menu. This is avialable
0259 under teh 'MenuItems' key
0260 """
0261 props = {}
0262 props['Index'] = self.Index()
0263 props['State'] = self.State()
0264 props['Type'] = self.Type()
0265 props['ID'] = self.ID()
0266 props['Text'] = self.Text()
0267
0268 submenu = self.SubMenu()
0269 if submenu:
0270 props['MenuItems'] = submenu.GetProperties()
0271
0272 return props
0273
0274 def __repr__(self):
0275 "Return a representation of the object as a string"
0276 return "<MenuItem %s>" % `self.Text()`
0277
0278
0279
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319class Menu(object):
0320 """A simple wrapper around a menu handle
0321
0322 A menu supports methods for querying the menu
0323 and getting it's menu items."""
0324 def __init__(
0325 self,
0326 owner_ctrl,
0327 menuhandle,
0328 is_main_menu = True,
0329 owner_item = None):
0330 """Initialize the class.
0331
0332 * **owner_ctrl** is the Control that owns this menu
0333 * **menuhandle** is the menu handle of the menu
0334 * **is_main_menu** we have to track whether it is the main menu
0335 or a popup menu
0336 * **owner_item** The item that contains this menu - this will be
0337 None for the main menu, it will be a MenuItem instance for a
0338 submenu.
0339
0340 """
0341 self.ctrl = owner_ctrl
0342 self.handle = menuhandle
0343 self.is_main_menu = is_main_menu
0344 self.owner_item = owner_item
0345
0346 self._as_parameter_ = self.handle
0347
0348 if self.is_main_menu:
0349 self.ctrl.SendMessageTimeout(win32defines.WM_INITMENU, self.handle)
0350
0351 def ItemCount(self):
0352 "Return the count of items in this menu"
0353 return win32functions.GetMenuItemCount(self.handle)
0354
0355 def Item(self, index):
0356 """Return a specific menu item
0357
0358 * **index** is the 0 based index of the menu item you want
0359 """
0360 return MenuItem(self.ctrl, self, index, self.is_main_menu)
0361
0362 def Items(self):
0363 "Return a list of all the items in this menu"
0364 items = []
0365 for i in range(0, self.ItemCount()):
0366 items.append(self.Item(i))
0367
0368 return items
0369
0370 def GetProperties(self):
0371 """Return the properties for the menu as a list of dictionaries
0372
0373 This method is actually recursive. It calls GetProperties() for each
0374 of the items. If the item has a sub menu it will call this
0375 GetProperties to get the sub menu items.
0376 """
0377 item_props = []
0378
0379 for item in self.Items():
0380 item_props.append(item.GetProperties())
0381
0382 return {'MenuItems': item_props}
0383
0384
0385 def GetMenuPath(self, path, path_items = None, appdata = None):
0386 "Walk the items in this menu to find the item specified by path"
0387
0388 if path_items is None:
0389 path_items = []
0390
0391
0392 parts = path.split("->", 1)
0393 current_part = parts[0]
0394
0395 if current_part.startswith("#"):
0396 index = int(current_part[1:])
0397 best_item = self.Item(index)
0398 else:
0399
0400 if appdata is None:
0401 item_texts = [item.Text() for item in self.Items()]
0402
0403 else:
0404 item_texts = [item['Text'] for item in appdata]
0405
0406
0407 best_item = findbestmatch.find_best_match(
0408 current_part,
0409 item_texts,
0410 self.Items())
0411
0412 path_items.append(best_item)
0413
0414
0415
0416 if parts[1:]:
0417 if appdata:
0418 appdata = appdata[best_item.Index()]['MenuItems']
0419 if best_item.SubMenu() is not None:
0420 best_item.SubMenu().GetMenuPath(
0421 "->".join(parts[1:]),
0422 path_items,
0423 appdata)
0424
0425 return path_items
0426
0427
0428 def __repr__(self):
0429 "Return a simple representation of the menu"
0430 return "<Menu %d>" % self.handle
0431
0432
0433
0434
0435
0436
0437
0438
0439
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475
0476
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487
0488
0489
0490
0491
0492
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502
0503
0504
0505
0506
0507
0508
0509
0510
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537
0538
0539
0540