Package dogtail :: Module procedural
[hide private]
[frames] | no frames]

Source Code for Module dogtail.procedural

  1  """ 
  2  Dogtail's procedural UI 
  3  All the classes here are intended to be single-instance, except for Action. 
  4  """ 
  5  __author__ = 'Zack Cerza <zcerza@redhat.com>' 
  6  # 
  7  # 
  8  # WARNING: Here There Be Dragons (TM)                                        # 
  9  # 
 10  # If you don't understand how to use this API, you almost certainly don't    # 
 11  # want to read the code first. We make use of some very non-intuitive        # 
 12  # features of Python in order to make the API very simplistic. Therefore,    # 
 13  # you should probably only read this code if you're already familiar with    # 
 14  # some of Python's advanced features. You have been warned. ;)               # 
 15  # 
 16  # 
 17   
 18  import tree 
 19  import predicate 
 20  from config import config 
 21  from utils import Lock 
 22  import rawinput 
 23   
 24  #FocusError = "FocusError: %s not found" 
 25   
 26   
27 -class FocusError(Exception):
28 pass
29 30 import errors 31 32
33 -def focusFailed(pred):
34 errors.warn('The requested widget could not be focused: %s' % 35 pred.debugName)
36 37 ENOARGS = "At least one argument is needed" 38 39
40 -class FocusBase(object):
41 42 """ 43 The base for every class in the module. Does nothing special, really. 44 """ 45 node = None 46
47 - def __getattr__(self, name):
48 # Fold all the Node's AT-SPI properties into the Focus object. 49 try: 50 return getattr(self.node, name) 51 except AttributeError: 52 raise AttributeError(name)
53
54 - def __setattr__(self, name, value):
55 # Fold all the Node's AT-SPI properties into the Focus object. 56 if name == 'node': 57 setattr(self.__class__, name, value) 58 else: 59 try: 60 setattr(self.node, name, value) 61 except AttributeError: 62 raise AttributeError(name)
63 64
65 -class FocusApplication (FocusBase):
66 67 """ 68 Keeps track of which application is currently focused. 69 """ 70 desktop = tree.root 71
72 - def __call__(self, name):
73 """ 74 Search for an application that matches and refocus on the given name. 75 """ 76 try: 77 pred = predicate.IsAnApplicationNamed(name) 78 app = self.desktop.findChild( 79 pred, recursive=False, retry=False) 80 except tree.SearchError: 81 if config.fatalErrors: 82 raise FocusError(name) 83 else: 84 focusFailed(pred) 85 return False 86 if app: 87 FocusApplication.node = app 88 FocusDialog.node = None 89 FocusWindow.node = None 90 FocusWidget.node = None 91 return True
92 93
94 -class FocusDesktop (FocusBase):
95 96 """ 97 This isn't used yet, and may never be used. 98 """ 99 pass
100 101
102 -class FocusWindow (FocusBase):
103 104 """ 105 Keeps track of which window is currently focused. 106 """ 107
108 - def __call__(self, name):
109 """ 110 Search for a dialog that matches the given name and refocus on it. 111 """ 112 result = None 113 pred = predicate.IsAWindowNamed(name) 114 try: 115 result = FocusApplication.node.findChild( 116 pred, requireResult=False, recursive=False) 117 except AttributeError: 118 pass 119 if result: 120 FocusWindow.node = result 121 FocusDialog.node = None 122 FocusWidget.node = None 123 else: 124 if config.fatalErrors: 125 raise FocusError(pred.debugName) 126 else: 127 focusFailed(pred) 128 return False 129 return True
130 131
132 -class FocusDialog (FocusBase):
133 134 """ 135 Keeps track of which dialog is currently focused. 136 """ 137
138 - def __call__(self, name):
139 """ 140 Search for a dialog that matches the given name and refocus on it. 141 """ 142 result = None 143 pred = predicate.IsADialogNamed(name) 144 try: 145 result = FocusApplication.node.findChild( 146 pred, requireResult=False, recursive=False) 147 except AttributeError: 148 pass 149 if result: 150 FocusDialog.node = result 151 FocusWidget.node = None 152 else: 153 if config.fatalErrors: 154 raise FocusError(pred.debugName) 155 else: 156 focusFailed(pred) 157 return False 158 return True
159 160
161 -class FocusWidget (FocusBase):
162 163 """ 164 Keeps track of which widget is currently focused. 165 """ 166
167 - def findByPredicate(self, pred):
168 result = None 169 try: 170 result = FocusWidget.node.findChild( 171 pred, requireResult=False, retry=False) 172 except AttributeError: 173 pass 174 if result: 175 FocusWidget.node = result 176 else: 177 try: 178 result = FocusDialog.node.findChild( 179 pred, requireResult=False, retry=False) 180 except AttributeError: 181 pass 182 if result: 183 FocusWidget.node = result 184 else: 185 try: 186 result = FocusWindow.node.findChild( 187 pred, requireResult=False, retry=False) 188 except AttributeError: 189 pass 190 if result: 191 FocusWidget.node = result 192 else: 193 try: 194 result = FocusApplication.node.findChild( 195 pred, requireResult=False, retry=False) 196 if result: 197 FocusWidget.node = result 198 except AttributeError: 199 if config.fatalErrors: 200 raise FocusError(pred) 201 else: 202 focusFailed(pred) 203 return False 204 205 if result is None: 206 FocusWidget.node = result 207 if config.fatalErrors: 208 raise FocusError(pred.debugName) 209 else: 210 focusFailed(pred) 211 return False 212 return True
213
214 - def __call__(self, name='', roleName='', description=''):
215 """ 216 If name, roleName or description are specified, search for a widget that matches and refocus on it. 217 """ 218 if not name and not roleName and not description: 219 raise TypeError(ENOARGS) 220 221 # search for a widget. 222 pred = predicate.GenericPredicate(name=name, 223 roleName=roleName, description=description) 224 return self.findByPredicate(pred)
225 226
227 -class Focus (FocusBase):
228 229 """ 230 The container class for the focused application, dialog and widget. 231 """ 232
233 - def __getattr__(self, name):
234 raise AttributeError(name)
235
236 - def __setattr__(self, name, value):
237 if name in ('application', 'dialog', 'widget', 'window'): 238 self.__dict__[name] = value 239 else: 240 raise AttributeError(name)
241 242 desktop = tree.root 243 application = FocusApplication() 244 app = application # shortcut :) 245 dialog = FocusDialog() 246 window = FocusWindow() 247 frame = window 248 widget = FocusWidget() 249
250 - def button(self, name):
251 """ 252 A shortcut to self.widget.findByPredicate(predicate.IsAButtonNamed(name)) 253 """ 254 return self.widget.findByPredicate(predicate.IsAButtonNamed(name))
255
256 - def icon(self, name):
257 """ 258 A shortcut to self.widget(name, roleName = 'icon') 259 """ 260 return self.widget(name=name, roleName='icon')
261
262 - def menu(self, name):
263 """ 264 A shortcut to self.widget.findByPredicate(predicate.IsAMenuNamed(name)) 265 """ 266 return self.widget.findByPredicate(predicate.IsAMenuNamed(name))
267
268 - def menuItem(self, name):
269 """ 270 A shortcut to self.widget.findByPredicate(predicate.IsAMenuItemNamed(name)) 271 """ 272 return self.widget.findByPredicate(predicate.IsAMenuItemNamed(name))
273
274 - def table(self, name=''):
275 """ 276 A shortcut to self.widget(name, roleName 'table') 277 """ 278 return self.widget(name=name, roleName='table')
279
280 - def tableCell(self, name=''):
281 """ 282 A shortcut to self.widget(name, roleName 'table cell') 283 """ 284 return self.widget(name=name, roleName='table cell')
285
286 - def text(self, name=''):
287 """ 288 A shortcut to self.widget.findByPredicate(IsATextEntryNamed(name)) 289 """ 290 return self.widget.findByPredicate(predicate.IsATextEntryNamed(name))
291 292
293 -class Action (FocusWidget):
294 295 """ 296 Aids in executing AT-SPI actions, refocusing the widget if necessary. 297 """ 298
299 - def __init__(self, action):
300 """ 301 action is a string with the same name as the AT-SPI action you wish to execute using this class. 302 """ 303 self.action = action
304
305 - def __call__(self, name='', roleName='', description='', delay=config.actionDelay):
306 """ 307 If name, roleName or description are specified, first search for a widget that matches and refocus on it. 308 Then execute the action. 309 """ 310 if name or roleName or description: 311 FocusWidget.__call__( 312 self, name=name, roleName=roleName, description=description) 313 self.node.doActionNamed(self.action)
314
315 - def __getattr__(self, attr):
316 return getattr(FocusWidget.node, attr)
317
318 - def __setattr__(self, attr, value):
319 if attr == 'action': 320 self.__dict__[attr] = value 321 else: 322 setattr(FocusWidget, attr, value)
323
324 - def button(self, name):
325 """ 326 A shortcut to self(name, roleName = 'push button') 327 """ 328 self.__call__(name=name, roleName='push button')
329
330 - def menu(self, name):
331 """ 332 A shortcut to self(name, roleName = 'menu') 333 """ 334 self.__call__(name=name, roleName='menu')
335
336 - def menuItem(self, name):
337 """ 338 A shortcut to self(name, roleName = 'menu item') 339 """ 340 self.__call__(name=name, roleName='menu item')
341
342 - def table(self, name=''):
343 """ 344 A shortcut to self(name, roleName 'table') 345 """ 346 self.__call__(name=name, roleName='table')
347
348 - def tableCell(self, name=''):
349 """ 350 A shortcut to self(name, roleName 'table cell') 351 """ 352 self.__call__(name=name, roleName='table cell')
353
354 - def text(self, name=''):
355 """ 356 A shortcut to self(name, roleName = 'text') 357 """ 358 self.__call__(name=name, roleName='text')
359 360
361 -class Click (Action):
362 363 """ 364 A special case of Action, Click will eventually handle raw mouse events. 365 """ 366 primary = 1 367 middle = 2 368 secondary = 3 369
370 - def __init__(self):
371 Action.__init__(self, 'click')
372
373 - def __call__(self, name='', roleName='', description='', raw=True, button=primary, delay=config.actionDelay):
374 """ 375 By default, execute a raw mouse event. 376 If raw is False or if button evaluates to False, just pass the rest of 377 the arguments to Action. 378 """ 379 if name or roleName or description: 380 FocusWidget.__call__( 381 self, name=name, roleName=roleName, description=description) 382 if raw and button: 383 # We're doing a raw mouse click 384 Click.node.click(button) 385 else: 386 Action.__call__( 387 self, name=name, roleName=roleName, description=description, delay=delay)
388 389
390 -class Select (Action):
391 392 """ 393 Aids in selecting and deselecting widgets, i.e. page tabs 394 """ 395 select = 'select' 396 deselect = 'deselect' 397
398 - def __init__(self, action):
399 """ 400 action must be 'select' or 'deselect'. 401 """ 402 if action not in (self.select, self.deselect): 403 raise ValueError(action) 404 Action.__init__(self, action)
405
406 - def __call__(self, name='', roleName='', description='', delay=config.actionDelay):
407 """ 408 If name, roleName or description are specified, first search for a widget that matches and refocus on it. 409 Then execute the action. 410 """ 411 if name or roleName or description: 412 FocusWidget.__call__( 413 self, name=name, roleName=roleName, description=description) 414 func = getattr(self.node, self.action) 415 func()
416 417
418 -def type(text):
419 if focus.widget.node: 420 focus.widget.node.typeText(text) 421 else: 422 rawinput.typeText(text)
423 424
425 -def keyCombo(combo):
426 if focus.widget.node: 427 focus.widget.node.keyCombo(combo) 428 else: 429 rawinput.keyCombo(combo)
430 431
432 -def run(application, arguments='', appName=''):
433 from utils import run as utilsRun 434 pid = utilsRun(application + ' ' + arguments, appName=appName) 435 focus.application(application) 436 return pid
437 438 import os 439 # tell sniff not to use auto-refresh while script using this module is running 440 # may have already been locked by dogtail.tree 441 if not os.path.exists('/tmp/sniff_refresh.lock'): # pragma: no cover 442 sniff_lock = Lock(lockname='sniff_refresh.lock', randomize=False) 443 try: 444 sniff_lock.lock() 445 except OSError: 446 pass # lock was already present from other script instance or leftover from killed instance 447 # lock should unlock automatically on script exit. 448 449 focus = Focus() 450 click = Click() 451 activate = Action('activate') 452 openItem = Action('open') 453 menu = Action('menu') 454 select = Select(Select.select) 455 deselect = Select(Select.deselect) 456