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
38 PUMP_MAX = 100
39 PUMP_INTERVAL = 250
40 WIN_WIDTH = 400
41 WIN_HEIGHT = 450
42
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
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
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
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
97 box = gtk.VBox(False, 0)
98 self.window.add(box)
99
100
101 menu_bar = self._createMenuBar()
102 box.pack_start(menu_bar, False)
103
104
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
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
118 sw1 = gtk.ScrolledWindow()
119 sw1.add(self.text_view)
120 box.pack_start(sw1, True, True)
121
122
123 self.timer = gobject.timeout_add(PUMP_INTERVAL, self._onPumpQueue)
124
125
126 self.window.show_all()
127
128
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
149 super(GTKEventDialog, self).close()
150
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
162 acc = gtk.AccelGroup()
163 self.window.add_accel_group(acc)
164
165
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
180 file_item = gtk.MenuItem(_('_File'))
181 file_item.set_submenu(file_menu)
182
183
184
185 raw_item = gtk.MenuItem(_('_View'))
186 raw_item.set_submenu(self._createViewMenu())
187
188
189 menu_bar = gtk.MenuBar()
190 menu_bar.append(file_item)
191 menu_bar.append(raw_item)
192 return menu_bar
193
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
208 for name in self.getEventNames():
209
210 i = gtk.CheckMenuItem(name)
211
212 i.connect('activate', self._onCheckFilters, name)
213
214 if name in self.getEventDefaults():
215 i.set_active(True)
216 display_menu.append(i)
217
218 try:
219 self._addViewMenuItems(display_menu)
220 except NotImplementedError:
221 pass
222 return display_menu
223
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
234 dlg = gtk.FileChooserDialog(_('_Save log'), self.window,
235 gtk.FILE_CHOOSER_ACTION_SAVE)
236
237 dlg.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
238 dlg.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
239
240 if dlg.run() == gtk.RESPONSE_ACCEPT:
241
242 filename = dlg.get_filename()
243 try:
244 f = file(filename, 'w')
245 f.write(self._getActiveLogText())
246 f.close()
247 finally:
248
249 dlg.destroy()
250 else:
251 dlg.destroy()
252
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
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
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
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
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
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
331
332 count = 0
333
334
335 while count < PUMP_MAX and not self.window.is_active():
336 try:
337
338 text,tags = self.queue.get_nowait()
339 count += 1
340 except Queue.Empty:
341 break
342
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
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
357 raise NotImplementedError
358
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