diff options
author | Gary Bressler <garybressler@nc.rr.com> | 2010-04-06 12:45:04 -0400 |
---|---|---|
committer | Gary Bressler <garybressler@nc.rr.com> | 2010-04-06 12:45:04 -0400 |
commit | c7e3aaebdba7bf880534abd91a383b5543cf0be4 (patch) | |
tree | 048977efdaaa3d60e93c3d21ba29c46a0bfe71c3 /unit_trace/viz/viewer.py | |
parent | 7fdb4dbbbca577efbeec47cd1364eb319346a0cc (diff) |
Making sure everything committed
Diffstat (limited to 'unit_trace/viz/viewer.py')
-rw-r--r-- | unit_trace/viz/viewer.py | 407 |
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 | |||
28 | class GraphArea(gtk.DrawingArea): | 35 | class 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 | ||