summaryrefslogtreecommitdiffstats
path: root/unit_trace/viz/viewer.py
diff options
context:
space:
mode:
authorGary Bressler <garybressler@nc.rr.com>2010-04-06 12:45:04 -0400
committerGary Bressler <garybressler@nc.rr.com>2010-04-06 12:45:04 -0400
commitc7e3aaebdba7bf880534abd91a383b5543cf0be4 (patch)
tree048977efdaaa3d60e93c3d21ba29c46a0bfe71c3 /unit_trace/viz/viewer.py
parent7fdb4dbbbca577efbeec47cd1364eb319346a0cc (diff)
Making sure everything committed
Diffstat (limited to 'unit_trace/viz/viewer.py')
-rw-r--r--unit_trace/viz/viewer.py407
1 files changed, 309 insertions, 98 deletions
diff --git a/unit_trace/viz/viewer.py b/unit_trace/viz/viewer.py
index 909da76..9b8502a 100644
--- a/unit_trace/viz/viewer.py
+++ b/unit_trace/viz/viewer.py
@@ -17,21 +17,28 @@ class GraphContextMenu(gtk.Menu):
17 def __init__(self, selected): 17 def __init__(self, selected):
18 super(GraphContextMenu, self).__init__() 18 super(GraphContextMenu, self).__init__()
19 19
20 for event in selected: 20 if not selected:
21 string = str(event) 21 item = gtk.MenuItem("(No events selected)")
22 if len(string) > GraphContextMenu.MAX_STR_LEN - 3: 22 item.set_sensitive(False)
23 string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...'
24 item = gtk.MenuItem(string)
25 self.append(item) 23 self.append(item)
26 item.show() 24 item.show()
27 25 else:
26 for layer in selected:
27 for event in selected[layer]:
28 string = str(event)
29 if len(string) > GraphContextMenu.MAX_STR_LEN - 3:
30 string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...'
31 item = gtk.MenuItem(string)
32 self.append(item)
33 item.show()
34
28class GraphArea(gtk.DrawingArea): 35class GraphArea(gtk.DrawingArea):
29 HORIZ_PAGE_SCROLL_FACTOR = 10.8 36 HORIZ_PAGE_SCROLL_FACTOR = 10.8
30 HORIZ_STEP_SCROLL_FACTOR = 0.8 37 HORIZ_STEP_SCROLL_FACTOR = 0.8
31 VERT_PAGE_SCROLL_FACTOR = 3.0 38 VERT_PAGE_SCROLL_FACTOR = 3.0
32 VERT_STEP_SCROLL_FACTOR = 0.5 39 VERT_STEP_SCROLL_FACTOR = 0.5
33 40
34 REFRESH_INFLATION_FACTOR = 20.0 41 REFRESH_INFLATION_FACTOR = 4.0
35 42
36 def __init__(self, renderer): 43 def __init__(self, renderer):
37 super(GraphArea, self).__init__() 44 super(GraphArea, self).__init__()
@@ -42,6 +49,7 @@ class GraphArea(gtk.DrawingArea):
42 self.cur_y = 0 49 self.cur_y = 0
43 self.width = 0 50 self.width = 0
44 self.height = 0 51 self.height = 0
52 self.scale = 1.0
45 53
46 self.set_set_scroll_adjustments_signal('set-scroll-adjustments') 54 self.set_set_scroll_adjustments_signal('set-scroll-adjustments')
47 55
@@ -50,6 +58,7 @@ class GraphArea(gtk.DrawingArea):
50 58
51 self.band_rect = None 59 self.band_rect = None
52 self.ctrl_clicked = False 60 self.ctrl_clicked = False
61 self.last_selected = {}
53 self.dirtied_regions = [] 62 self.dirtied_regions = []
54 63
55 self.connect('expose-event', self.expose) 64 self.connect('expose-event', self.expose)
@@ -62,7 +71,7 @@ class GraphArea(gtk.DrawingArea):
62 def expose(self, widget, expose_event, data=None): 71 def expose(self, widget, expose_event, data=None):
63 ctx = widget.window.cairo_create() 72 ctx = widget.window.cairo_create()
64 graph = self.renderer.get_graph() 73 graph = self.renderer.get_graph()
65 graph.update_view(self.cur_x, self.cur_y, self.width, self.height, ctx) 74 graph.update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale, ctx)
66 75
67 # We ourselves didn't update dirtied_regions, so this means that X or the 76 # We ourselves didn't update dirtied_regions, so this means that X or the
68 # window manager must have caused the expose event. So just update the 77 # window manager must have caused the expose event. So just update the
@@ -91,7 +100,30 @@ class GraphArea(gtk.DrawingArea):
91 100
92 def get_graph(self): 101 def get_graph(self):
93 return self.renderer.get_graph() 102 return self.renderer.get_graph()
103
104 MIN_ZOOM_OUT = 0.25
105 MAX_ZOOM_IN = 4.0
106 ZOOM_INCR = 0.25
107
108 def zoom_in(self):
109 scale = self.scale + GraphArea.ZOOM_INCR
110 if scale > GraphArea.MAX_ZOOM_IN:
111 scale = GraphArea.MAX_ZOOM_IN
112 self.set_scale(scale)
113
114 def zoom_out(self):
115 scale = self.scale - GraphArea.ZOOM_INCR
116 if scale < GraphArea.MIN_ZOOM_OUT:
117 scale = GraphArea.MIN_ZOOM_OUT
118 self.set_scale(scale)
119
120 def set_scale(self, scale):
121 if scale == self.scale:
122 return
94 123
124 self.scale = scale
125 self._dirty(0, 0, self.width, self.height)
126
95 def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): 127 def set_scroll_adjustments(self, widget, horizontal, vertical, data=None):
96 graph = self.renderer.get_graph() 128 graph = self.renderer.get_graph()
97 width = graph.get_width() 129 width = graph.get_width()
@@ -110,12 +142,14 @@ class GraphArea(gtk.DrawingArea):
110 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) 142 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width())
111 self.cur_x = max(adjustment.value, 0.0) 143 self.cur_x = max(adjustment.value, 0.0)
112 144
145 self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True)
113 self._dirty(0, 0, self.width, self.height) 146 self._dirty(0, 0, self.width, self.height)
114 147
115 def vertical_value_changed(self, adjustment): 148 def vertical_value_changed(self, adjustment):
116 self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) 149 self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height())
117 self.cur_y = max(adjustment.value, 0.0) 150 self.cur_y = max(adjustment.value, 0.0)
118 151
152 self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True)
119 self._dirty(0, 0, self.width, self.height) 153 self._dirty(0, 0, self.width, self.height)
120 154
121 def size_allocate(self, widget, allocation): 155 def size_allocate(self, widget, allocation):
@@ -129,12 +163,38 @@ class GraphArea(gtk.DrawingArea):
129 height = graph.get_height() 163 height = graph.get_height()
130 164
131 if self.horizontal is not None: 165 if self.horizontal is not None:
132 self.horizontal.set_all(hvalue, 0.0, width, graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR, 166 self.horizontal.set_all(hvalue, 0.0, width + self.width,
167 graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR,
133 graph.get_attrs().maj_sep * GraphArea.HORIZ_PAGE_SCROLL_FACTOR, self.width) 168 graph.get_attrs().maj_sep * GraphArea.HORIZ_PAGE_SCROLL_FACTOR, self.width)
134 if self.vertical is not None: 169 if self.vertical is not None:
135 self.vertical.set_all(vvalue, 0.0, height, graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR, 170 self.vertical.set_all(vvalue, 0.0, height + self.height,
171 graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR,
136 graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height) 172 graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height)
137 173
174 def refresh_events(self, sender, new, old, replace):
175 """Even if the selected areas change on one graph, they change
176 everywhere, and different events might be in different regions on
177 different graphs. So when an event is selected on one graph its
178 region on a completely different graph needs to be updated. This
179 is why this method is here: given the graph that requested the
180 change, the old set of events selected, and the new set of events
181 selected, it updates the selected regions of this graph, and
182 refreshes the drawing of the graph that requested the change."""
183 if self is not sender:
184 self.renderer.get_graph().render_events(new, True)
185
186 self._tag_events(new)
187
188 if self is sender:
189 self._copy_tags(old)
190 self._dirty_events(new)
191 self._dirty_events(old)
192 if replace:
193 self.renderer.get_schedule().set_selected(new)
194 else:
195 self.renderer.get_schedule().remove_selected(old)
196 self.renderer.get_schedule().add_selected(new)
197
138 def _find_max_layer(self, regions): 198 def _find_max_layer(self, regions):
139 max_layer = Canvas.BOTTOM_LAYER 199 max_layer = Canvas.BOTTOM_LAYER
140 for event in regions: 200 for event in regions:
@@ -142,17 +202,46 @@ class GraphArea(gtk.DrawingArea):
142 max_layer = event.get_layer() 202 max_layer = event.get_layer()
143 return max_layer 203 return max_layer
144 204
145 def _update_event_changes(self, list1, list2): 205 def _dirty_events(self, events):
146 # if an event changed selected status, update the bounding area 206 # if an event changed selected status, update the bounding area
147 for event in list1: 207 for layer in events:
148 if event not in list2: 208 for event in events[layer]:
149 x, y, width, height = list1[event].get_dimensions() 209 x, y, width, height = events[layer][event][self].get_dimensions()
150 self._dirty_inflate(x - self.cur_x, y - self.cur_y, width, height, GraphFormat.BORDER_THICKNESS) 210 self._dirty_inflate((x - self.cur_x) * self.scale,
151 for event in list2: 211 (y - self.cur_y) * self.scale,
152 if event not in list1: 212 width * self.scale,
153 x, y, width, height = list2[event].get_dimensions() 213 height * self.scale,
154 self._dirty_inflate(x - self.cur_x, y - self.cur_y, width, height, GraphFormat.BORDER_THICKNESS) 214 GraphFormat.BORDER_THICKNESS * self.scale)
155 215
216 def _tag_events(self, selected):
217 """Some of the events in the collection of selected events might be new.
218 In this case, these events are not yet associated with the region on
219 the graph that they belong to. This method fixes this.
220 """
221 graph = self.renderer.get_graph()
222 for layer in selected:
223 for event in selected[layer]:
224 # note that each graph has its own region associated
225 # with the event
226 selected[layer][event][self] = graph.get_sel_region(event)
227
228 def _copy_tags(self, selected):
229 """When we want to specify a collection of selected events to perform
230 an operation on, we usually do not know ahead of time what regions (in
231 which graphs) the events are associated with. But we have this information
232 stored in the collection of all selected events. This method just copies
233 this information over to the selected variable."""
234 cur_selected = self.renderer.get_schedule().get_selected()
235 for layer in selected:
236 for event in selected[layer]:
237 selected[layer][event] = cur_selected[layer][event]
238
239 def _select_event(self, coll, event):
240 if event.get_layer() not in coll:
241 coll[event.get_layer()] = {}
242 if event not in coll[event.get_layer()]:
243 coll[event.get_layer()][event] = {}
244
156 def motion_notify(self, widget, motion_event, data=None): 245 def motion_notify(self, widget, motion_event, data=None):
157 msg = None 246 msg = None
158 247
@@ -160,7 +249,9 @@ class GraphArea(gtk.DrawingArea):
160 249
161 graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y, 250 graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y,
162 0, 0)], True) 251 0, 0)], True)
252
163 just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0) 253 just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0)
254 was_selected = self.renderer.get_schedule().get_selected()
164 if not just_selected: 255 if not just_selected:
165 msg = '' 256 msg = ''
166 the_event = None 257 the_event = None
@@ -177,30 +268,57 @@ class GraphArea(gtk.DrawingArea):
177 self.emit('update-event-description', the_event, msg) 268 self.emit('update-event-description', the_event, msg)
178 269
179 if self.band_rect is not None: 270 if self.band_rect is not None:
180 selected = {} 271 remove_selected = {}
181 was_selected = self.renderer.get_schedule().get_selected() 272 add_selected = {}
182 if self.ctrl_clicked:
183 selected = copy.copy(was_selected)
184 273
185 # dragging a rectangle 274 # dragging a rectangle
186 x = self.band_rect[0] 275 x = self.band_rect[0]
187 y = self.band_rect[1] 276 y = self.band_rect[1]
188 width = motion_event.x - self.band_rect[0] 277 width = motion_event.x - self.band_rect[0]
189 height = motion_event.y - self.band_rect[1] 278 height = motion_event.y - self.band_rect[1]
279 old_x, old_y, old_width, old_height = self.band_rect
190 280
191 x_p, y_p, width_p, height_p = self._positivify(x, y, width, height) 281 x_p, y_p, width_p, height_p = self._positivify(x, y, width, height)
192 graph.render_surface(self.renderer.get_schedule(), [(x_p, y_p, width_p, height_p)], True) 282 old_x_p, old_y_p, old_width_p, old_height_p = self._positivify(old_x, old_y, old_width, old_height)
193 selected.update(graph.get_selected_regions(x_p, y_p, width_p, height_p)) 283 x_p, y_p, width_p, height_p = int(x_p), int(y_p), int(width_p), int(height_p)
194 self.renderer.get_schedule().set_selected(selected) 284 old_x_p, old_y_p, old_width_p, old_height_p = int(old_x_p), int(old_y_p), int(old_width_p), int(old_height_p)
195 285
196 old_x, old_y, old_width, old_height = self.band_rect 286 new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p))
197 self.band_rect = (x, y, width, height) 287 old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p))
288
289 # To find the events that should be deselected and the new events that should be selected, compute
290 # the set differences between the old and new selection rectangles
291
292 remove_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p))
293 remove_reg.subtract(new_reg)
294 dirty_list = []
295 for rect in remove_reg.get_rectangles():
296 dirty_list.append((rect.x, rect.y, rect.width, rect.height))
297 graph.render_surface(self.renderer.get_schedule(), dirty_list, True)
298 for rect in dirty_list:
299 rx, ry, rwidth, rheight = rect
300 for event in graph.get_selected_regions(rx, ry, rwidth, rheight):
301 if event.get_layer() in was_selected and event in was_selected[event.get_layer()]:
302 self._select_event(remove_selected, event)
303
304 add_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p))
305 add_reg.subtract(old_reg)
306 dirty_list = [(x_p, y_p, width_p, 0), (x_p, y_p, 0, height_p),
307 (x_p, y_p + height_p, width_p, 0), (x_p + width_p, y_p, 0, height_p)]
308 for rect in add_reg.get_rectangles():
309 dirty_list.append((rect.x, rect.y, rect.width, rect.height))
310 graph.render_surface(self.renderer.get_schedule(), dirty_list, True)
311 for rect in dirty_list:
312 rx, ry, rwidth, rheight = rect
313 for event in graph.get_selected_regions(rx, ry, rwidth, rheight):
314 self._select_event(add_selected, event)
315
316 self.band_rect = x, y, width, height
317 self.emit('request-refresh-events', self, add_selected, remove_selected, False)
198 318
199 self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS) 319 self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS)
200 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) 320 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS)
201 321
202 self._update_event_changes(was_selected, selected)
203
204 def button_press(self, widget, button_event, data=None): 322 def button_press(self, widget, button_event, data=None):
205 graph = self.renderer.get_graph() 323 graph = self.renderer.get_graph()
206 324
@@ -215,34 +333,43 @@ class GraphArea(gtk.DrawingArea):
215 333
216 max_layer = self._find_max_layer(just_selected) 334 max_layer = self._find_max_layer(just_selected)
217 335
218 new_now_selected = None
219 was_selected = self.renderer.get_schedule().get_selected() 336 was_selected = self.renderer.get_schedule().get_selected()
220 if self.ctrl_clicked: 337 if not self.ctrl_clicked:
221 new_now_selected = copy.copy(was_selected)
222 else:
223 new_now_selected = {} 338 new_now_selected = {}
224 339
225 # only select those events which were in the top layer (it's 340 more_than_one = 0
226 # not intuitive to click something and then have something 341 for layer in was_selected:
227 # below it get selected). Also, clicking something that 342 for event in was_selected[layer]:
228 # is selected deselects it, if it's the only thing selected 343 more_than_one += 1
229 for event in just_selected: 344 if more_than_one > 1:
230 if event.get_layer() == max_layer: 345 break
231 val = True 346
232 now_selected = self.renderer.get_schedule().get_selected() 347 # only select those events which were in the top layer (it's
233 if (len(now_selected) == 1 or self.ctrl_clicked) and event in now_selected: 348 # not intuitive to click something and then have something
234 val = not event.is_selected 349 # below it get selected). Also, clicking something that
235 if val: 350 # is selected deselects it, if it's the only thing selected
236 new_now_selected[event] = graph.get_sel_region(event) 351 for event in just_selected:
237 elif event in new_now_selected: 352 if event.get_layer() == max_layer:
238 del new_now_selected[event] 353 if not (more_than_one == 1 and event in was_selected):
239 break # only pick one event when just clicking 354 self._select_event(new_now_selected, event)
240 355 break # only pick one event when just clicking
241 self.renderer.get_schedule().set_selected(new_now_selected)
242 #self.last_selected = new_now_selected
243
244 self._update_event_changes(new_now_selected, was_selected)
245 356
357 self.emit('request-refresh-events', self, new_now_selected, was_selected, True)
358 else:
359 remove_selected = {}
360 add_selected = {}
361
362 for event in just_selected:
363 layer = event.get_layer()
364 if layer == max_layer:
365 if layer in was_selected and event in was_selected[layer]:
366 self._select_event(remove_selected, event)
367 else:
368 self._select_event(add_selected, event)
369 break # again, only pick one event because we are just clicking
370
371 self.emit('request-refresh-events', self, add_selected, remove_selected, False)
372
246 if self.band_rect is None: 373 if self.band_rect is None:
247 self.band_rect = (button_event.x, button_event.y, 0, 0) 374 self.band_rect = (button_event.x, button_event.y, 0, 0)
248 375
@@ -252,11 +379,10 @@ class GraphArea(gtk.DrawingArea):
252 379
253 def button_release(self, widget, button_event, data=None): 380 def button_release(self, widget, button_event, data=None):
254 self.ctrl_clicked = False 381 self.ctrl_clicked = False
255 #self.last_selected = copy.copy(self.renderer.get_schedule().get_selected())
256 382
257 if button_event.button == 1: 383 if button_event.button == 1:
258 self._release_band() 384 self._release_band()
259 385
260 def _release_band(self): 386 def _release_band(self):
261 if self.band_rect is not None: 387 if self.band_rect is not None:
262 x, y, width, height = self.band_rect 388 x, y, width, height = self.band_rect
@@ -306,20 +432,45 @@ class GraphWindow(gtk.ScrolledWindow):
306 def __init__(self, renderer): 432 def __init__(self, renderer):
307 super(GraphWindow, self).__init__(None, None) 433 super(GraphWindow, self).__init__(None, None)
308 434
309 self.add_events(gtk.gdk.KEY_PRESS_MASK) 435 self.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.SCROLL_MASK)
310 436
311 self.ctr = 0 437 self.ctr = 0
312 self.connect('key-press-event', self.key_press) 438 self.connect('key-press-event', self.key_press)
439 self.connect('scroll-event', self.scroll)
313 440
314 self.garea = GraphArea(renderer) 441 self.garea = GraphArea(renderer)
315 self.add(self.garea) 442 self.add(self.garea)
316 self.garea.show() 443 self.garea.show()
317 444
318 def key_press(self, widget, key_event): 445 def key_press(self, widget, key_event):
319 hadj = self.get_hadjustment() 446 hadj = self.get_hadjustment()
320 vadj = self.get_vadjustment() 447 vadj = self.get_vadjustment()
321 if hadj is None or vadj is None: 448 if hadj is None or vadj is None:
322 return 449 return
450
451 ctrl_clicked = key_event.state & gtk.gdk.CONTROL_MASK
452
453 keystr = None
454 keymap = {gtk.keysyms.Up : 'up', gtk.keysyms.Down : 'down',
455 gtk.keysyms.Left : 'left', gtk.keysyms.Right : 'right'}
456 if key_event.keyval in keymap:
457 keystr = keymap[key_event.keyval]
458 else:
459 return True
460
461 if ctrl_clicked:
462 keystr = 'ctrl-' + keystr
463
464 if keystr is not None:
465 self._scroll_direction(keystr)
466
467 return True
468
469 def _scroll_direction(self, keystr):
470 hadj = self.get_hadjustment()
471 vadj = self.get_vadjustment()
472 if hadj is None or vadj is None:
473 return
323 474
324 hupper = hadj.get_upper() 475 hupper = hadj.get_upper()
325 hlower = hadj.get_lower() 476 hlower = hadj.get_lower()
@@ -334,8 +485,6 @@ class GraphWindow(gtk.ScrolledWindow):
334 vsincr = vadj.get_step_increment() 485 vsincr = vadj.get_step_increment()
335 vpsize = vadj.get_page_size() 486 vpsize = vadj.get_page_size()
336 487
337 ctrl_clicked = key_event.state & gtk.gdk.CONTROL_MASK
338
339 adj_tuple = {'up' : (vadj, -vsincr, 0, vval, max), 488 adj_tuple = {'up' : (vadj, -vsincr, 0, vval, max),
340 'ctrl-up' : (vadj, -vpincr, 0, vval, max), 489 'ctrl-up' : (vadj, -vpincr, 0, vval, max),
341 'down' : (vadj, vsincr, vupper - vpsize, vval, min), 490 'down' : (vadj, vsincr, vupper - vpsize, vval, min),
@@ -344,22 +493,24 @@ class GraphWindow(gtk.ScrolledWindow):
344 'ctrl-left' : (hadj, -hpincr, 0, hval, max), 493 'ctrl-left' : (hadj, -hpincr, 0, hval, max),
345 'right' : (hadj, hsincr, hupper - hpsize, hval, min), 494 'right' : (hadj, hsincr, hupper - hpsize, hval, min),
346 'ctrl-right' : (hadj, hpincr, hupper - hpsize, hval, min)} 495 'ctrl-right' : (hadj, hpincr, hupper - hpsize, hval, min)}
347
348 keystr = None
349 keymap = {gtk.keysyms.Up : 'up', gtk.keysyms.Down : 'down',
350 gtk.keysyms.Left : 'left', gtk.keysyms.Right : 'right'}
351 if key_event.keyval in keymap:
352 keystr = keymap[key_event.keyval]
353
354 if ctrl_clicked:
355 keystr = 'ctrl-' + keystr
356
357 if keystr is not None:
358 adj, inc, lim, val, extr = adj_tuple[keystr]
359 adj.set_value(extr(val + inc, lim))
360
361 return True
362 496
497 adj, inc, lim, val, extr = adj_tuple[keystr]
498 adj.set_value(extr(val + inc, lim))
499
500 def scroll(self, widget, scroll_event):
501 if scroll_event.state & gtk.gdk.CONTROL_MASK:
502 if scroll_event.direction == gtk.gdk.SCROLL_UP:
503 self.emit('request-zoom-in')
504 elif scroll_event.direction == gtk.gdk.SCROLL_DOWN:
505 self.emit('request-zoom-out')
506 else:
507 if scroll_event.direction == gtk.gdk.SCROLL_UP:
508 self._scroll_direction('up')
509 elif scroll_event.direction == gtk.gdk.SCROLL_DOWN:
510 self._scroll_direction('down')
511
512 return True
513
363 def get_graph_area(self): 514 def get_graph_area(self):
364 return self.garea 515 return self.garea
365 516
@@ -370,38 +521,76 @@ class MainWindow(gtk.Window):
370 def __init__(self): 521 def __init__(self):
371 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) 522 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL)
372 523
524 self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
525
373 self.connect('delete_event', self.delete_event) 526 self.connect('delete_event', self.delete_event)
374 self.connect('destroy', self.die) 527 self.connect('destroy', self.die)
375 528
376 self.file_menu = gtk.Menu() 529 file_menu = gtk.Menu()
377 self.quit_item = gtk.MenuItem('_Quit', True) 530 view_menu = gtk.Menu()
378 self.quit_item.connect('activate', self.quit_item_activate) 531
379 self.quit_item.show() 532 agr = gtk.AccelGroup()
380 self.file_menu.append(self.quit_item) 533 self.add_accel_group(agr)
534
535 quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr)
536 key, mod = gtk.accelerator_parse('Q')
537 quit_item.add_accelerator('activate', agr, key, mod,
538 gtk.ACCEL_VISIBLE)
539 quit_item.connect('activate', self.quit_item_activate)
540 quit_item.show()
541
542 file_menu.append(quit_item)
543
544 file_item = gtk.MenuItem('_File', True)
545 file_item.set_submenu(file_menu)
546 file_item.show()
547
548 zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr)
549 key, mod = gtk.accelerator_parse('<Ctrl>plus')
550 zoom_in_item.add_accelerator('activate', agr, key, mod,
551 gtk.ACCEL_VISIBLE)
552 key, mod = gtk.accelerator_parse('<Ctrl>equal')
553 zoom_in_item.add_accelerator('activate', agr, key, mod, 0)
381 554
382 self.file_item = gtk.MenuItem('_File', True) 555 zoom_in_item.connect('activate', self.zoom_in_item_activate)
383 self.file_item.set_submenu(self.file_menu) 556 zoom_in_item.show()
384 self.file_item.show()
385 557
386 self.menu_bar = gtk.MenuBar() 558 zoom_out_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_OUT, agr)
387 self.menu_bar.append(self.file_item) 559 key, mod = gtk.accelerator_parse('<Ctrl>minus')
560 zoom_out_item.add_accelerator('activate', agr, key, mod,
561 gtk.ACCEL_VISIBLE)
562 key, mod = gtk.accelerator_parse('<Ctrl>underscore')
563 zoom_out_item.add_accelerator('activate', agr, key, mod, 0)
388 564
389 self.menu_bar.show() 565 zoom_out_item.connect('activate', self.zoom_out_item_activate)
566 zoom_out_item.show()
390 567
568 view_menu.append(zoom_in_item)
569 view_menu.append(zoom_out_item)
570
571 view_item = gtk.MenuItem('_View', True)
572 view_item.set_submenu(view_menu)
573 view_item.show()
574
575 menu_bar = gtk.MenuBar()
576 menu_bar.append(file_item)
577 menu_bar.append(view_item)
578
579 menu_bar.show()
391 self.vbox = gtk.VBox(False, 0) 580 self.vbox = gtk.VBox(False, 0)
392 581
393 self.notebook = gtk.Notebook() 582 self.notebook = gtk.Notebook()
394 583
395 self.notebook.last_page = -1 584 self.notebook.last_page = -1
396 self.notebook.connect('switch-page', self.switch_page) 585 self.notebook.connect('switch-page', self.switch_page)
397 586
398 self.notebook.show() 587 self.notebook.show()
399 588
400 self.desc_label = gtk.Label('') 589 self.desc_label = gtk.Label('')
401 self.desc_label.set_justify(gtk.JUSTIFY_LEFT) 590 self.desc_label.set_justify(gtk.JUSTIFY_LEFT)
402 self.desc_label.show() 591 self.desc_label.show()
403 592
404 self.vbox.pack_start(self.menu_bar, False, False, 0) 593 self.vbox.pack_start(menu_bar, False, False, 0)
405 self.vbox.pack_start(self.notebook, True, True, 0) 594 self.vbox.pack_start(self.notebook, True, True, 0)
406 self.vbox.pack_start(self.desc_label, False, False, 0) 595 self.vbox.pack_start(self.desc_label, False, False, 0)
407 self.vbox.show() 596 self.vbox.show()
@@ -412,18 +601,23 @@ class MainWindow(gtk.Window):
412 601
413 self.show() 602 self.show()
414 603
415 def connect_widgets(self, garea): 604 def connect_widgets(self, gwindow):
416 garea.connect('update-event-description', self.update_event_description) 605 gwindow.get_graph_area().connect('update-event-description', self.update_event_description)
417 garea.connect('request-context-menu', self.request_context_menu) 606 gwindow.get_graph_area().connect('request-context-menu', self.request_context_menu)
418 607 gwindow.get_graph_area().connect('request-refresh-events', self.request_refresh_events)
608 gwindow.connect('request-zoom-in', self.zoom_in_item_activate)
609 gwindow.connect('request-zoom-out', self.zoom_out_item_activate)
610
419 def set_renderers(self, renderers): 611 def set_renderers(self, renderers):
420 for i in range(0, self.notebook.get_n_pages()): 612 for i in range(0, self.notebook.get_n_pages()):
421 self.notebook.remove_page(0) 613 self.notebook.remove_page(0)
422 for title in renderers: 614 for title in renderers:
423 gwindow = GraphWindow(renderers[title]) 615 gwindow = GraphWindow(renderers[title])
424 self.connect_widgets(gwindow.get_graph_area()) 616 self.connect_widgets(gwindow)
425 gwindow.show() 617 gwindow.show()
426 self.notebook.append_page(gwindow, gtk.Label(title)) 618 self.notebook.append_page(gwindow, gtk.Label(title))
619 if self.notebook.get_n_pages() > 0:
620 self.notebook.get_nth_page(0).grab_focus()
427 621
428 def switch_page(self, widget, page, page_num): 622 def switch_page(self, widget, page, page_num):
429 if self.notebook.get_nth_page(self.notebook.last_page) is not None: 623 if self.notebook.get_nth_page(self.notebook.last_page) is not None:
@@ -432,7 +626,7 @@ class MainWindow(gtk.Window):
432 new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0] 626 new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0]
433 new_value = old_value - old_ofs + new_ofs 627 new_value = old_value - old_ofs + new_ofs
434 self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value) 628 self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value)
435 629
436 self.notebook.last_page = page_num 630 self.notebook.last_page = page_num
437 631
438 def update_event_description(self, widget, event, msg): 632 def update_event_description(self, widget, event, msg):
@@ -446,10 +640,27 @@ class MainWindow(gtk.Window):
446 640
447 menu = GraphContextMenu(selected) 641 menu = GraphContextMenu(selected)
448 menu.popup(None, None, None, button, time) 642 menu.popup(None, None, None, button, time)
643
644 def request_refresh_events(self, widget, sender, old, new, replace):
645 for i in range(0, self.notebook.get_n_pages()):
646 if self.notebook.get_nth_page(i).get_graph_area() is not sender:
647 self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace)
648 break
649 for i in range(0, self.notebook.get_n_pages()):
650 if self.notebook.get_nth_page(i).get_graph_area() is sender:
651 self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace)
652
653 def zoom_in_item_activate(self, widget):
654 for i in range(0, self.notebook.get_n_pages()):
655 self.notebook.get_nth_page(i).get_graph_area().zoom_in()
656
657 def zoom_out_item_activate(self, widget):
658 for i in range(0, self.notebook.get_n_pages()):
659 self.notebook.get_nth_page(i).get_graph_area().zoom_out()
449 660
450 def quit_item_activate(self, widget): 661 def quit_item_activate(self, widget):
451 self.destroy() 662 self.destroy()
452 663
453 def delete_event(self, widget, event, data=None): 664 def delete_event(self, widget, event, data=None):
454 return False 665 return False
455 666