Package pyLinAcc :: Module Accessible
[hide private]
[frames] | no frames]

Source Code for Module pyLinAcc.Accessible

  1  ''' 
  2  Defines a mixin class that extend the functionality of the existing AT-SPI  
  3  objects with features such as searching on any criteria and comparison of 
  4  two AT-SPI Accessibles. 
  5   
  6  @author: Peter Parente 
  7  @organization: IBM Corporation 
  8  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
  9  @license: The BSD License 
 10   
 11  All rights reserved. This program and the accompanying materials are made  
 12  available under the terms of the BSD license which accompanies 
 13  this distribution, and is available at 
 14  U{http://www.opensource.org/licenses/bsd-license.php} 
 15  ''' 
 16   
 17  import new, types 
 18  import Constants, Interfaces 
 19   
20 -def stirInto(cls, new_cls, include=None, exclude=None):
21 ''' 22 Adds the methods in new_cls named in include or those not in exclude, or all 23 methods if neither is specified, to cls. After stirring, all instances of 24 cls will have the new methods. If there is a method name clash, the method 25 already in cls will be prefixed with an underscore before the new method of 26 the same name is mixed in. 27 28 Based on code from http://wwwx.cs.unc.edu/~parente/cgi-bin/RuntimeClassMixins. 29 30 @param cls: Existing class to mix features into 31 @type cls: class 32 @param new_cls: Class containing features to add 33 @type new_cls: class 34 @param include: Method names to add 35 @type include: list 36 @param exclude: Method names to avoid adding 37 @type exclude: list 38 ''' 39 # add new methods to the class 40 for name in new_cls.__dict__.keys(): 41 # add methods 42 if isinstance(new_cls.__dict__[name], types.FunctionType) and \ 43 (include is None or name in include) and \ 44 (exclude is None or name not in exclude): 45 # get the function from the new_cls 46 func = new_cls.__dict__[name] 47 # build a new function that is a clone of the one from new_cls 48 method = new.function(func.func_code, func.func_globals, name, 49 func.func_defaults, func.func_closure) 50 try: 51 # check if a method of the same name already exists in the target 52 old_method = getattr(cls, name) 53 except AttributeError: 54 pass 55 else: 56 # rename the old method so we can still call it if need be 57 setattr(cls, '_'+name, old_method) 58 # add the clone to cls 59 setattr(cls, name, method)
60
61 -class AccessibleMixin(object):
62 ''' 63 Defines additional methods to be added to the AT-SPI Accessible object. The 64 features defined here will be added to the Accessible class at run time so 65 that all instances of Accessible have them (i.e. there is no need to 66 explicitly wrap an Accessible in this class or derive a new class from it.) 67 '''
68 - def __del__(self):
69 ''' 70 Decrements the reference count on the accessible object when there are no 71 Python references to this object. This provides automatic reference 72 counting for AT-SPI objects. 73 ''' 74 try: 75 self.unref() 76 except Exception: 77 pass
78
79 - def __iter__(self):
80 ''' 81 Iterator that yields one accessible child per iteration. If an exception is 82 encountered, None is yielded instead. 83 84 @return: A child accessible 85 @rtype: Accessibility.Accessible 86 ''' 87 for i in xrange(self.childCount): 88 try: 89 yield self.getChildAtIndex(i) 90 except Constants.CORBAException: 91 yield None
92
93 - def __str__(self):
94 ''' 95 Gets a human readable representation of the accessible. 96 97 @return: Role and name information for the accessible 98 @rtype: string 99 ''' 100 try: 101 return '[%s | %s]' % (self.getLocalizedRoleName(), self.name) 102 # self.getApplication()) 103 except Exception: 104 return '[DEAD]'
105
106 - def __nonzero__(self):
107 ''' 108 @return: True, always 109 @rtype: boolean 110 ''' 111 return True
112
113 - def __getitem__(self, index):
114 ''' 115 Thin wrapper around getChildAtIndex. 116 117 @param index: Index of desired child 118 @type index: integer 119 @return: Accessible child 120 @rtype: Accessibility.Accessible 121 ''' 122 return self.getChildAtIndex(index)
123
124 - def __len__(self):
125 ''' 126 Thin wrapper around childCount. 127 128 @return: Number of child accessibles 129 @rtype: integer 130 ''' 131 return self.childCount
132
133 - def getIndexInParent(self):
134 ''' 135 Gets the index of this accessible in its parent. Uses the implementation of 136 this method provided by the Accessibility.Accessible object, but checks the 137 bound of the value to ensure it is not outside the range of childCount 138 reported by this accessible's parent. 139 140 @return: Index of this accessible in its parent 141 @rtype: integer 142 ''' 143 i = self._getIndexInParent() 144 try: 145 # correct for out-of-bounds index reporting 146 return min(self.parent.childCount-1, i) 147 except AttributeError: 148 # return sentinel if there is no parent 149 return -1
150
151 - def findDescendant(self, pred, breadth_first=False):
152 ''' 153 Searches for a descendant node satisfying the given predicate starting at 154 this node. The search is performed in depth-first order by default or 155 in breadth first order if breadth_first is True. For example, 156 157 my_win = node.findDescendant(lambda x: x.name == 'My Window') 158 159 will search all descendants of node until one is located with the name 'My 160 Window' or all nodes are exausted. Calls L{_findDescendantDepth} or 161 L{_findDescendantBreadth} to start the recursive search. 162 163 @param pred: Search predicate returning True if accessible matches the 164 search criteria or False otherwise 165 @type pred: callable 166 @param breadth_first: Search breadth first (True) or depth first (False)? 167 @type breadth_first: boolean 168 @return: Accessible matching the criteria or None if not found 169 @rtype: Accessibility.Accessible or None 170 ''' 171 if breadth_first: 172 return self._findDescendantBreadth(pred) 173 for i in xrange(self.childCount): 174 try: 175 c = self.getChildAtIndex(i) 176 ret = c._findDescendantDepth(pred) 177 except Exception: 178 ret = None 179 if ret is not None: return ret
180
181 - def _findDescendantBreadth(self, pred):
182 ''' 183 Internal function for locating one descendant. Called by 184 L{AccessibleMixin.findDescendant} to start the search. 185 186 @param pred: Search predicate returning True if accessible matches the 187 search criteria or False otherwise 188 @type pred: callable 189 @return: Matching node or None to keep searching 190 @rtype: Accessibility.Accessible or None 191 ''' 192 for i in xrange(self.childCount): 193 try: 194 c = self.getChildAtIndex(i) 195 if pred(c): return c 196 except Exception: 197 pass 198 for i in xrange(self.childCount): 199 try: 200 c = self.getChildAtIndex(i) 201 ret = c._findDescendantBreadth(pred) 202 except Exception: 203 ret = None 204 if ret is not None: return ret
205
206 - def _findDescendantDepth(self, pred):
207 ''' 208 Internal function for locating one descendant. Called by 209 L{AccessibleMixin.findDescendant} to start the search. 210 211 @param pred: Search predicate returning True if accessible matches the 212 search criteria or False otherwise 213 @type pred: callable 214 @return: Matching node or None to keep searching 215 @rtype: Accessibility.Accessible or None 216 ''' 217 try: 218 if pred(self): return self 219 except Exception: 220 pass 221 for i in xrange(self.childCount): 222 try: 223 c = self.getChildAtIndex(i) 224 ret = c._findDescendantDepth(pred) 225 except Exception: 226 ret = None 227 if ret is not None: return ret
228
229 - def findAllDescendants(self, pred):
230 ''' 231 Searches for all descendant nodes satisfying the given predicate starting at 232 this node. For example, 233 234 pred = lambda x: x.getRole() == Accessible.ROLE_PUSH_BUTTON 235 buttons = node.findAllDescendants(pred) 236 237 will locate all push button descendants of the current node. Calls 238 L{findIterDescendants} to start the exhaustive recursive search. 239 240 @param pred: Search predicate returning True if accessible matches the 241 search criteria or False otherwise 242 @type pred: callable 243 @return: All nodes matching the search criteria 244 @rtype: list 245 ''' 246 return [result for result in self.findIterDescendants(pred)]
247
248 - def findIterDescendants(self, pred):
249 ''' 250 Iterator that yields Accessibility.Accessible that passed search criteria 251 defined by pred. The search is performed in depth-first order and the 252 results are returned in that order. 253 254 @param pred: Search predicate returning True if accessible matches the 255 search criteria or False otherwise 256 @type pred: callable 257 @return: A node matching the search criteria 258 @rtype: Accessibility.Accessible or None 259 ''' 260 for i in xrange(self.childCount): 261 try: 262 c = self.getChildAtIndex(i) 263 if pred(self): yield c 264 except AttributeError: 265 continue
266
267 - def findAncestor(self, pred):
268 ''' 269 Searches for an ancestor satisfying the given predicate. Note that the 270 AT-SPI DOM is not perfectly doubly linked. Node A may consider node B its 271 child, but B is not guaranteed to have node A as its parent (i.e. its parent 272 may be set to None). This means some searches may never make it all the 273 way up the DOM to the desktop level. 274 275 @param pred: Search predicate returning True if accessible matches the 276 search criteria or False otherwise 277 @type pred: callable 278 @return: Node matching the criteria or None if not found 279 @rtype: Accessibility.Accessible or None 280 ''' 281 if self.parent is None: 282 return None 283 else: 284 try: 285 if pred(self.parent): return self.parent 286 except Exception: 287 pass 288 return self.parent.findAncestor(pred)
289
290 - def getApplication(self):
291 ''' 292 Gets the most-parent accessible (the application) of this accessible. Tries 293 using the getApplication method introduced in AT-SPI 1.7.0 first before 294 resorting to traversing parent links. 295 296 @warning: Cycles involving more than the previously traversed accessible 297 are not detected by this code. 298 @return: Application object 299 @rtype: Accessibility.Application 300 ''' 301 try: 302 return self._getApplication() 303 except AttributeError: 304 pass 305 curr = self 306 try: 307 while curr.parent is not None and (not curr.parent == curr): 308 curr = curr.parent 309 return Interfaces.IApplication(curr) 310 except Exception: 311 pass 312 # return None if the application isn't reachable for any reason 313 return None
314
315 - def isAlive(self):
316 ''' 317 Verifies that the underlying CORBA object still exists. 318 319 @return: True if the CORBA object is still valid. False if not. 320 @rtype: boolean 321 ''' 322 try: 323 self.id 324 except Exception: 325 return False 326 else: 327 return True
328 329 # mixes into the Accessibility.Accessible object; don't need to mix into 330 # Accessibility.Application or Accessibility.Desktop because they derive from 331 # Accessibility.Accessible 332 import Accessibility 333 stirInto(Accessibility.Accessible, AccessibleMixin) 334