Package AccessEngine :: Module AEEventManager'
[hide private]
[frames] | no frames]

Source Code for Module AccessEngine.AEEventManager'

  1  ''' 
  2  Defines a class that uses a binding to the accessible platform and supports  
  3  L{AEEvent <AEvent.AEEvent>}s. 
  4   
  5  @var MAX_EVENTS: Maximum number of raw or L{AEEvent <AEvent.AEEvent>}s to  
  6    process in a single callback from the main loop 
  7  @type MAX_EVENTS: integer 
  8  @var EVENT_INTERVAL: Number of ms between calls to pump the event queues 
  9  @type EVENT_INTERVAL: integer 
 10   
 11  @author: Peter Parente 
 12  @author: Pete Brunet 
 13  @organization: IBM Corporation 
 14  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
 15   
 16  @author: Frank Zenker 
 17  @author: Ramona Bunk 
 18  @organization: IT Science Center Ruegen gGmbH, Germany 
 19  @copyright: Copyright (c) 2007, 2008 ITSC Ruegen 
 20   
 21  @license: I{The BSD License} 
 22  All rights reserved. This program and the accompanying materials are made 
 23  available under the terms of the BSD license which accompanies 
 24  this distribution, and is available at 
 25  U{http://www.opensource.org/licenses/bsd-license.php} 
 26  ''' 
 27   
 28  import pyatspi 
 29  import Queue, time, logging 
 30  import AccessEngine 
 31  from AccessEngine import AERegistrar, AEEvent, AEMonitor 
 32  import AEConstants 
 33  from AccessEngine.AEAccInterfaces import implements 
 34  from AERegistrar import MONITOR 
 35  from Tools.i18n import _ 
 36   
 37  log = logging.getLogger('Event') 
 38  EVENT_INTERVAL = 10 
 39  MAX_EVENTS = 10 
 40   
41 -class _AEEventManager(object):
42 ''' 43 Watches for raw events in order to report them to observers and maintains a 44 queue of L{AEEvent <AEvent.AEEvent>} that are executed on a set interval. 45 46 @ivar ae_queue: FIFO queue of L{AEEvent <AEvent.AEEvent>} to be executed 47 @type ae_queue: Queue.Queue 48 @ivar monitors: collection of L{AEMonitor <AEMonitor.AEMonitor>}s to be 49 notified about events. 50 @type monitors: L{AEMonitor.MonitorCollection} 51 @ivar last_event: the last executed event 52 @type last_event: L{AEEvent <AEvent.AEEvent>} 53 @ivar acc_reg: Reference to the B{Accessibility.Registry} 54 @type acc_reg: type of B{Accessibility.Registry} 55 '''
56 - def __init__(self):
57 ''' 58 Initializes the parent class. 59 60 Creates an event queue. Asks the L{AEMain} to schedule interval callbacks 61 to the L{onPumpRawEvents} and L{onPumpAEEvents} methods. 62 ''' 63 self.ae_queue = Queue.Queue() 64 self.monitors = AEMonitor.MonitorCollection() 65 self.last_event = None 66 self.acc_reg = None 67 68 # ask the open access engine to notify us on a set interval to pump both the 69 # raw and AE event queues 70 AccessEngine.AEMain.addTimer(self.onPumpRawEvents, EVENT_INTERVAL) 71 AccessEngine.AEMain.addTimer(self.onPumpAEEvents, EVENT_INTERVAL)
72
73 - def init(self):
74 ''' 75 Instantiates all L{AEMonitor <AEMonitor.AEMonitor>} UIEs registered with 76 L{AERegistrar} to be loaded at startup. 77 78 Called by L{AEMain} at startup. 79 ''' 80 # get the registry 81 self.acc_reg = pyatspi.Registry 82 83 # load all startup monitors 84 reg = AERegistrar 85 mons = reg.loadAssociated(MONITOR, AccessEngine.AESettingsManager.getProfileName()) 86 self.addMonitors(*mons)
87
88 - def startAccessibility(self, async=True, gil=True):
89 ''' 90 Enter the main loop to start recieving and dispatching events. 91 92 @param async: Should event dispatch be asynchronous (decoupled) from 93 event recieving from the AT-SPI registry? 94 @type async: boolean 95 @param gil: Add an idle callback which releses the Python GIL for a few 96 milliseconds to allow other threads to run? Necessary if other threads 97 will be used in this process. 98 @type gil: boolean 99 ''' 100 return self.acc_reg.start(async, gil)
101
102 - def stopAccessibility(self):
103 ''' 104 Quits the main loop. 105 ''' 106 return self.acc_reg.stop()
107
108 - def close(self):
109 ''' 110 Throws away all events in the L{ae_queue} and stops the event loop. 111 ''' 112 self.acc_reg.stop() 113 self.ae_queue = Queue.Queue() 114 self.monitors.clear()
115
116 - def addMonitors(self, *monitors):
117 ''' 118 Adds one or more AEMonitors to the list of monitors 119 to be notified about events. 120 121 @param monitors: AEMonitors to notify 122 @type monitors: tuple of L{AEMonitor <AEMonitor.AEMonitor>}s 123 ''' 124 self.monitors.add(pyatspi.event.Event, monitors)
125
126 - def getMonitors(self):
127 ''' 128 Gets all loaded AEMonitors. 129 130 @return: Collection of all loaded L{AEMonitor <AEMonitor.AEMonitor>}s 131 @rtype: L{AEMonitor.MonitorCollection} 132 ''' 133 return self.monitors
134
135 - def showEvent(self, event):
136 ''' 137 Informs L{AEMonitor <AEMonitor.AEMonitor>}s added via L{addMonitors} of a 138 raw event. 139 140 @param event: Raw accessibility event 141 @type event: C{pyatspi.event.Event} 142 ''' 143 self.monitors.show(event=event)
144
145 - def _executeEvent(self, event):
146 ''' 147 Executes the provided L{AEEvent <AEvent.AEEvent>}. 148 149 Catches and logs all exceptions to prevent bad events from getting re-queued 150 for later execution. 151 152 @param event: Event to execute 153 @type event: L{AEEvent <AEvent.AEEvent>} 154 @return: Did the event execute properly or does it need to be re-queued for 155 later execution? 156 @rtype: True 157 ''' 158 # don't let our pump die if something fails 159 try: 160 return event.execute() 161 except LookupError: 162 # just ignore dead objects 163 return True 164 except Exception, e: 165 # don't get bad events stuck in a loop 166 log.exception(str(e)) 167 return True
168
169 - def flushEvents(self):
170 ''' 171 Flushes the event queue by destroying it and recreating it. 172 ''' 173 self.ae_queue = Queue.Queue()
174
175 - def postEvents(self, *events):
176 ''' 177 Add L{AEEvent <AEvent.AEEvent>}s to the queue. 178 179 Events can be added by any part of the system. Events marked with immediate 180 priority are executed now instead of being added to the queue. Immediate 181 execution is necessary for some kinds of events. 182 183 @param events: Events to queue for dispatch 184 @type events: tuple of L{AEEvent <AEvent.AEEvent>}s 185 ''' 186 for e in events: 187 if e is not None: 188 if e.priority == AEConstants.EXEC_IMMEDIATE: 189 # some events need to execute immediately (e.g. view update) 190 if self._executeEvent(e) == False: 191 # if it doesn't work now, try again later 192 log.debug('re-queuing immediate AEEvent') 193 self.ae_queue.put_no_wait(e) 194 else: 195 self.ae_queue.put_nowait(e)
196
197 - def onPumpRawEvents(self):
198 ''' 199 Pumps all C{pyatspi.event.Event}s in the C{pyatspi.Registry.queue}. 200 201 Calls the C{pyatspi.Regitry._dispatchEvent} method to forward the event to 202 all registered clients. Catches and logs all exceptions to prevent them 203 from propogating to the main loop that called this method. 204 205 This method is called on a set interval by the main event loop in 206 L{AEMain.run <AEMain._AEMain>}. 207 It is registered for callbacks in the constructor of this class. 208 209 @return: C{True} to continue receiving notifications 210 @rtype: boolean 211 ''' 212 queue = self.acc_reg.queue 213 for i in xrange(MAX_EVENTS): 214 try: 215 # get next waiting events 216 event = queue.get_nowait() 217 except Queue.Empty: 218 break 219 # notify monitors that the event was received 220 self.showEvent(event) 221 try: 222 self.acc_reg._dispatchEvent(event) 223 except LookupError: 224 break 225 except Exception: 226 # catch all exceptions to prevent our main loop from dying 227 log.exception('Raw event exception') 228 return True
229
230 - def onPumpAEEvents(self):
231 ''' 232 Pumps all L{AEEvent <AEvent.AEEvent>}s in the L{ae_queue}. 233 234 Executes each event pumped. Re-queues events for later execution that return 235 C{False} from their execute methods. 236 237 This method is called on a set interval by the main event loop in 238 L{AEMain.run <AEMain._AEMain>}. It is registered for callbacks in the 239 constructor of this class. 240 241 @return: C{True} to continue receiving notifications 242 @rtype: boolean 243 ''' 244 later = [] 245 for i in xrange(MAX_EVENTS): 246 try: 247 event = self.ae_queue.get_nowait() 248 except Queue.Empty: 249 break 250 rv = self._executeEvent(event) 251 if rv == False: 252 # try to execute the event again later 253 later.append(event) 254 log.debug('re-queuing AEEvent') 255 self.last_event = event 256 # put all events that failed to execute back in the queue for later 257 map(self.postEvents, later) 258 return True
259