Module GTKEventDialog
[hide private]
[frames] | no frames]

Source Code for Module GTKEventDialog

  1  ''' 
  2  Defines a gtk dialog for buffering 
  3  L{AEMonitor <AccessEngine.AEMonitor.Base.AEMonitor>} data. 
  4   
  5  @var PUMP_MAX: Maximum number of items to buffer during one timer interval. 
  6  @type PUMP_MAX: integer 
  7  @var PUMP_INTERVAL: Milliseconds between timer fire. 
  8  @type PUMP_INTERVAL: integer 
  9   
 10  See the pygtk class reference at 
 11  U{http://www.pygtk.org/pygtk2reference/gtk-class-reference.html} and the pygtk 
 12  tutorial at U{http://www.pygtk.org/pygtk2tutorial/index.html} for details about 
 13  the construction of gtk GUIs. 
 14   
 15  @author: Peter Parente 
 16  @organization: IBM Corporation 
 17  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
 18   
 19  @author: Frank Zenker 
 20  @organization: IT Science Center Ruegen gGmbH, Germany 
 21  @copyright: Copyright (c) 2007, 2008 ITSC Ruegen 
 22   
 23  @license: I{The BSD License} 
 24  All rights reserved. This program and the accompanying materials are made  
 25  available under the terms of the BSD license which accompanies 
 26  this distribution, and is available at 
 27  U{http://www.opensource.org/licenses/bsd-license.php} 
 28  ''' 
 29   
 30  import pygtk 
 31  pygtk.require('2.0') 
 32  import gtk, gobject 
 33  import warnings, Queue 
 34  from AccessEngine import AEMonitor 
 35  from Tools.i18n import _ 
 36   
 37  # pump constants 
 38  PUMP_MAX = 100 
 39  PUMP_INTERVAL = 250 
 40  WIN_WIDTH = 400 
 41  WIN_HEIGHT = 450 
 42   
43 -class GTKEventDialog(AEMonitor.AEMonitor):
44 ''' 45 Buffers text describing raw C{pyatspi} events. Lets the user choose which 46 events are displayed and to enable or disable logging altogether. Supports the 47 saving of logs to text files on disk. Items are only buffered when the 48 L{window} created by this object does not have the focus to prevent the text 49 box from scrolling while the user is trying to inspect it or select its 50 contents. 51 52 @ivar shown: Names of events to be shown 53 @type shown: list of string 54 @ivar text_buffer: Buffer holding string representations of raw events 55 @type text_buffer: gtk.TextBuffer 56 @ivar text_view: View of past raw events 57 @type text_view: gtk.TextView 58 @ivar window: Top level window 59 @type window: gtk.Window 60 @ivar logging: Is logging to the text view enabled? 61 @type logging: boolean 62 @ivar timer: ID for the timer callback to L{_onPumpQueue} 63 @type timer: integer 64 @ivar queue: Queue of text to buffer 65 @type queue: Queue.Queue 66 @ivar scroll: Scroll the text buffer on the next timer interval? 67 @type scroll: boolean 68 ''' 69 num_windows = 0
70 - def __init__(self):
71 ''' 72 Initializes instance variables. 73 ''' 74 AEMonitor.AEMonitor.__init__(self) 75 self.logging = True 76 self.shown = [] 77 self.window = None 78 self.text_buffer = None 79 self.text_view = None 80 self.timer = None 81 self.queue = Queue.Queue() 82 self.scroll = False
83
84 - def init(self):
85 ''' 86 Creates and shows the monitor window and its components. Calls the parent 87 implementation to set the L{AccessEngine.AEMonitor.Base.AEMonitor.is_init} flag to True. 88 ''' 89 # create and configure the window object 90 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 91 self.window.set_title(self.getName()) 92 self.window.set_size_request(WIN_WIDTH, WIN_HEIGHT) 93 self.window.move((WIN_WIDTH+10)*GTKEventDialog.num_windows, 0) 94 GTKEventDialog.num_windows += 1 95 96 # create the layout box 97 box = gtk.VBox(False, 0) 98 self.window.add(box) 99 100 # create the menu bar using the XML description 101 menu_bar = self._createMenuBar() 102 box.pack_start(menu_bar, False) 103 104 # logging option 105 display_check = gtk.CheckButton(_('_Enable logging')) 106 display_check.set_active(self.logging) 107 display_check.connect('toggled', self._onCheckLogging) 108 box.pack_start(display_check, False) 109 110 # create one text buffer, two views 111 self.text_buffer = gtk.TextBuffer() 112 self.text_view = gtk.TextView(self.text_buffer) 113 self.text_view.set_pixels_below_lines(1) 114 self.text_view.set_wrap_mode(gtk.WRAP_WORD) 115 self.text_view.set_editable(False) 116 117 # make the text views scrollable 118 sw1 = gtk.ScrolledWindow() 119 sw1.add(self.text_view) 120 box.pack_start(sw1, True, True) 121 122 # create timer for pumping text to buffer 123 self.timer = gobject.timeout_add(PUMP_INTERVAL, self._onPumpQueue) 124 125 # show all elements 126 self.window.show_all() 127 128 # set the initialized flag to True 129 super(GTKEventDialog, self).init()
130
131 - def close(self, widget=None):
132 ''' 133 Destroys the window, sets L{logging} to False to prevent further adds to the 134 queue, and removes the timer. Calls the parent implementation to set the 135 L{AccessEngine.AEMonitor.Base.AEMonitor.is_init} flag to False. 136 137 @param widget: Source of the gtk quit event or None if called externally 138 @type widget: gtk.Widget 139 ''' 140 if not self.isInitialized(): 141 return 142 GTKEventDialog.num_windows -= 1 143 self.logging = False 144 gobject.source_remove(self.timer) 145 self.window.destroy() 146 self.window = None 147 del self.queue 148 # set the initialized flag to False 149 super(GTKEventDialog, self).close()
150
151 - def _createMenuBar(self):
152 ''' 153 Creates the main menu bar consisting of a File menu for saving the active 154 buffer, clearing the active buffer, and quiting the program; a Raw menu for 155 selecting which raw events should be buffered; and a View menu for 156 selecting which types of events should be buffered. 157 158 @return: Fully constructed menu bar and menus 159 @rtype: gtk.MenuBar 160 ''' 161 # create an accelerator group 162 acc = gtk.AccelGroup() 163 self.window.add_accel_group(acc) 164 165 # build the file menu 166 file_menu = gtk.Menu() 167 i = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS) 168 i.add_accelerator('activate', acc, ord('S'), gtk.gdk.CONTROL_MASK, 169 gtk.ACCEL_VISIBLE) 170 i.connect('activate', self._onSaveLog) 171 file_menu.append(i) 172 173 i = gtk.ImageMenuItem(gtk.STOCK_CLEAR) 174 i.add_accelerator('activate', acc, ord('L'), gtk.gdk.CONTROL_MASK, 175 gtk.ACCEL_VISIBLE) 176 i.connect('activate', self._onClearLog) 177 file_menu.append(i) 178 179 # assign the top level menus labels 180 file_item = gtk.MenuItem(_('_File')) 181 file_item.set_submenu(file_menu) 182 183 # create the Raw and Task menus dynamically based on what events and tasks 184 # are exposed by the pyatspi and Task packages 185 raw_item = gtk.MenuItem(_('_View')) 186 raw_item.set_submenu(self._createViewMenu()) 187 188 # put the menus on a menubar 189 menu_bar = gtk.MenuBar() 190 menu_bar.append(file_item) 191 menu_bar.append(raw_item) 192 return menu_bar
193
194 - def _createViewMenu(self):
195 ''' 196 Builds a menu with checkable items indicating whether a certain type of 197 event should be buffered. The names of the menu items and whether they are 198 checked or not are gotten from the 199 L{AEMonitor.getEventNames <AccessEngine.AEMonitor.Base.AEMonitor.getEventNames>} 200 and L{AEMonitor.getEventDefaults <AccessEngine.AEMonitor.Base.AEMonitor.getEventDefaults>} 201 methods. 202 203 @return: Fully constructed menu 204 @rtype: gtk.Menu 205 ''' 206 display_menu = gtk.Menu() 207 # iterate over all the names 208 for name in self.getEventNames(): 209 # create a chackable menu item 210 i = gtk.CheckMenuItem(name) 211 # create a callback to onCheckFilters 212 i.connect('activate', self._onCheckFilters, name) 213 # set default listeners 214 if name in self.getEventDefaults(): 215 i.set_active(True) 216 display_menu.append(i) 217 # add additional menu items if needed 218 try: 219 self._addViewMenuItems(display_menu) 220 except NotImplementedError: 221 pass 222 return display_menu
223
224 - def _onSaveLog(self, widget):
225 ''' 226 Saves the text in the L{text_buffer} to a plain text file on disk. Grabs all 227 the text in the active textbox using L{_getActiveLogText}. Uses the standard 228 gtk.FileChooserDialog to request a filename and location for the save. 229 230 @param widget: Source of the gtk activate event 231 @type widget: gtk.Widget 232 ''' 233 # create a file dialog 234 dlg = gtk.FileChooserDialog(_('_Save log'), self.window, 235 gtk.FILE_CHOOSER_ACTION_SAVE) 236 # add OK and cancel buttons 237 dlg.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) 238 dlg.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT) 239 # show the dialog modally (i.e. wait for a response after calling run()) 240 if dlg.run() == gtk.RESPONSE_ACCEPT: 241 # save the log to a file if the user presses OK 242 filename = dlg.get_filename() 243 try: 244 f = file(filename, 'w') 245 f.write(self._getActiveLogText()) 246 f.close() 247 finally: 248 # allow any exceptions to propagate and get printed 249 dlg.destroy() 250 else: 251 dlg.destroy()
252
253 - def _getActiveLogText(self):
254 ''' 255 Grabs all of the text from the text buffer. Used by L{_onSaveLog} to save 256 the buffer to disk. 257 258 @return: All text in the buffer used by the active textbox 259 @rtype: string 260 ''' 261 start = self.text_buffer.get_start_iter() 262 end = self.text_buffer.get_end_iter() 263 return self.text_buffer.get_text(start, end)
264
265 - def _onClearLog(self, widget):
266 ''' 267 Clears the text buffer. 268 269 @param widget: Source of the gtk event 270 @type widget: gtk.Widget 271 ''' 272 self.text_buffer.set_text('')
273
274 - def _onCheckLogging(self, widget):
275 ''' 276 Sets if logging to the text buffer is enabled or not. 277 278 @param widget: Source of the gtk toggled event 279 @type widget: gtk.Widget 280 ''' 281 self.logging = widget.get_active()
282
283 - def _onCheckFilters(self, widget, event_name):
284 ''' 285 Adds or removes an event from the shown list when an item is checked or 286 unchecked respectively in the View menu. 287 288 @param widget: Source of the gtk activate event 289 @type widget: gtk.Widget 290 @param event_name: Name of the event 291 @type event_name: string 292 ''' 293 if widget.get_active(): 294 self.shown.append(event_name) 295 else: 296 self.shown.remove(event_name)
297
298 - def _isShown(self, event_name):
299 ''' 300 Gets if the given event name is in the L{shown} list and if L{logging} is 301 enabled. 302 303 @param event_name: Name of the event 304 @type event_name: string 305 @return: Is the event to be shown? 306 @rtype: boolean 307 ''' 308 return self.logging and event_name in self.shown
309
310 - def _queueText(self, text, tags=None):
311 ''' 312 Queues text for later display in the L{text_view} when the L{window} does 313 not have the focus and when the L{timer} fires. 314 315 @param text: Text to buffer and display 316 @type text: string 317 ''' 318 self.queue.put_nowait((text,tags))
319
320 - def _onPumpQueue(self):
321 ''' 322 Buffers all waiting events as long as the top level window is not the 323 foreground window. Buffering stops immediately when the window is active 324 to allow the user to read and select text without having it scroll. 325 Buffering stops when L{PUMP_MAX} items have been displayed during this call. 326 327 @return: True to ensure this method is called on the next interval 328 @rtype: boolean 329 ''' 330 # count the number of events buffered so we know when to scroll and can 331 # bail out if we're getting flooded 332 count = 0 333 # loop while the top level window is not the foreground window and we have 334 # not buffered more than MAX_PUMP items 335 while count < PUMP_MAX and not self.window.is_active(): 336 try: 337 # get the text to buffer 338 text,tags = self.queue.get_nowait() 339 count += 1 340 except Queue.Empty: 341 break 342 # buffer the text 343 end = self.text_buffer.get_end_iter() 344 if tags is not None: 345 self.text_buffer.insert_with_tags_by_name(end, text, *tags) 346 else: 347 self.text_buffer.insert(end, text) 348 if self.scroll and not self.window.is_active(): 349 # scroll to the bottom 350 self.text_view.scroll_to_iter(self.text_buffer.get_end_iter(), 0.0) 351 self.scroll = False 352 if count: 353 self.scroll = True 354 return True
355
356 - def _addViewMenuItems(self, menu):
357 raise NotImplementedError
358
359 - def _insert_one_tag_into_buffer(self, name, params):
360 ''' 361 Adds one named style tag into the L{TextBuffer <text_buffer>} tag table. 362 These tags can then be used to add style to text that is queued in 363 L{_queueText}. 364 365 @param name: Tag name of this style 366 @type name: string 367 @param params: dictionary of key, value pairs that represent a style. 368 @type params: dictionary 369 ''' 370 tag = gtk.TextTag(name) 371 for key, value in params.iteritems(): 372 tag.set_property(key, value) 373 table = self.text_buffer.get_tag_table() 374 table.add(tag)
375