summaryrefslogtreecommitdiffstats
path: root/unit_trace/viz/viewer.py
diff options
context:
space:
mode:
authorGary Bressler <garybressler@nc.rr.com>2010-04-30 14:04:11 -0400
committerGary Bressler <garybressler@nc.rr.com>2010-04-30 14:04:11 -0400
commitc21dea9f97dd31ad8a915469edc01f44345a469e (patch)
tree9a66ff34dc8c90c695572038b443bc2fd05e8cbf /unit_trace/viz/viewer.py
parent6cfc2cc3bb104ef81e4d9e70f63ab803949f9764 (diff)
Fixes related to scrolling, along with the display of some extra information. Updated documentation.
Diffstat (limited to 'unit_trace/viz/viewer.py')
-rw-r--r--unit_trace/viz/viewer.py246
1 files changed, 155 insertions, 91 deletions
diff --git a/unit_trace/viz/viewer.py b/unit_trace/viz/viewer.py
index 4f7a511..bb1385c 100644
--- a/unit_trace/viz/viewer.py
+++ b/unit_trace/viz/viewer.py
@@ -30,7 +30,7 @@ class GraphContextMenu(gtk.Menu):
30 else: 30 else:
31 for layer in selected: 31 for layer in selected:
32 for event in selected[layer]: 32 for event in selected[layer]:
33 string = str(event) 33 string = event.str_short(info_win.get_unit())
34 if len(string) > GraphContextMenu.MAX_STR_LEN - 3: 34 if len(string) > GraphContextMenu.MAX_STR_LEN - 3:
35 string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...' 35 string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...'
36 item = gtk.MenuItem(string) 36 item = gtk.MenuItem(string)
@@ -68,19 +68,23 @@ class GraphArea(gtk.DrawingArea):
68 self.set_set_scroll_adjustments_signal('set-scroll-adjustments') 68 self.set_set_scroll_adjustments_signal('set-scroll-adjustments')
69 69
70 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | 70 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK |
71 gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK) 71 gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK | gtk.gdk.LEAVE_NOTIFY_MASK)
72 72
73 self.band_rect = None 73 self.band_rect = None
74 self.ctrl_clicked = False 74 self.ctrl_clicked = False
75 self.cursor_pos = None
75 self.last_selected = {} 76 self.last_selected = {}
76 self.dirtied_regions = [] 77 self.dirtied_regions = []
77 78
79 self.get_graph().update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale)
80
78 self.connect('expose-event', self.expose) 81 self.connect('expose-event', self.expose)
79 self.connect('size-allocate', self.size_allocate) 82 self.connect('size-allocate', self.size_allocate)
80 self.connect('set-scroll-adjustments', self.set_scroll_adjustments) 83 self.connect('set-scroll-adjustments', self.set_scroll_adjustments)
81 self.connect('button-press-event', self.button_press) 84 self.connect('button-press-event', self.button_press)
82 self.connect('button-release-event', self.button_release) 85 self.connect('button-release-event', self.button_release)
83 self.connect('motion-notify-event', self.motion_notify) 86 self.connect('motion-notify-event', self.motion_notify)
87 self.connect('leave-notify-event', self.leave_notify)
84 88
85 def output_to_file(self, filename, ftype): 89 def output_to_file(self, filename, ftype):
86 """Outputs the entire graph to a file.""" 90 """Outputs the entire graph to a file."""
@@ -92,17 +96,17 @@ class GraphArea(gtk.DrawingArea):
92 surface = SVGSurface() 96 surface = SVGSurface()
93 else: 97 else:
94 raise ValueError 98 raise ValueError
95 99
96 surface.renew(graph.get_width(), graph.get_height()) 100 surface.renew(graph.get_width(), graph.get_height())
97 graph.set_tmp_surface(surface) 101 graph.set_tmp_surface(surface)
98 graph.render_all(self.renderer.get_schedule()) 102 graph.render_all(self.renderer.get_schedule())
99 surface.write_out(filename) 103 surface.write_out(filename)
100 graph.revert_surface() 104 graph.revert_surface()
101 105
102 def expose(self, widget, expose_event, data=None): 106 def expose(self, widget, expose_event, data=None):
103 ctx = widget.window.cairo_create() 107 ctx = widget.window.cairo_create()
104 graph = self.renderer.get_graph() 108 graph = self.get_graph()
105 graph.update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale, ctx) 109 graph.load_ctx(ctx)
106 110
107 # If X caused the expose event, we need to update the entire area, not just the 111 # If X caused the expose event, we need to update the entire area, not just the
108 # changes we might have made. An expose event caused by X needs to take priority 112 # changes we might have made. An expose event caused by X needs to take priority
@@ -113,11 +117,13 @@ class GraphArea(gtk.DrawingArea):
113 self.dirtied_regions = [(expose_event.area.x, expose_event.area.y, 117 self.dirtied_regions = [(expose_event.area.x, expose_event.area.y,
114 expose_event.area.width, expose_event.area.height)] 118 expose_event.area.width, expose_event.area.height)]
115 119
116 graph.render_surface(self.renderer.get_schedule(), self.dirtied_regions) 120 virt_dirtied_regions = graph.real_to_virt_list(self.dirtied_regions)
121 graph.render_surface(self.renderer.get_schedule(), virt_dirtied_regions)
117 122
118 # render dragging band rectangle, if there is one 123 # render dragging band rectangle, if there is one
119 if self.band_rect is not None: 124 if self.band_rect is not None:
120 x, y, width, height = self.band_rect 125 x, y, width, height = graph.virt_to_real(self.band_rect[0], self.band_rect[1],
126 self.band_rect[2], self.band_rect[3])
121 thickness = GraphFormat.BAND_THICKNESS 127 thickness = GraphFormat.BAND_THICKNESS
122 color = GraphFormat.BAND_COLOR 128 color = GraphFormat.BAND_COLOR
123 129
@@ -136,21 +142,23 @@ class GraphArea(gtk.DrawingArea):
136 142
137 def get_schedule(self): 143 def get_schedule(self):
138 return self.renderer.get_schedule() 144 return self.renderer.get_schedule()
139 145
140 def zoom_in(self): 146 def zoom_in(self):
141 scale = self.scale + GraphArea.ZOOM_INCR 147 scale = self.scale + GraphArea.ZOOM_INCR
142 if scale > GraphArea.MAX_ZOOM_IN: 148 if scale > GraphArea.MAX_ZOOM_IN:
143 scale = GraphArea.MAX_ZOOM_IN 149 scale = GraphArea.MAX_ZOOM_IN
144 self.set_scale(scale) 150 self.set_scale(scale)
145 self.config_scrollbars(self.cur_x, self.cur_y, self.scale) 151 self.config_scrollbars(self.cur_x, self.cur_y, self.scale)
146 152 self._pos_update()
153
147 def zoom_out(self): 154 def zoom_out(self):
148 scale = self.scale - GraphArea.ZOOM_INCR 155 scale = self.scale - GraphArea.ZOOM_INCR
149 if scale < GraphArea.MIN_ZOOM_OUT: 156 if scale < GraphArea.MIN_ZOOM_OUT:
150 scale = GraphArea.MIN_ZOOM_OUT 157 scale = GraphArea.MIN_ZOOM_OUT
151 self.set_scale(scale) 158 self.set_scale(scale)
152 self.config_scrollbars(self.cur_x, self.cur_y, self.scale) 159 self.config_scrollbars(self.cur_x, self.cur_y, self.scale)
153 160 self._pos_update()
161
154 def set_scale(self, scale): 162 def set_scale(self, scale):
155 if scale == self.scale: 163 if scale == self.scale:
156 return 164 return
@@ -165,7 +173,7 @@ class GraphArea(gtk.DrawingArea):
165 value = max(value, self.horizontal.get_lower()) 173 value = max(value, self.horizontal.get_lower())
166 value = min(value, self.horizontal.get_upper() - self.horizontal.get_page_size()) 174 value = min(value, self.horizontal.get_upper() - self.horizontal.get_page_size())
167 self.horizontal.set_value(value) 175 self.horizontal.set_value(value)
168 176
169 def set_vvalue(self, value): 177 def set_vvalue(self, value):
170 if self.vertical is None: 178 if self.vertical is None:
171 return 179 return
@@ -173,7 +181,7 @@ class GraphArea(gtk.DrawingArea):
173 value = max(value, self.vertical.get_lower()) 181 value = max(value, self.vertical.get_lower())
174 value = min(value, self.vertical.get_upper() - self.vertical.get_page_size()) 182 value = min(value, self.vertical.get_upper() - self.vertical.get_page_size())
175 self.vertical.set_value(value) 183 self.vertical.set_value(value)
176 184
177 def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): 185 def set_scroll_adjustments(self, widget, horizontal, vertical, data=None):
178 graph = self.renderer.get_graph() 186 graph = self.renderer.get_graph()
179 width = graph.get_width() 187 width = graph.get_width()
@@ -192,20 +200,27 @@ class GraphArea(gtk.DrawingArea):
192 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) 200 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width())
193 self.cur_x = max(adjustment.value, 0.0) 201 self.cur_x = max(adjustment.value, 0.0)
194 202
195 self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) 203 self.get_graph().render_surface(self.renderer.get_schedule(),
204 [self.get_graph().real_to_virt(0, 0, self.width, self.height)], True)
196 self._dirty(0, 0, self.width, self.height) 205 self._dirty(0, 0, self.width, self.height)
197 206
207 self._pos_update()
208
198 def vertical_value_changed(self, adjustment): 209 def vertical_value_changed(self, adjustment):
199 self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) 210 self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height())
200 self.cur_y = max(adjustment.value, 0.0) 211 self.cur_y = max(adjustment.value, 0.0)
201 212
202 self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) 213 self.get_graph().render_surface(self.renderer.get_schedule(),
214 [self.get_graph().real_to_virt(0, 0, self.width, self.height)], True)
203 self._dirty(0, 0, self.width, self.height) 215 self._dirty(0, 0, self.width, self.height)
204 216
217 self._pos_update()
218
205 def size_allocate(self, widget, allocation): 219 def size_allocate(self, widget, allocation):
206 self.width = allocation.width 220 self.width = allocation.width
207 self.height = allocation.height 221 self.height = allocation.height
208 self.config_scrollbars(self.cur_x, self.cur_y, self.scale) 222 self.config_scrollbars(self.cur_x, self.cur_y, self.scale)
223 self._pos_update()
209 224
210 def config_scrollbars(self, hvalue, vvalue, scale): 225 def config_scrollbars(self, hvalue, vvalue, scale):
211 graph = self.renderer.get_graph() 226 graph = self.renderer.get_graph()
@@ -224,11 +239,11 @@ class GraphArea(gtk.DrawingArea):
224 graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR / scale, 239 graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR / scale,
225 self.height / scale) 240 self.height / scale)
226 self.set_vvalue(vvalue) 241 self.set_vvalue(vvalue)
227 242
228 def refresh_all(self): 243 def refresh_all(self):
229 """Refreshes the entire visible screen.""" 244 """Refreshes the entire visible screen."""
230 self._dirty(0, 0, self.width, self.height) 245 self._dirty(0, 0, self.width, self.height)
231 246
232 def refresh_events(self, sender, new, old, replace): 247 def refresh_events(self, sender, new, old, replace):
233 """Even if the selected areas change on one graph, they change 248 """Even if the selected areas change on one graph, they change
234 everywhere, and different events might be in different regions on 249 everywhere, and different events might be in different regions on
@@ -240,7 +255,7 @@ class GraphArea(gtk.DrawingArea):
240 refreshes the drawing of the graph that requested the change.""" 255 refreshes the drawing of the graph that requested the change."""
241 if self is not sender: 256 if self is not sender:
242 self.renderer.get_graph().render_events(new, True) 257 self.renderer.get_graph().render_events(new, True)
243 258
244 self._tag_events(new) 259 self._tag_events(new)
245 260
246 if self is sender: 261 if self is sender:
@@ -252,7 +267,7 @@ class GraphArea(gtk.DrawingArea):
252 else: 267 else:
253 self.renderer.get_schedule().remove_selected(old) 268 self.renderer.get_schedule().remove_selected(old)
254 self.renderer.get_schedule().add_selected(new) 269 self.renderer.get_schedule().add_selected(new)
255 270
256 self.emit('update-sel-changes', self.renderer.get_schedule().get_selected()) 271 self.emit('update-sel-changes', self.renderer.get_schedule().get_selected())
257 272
258 def _find_max_layer(self, regions): 273 def _find_max_layer(self, regions):
@@ -267,11 +282,9 @@ class GraphArea(gtk.DrawingArea):
267 for layer in events: 282 for layer in events:
268 for event in events[layer]: 283 for event in events[layer]:
269 if not events[layer][event][self].is_dummy(): 284 if not events[layer][event][self].is_dummy():
270 x, y, width, height = events[layer][event][self].get_dimensions() 285 dim = events[layer][event][self].get_dimensions()
271 self._dirty_inflate((x - self.cur_x) * self.scale, 286 x, y, width, height = self.get_graph().virt_to_real(dim[0], dim[1], dim[2], dim[3])
272 (y - self.cur_y) * self.scale, 287 self._dirty_inflate(x, y, width, height,
273 width * self.scale,
274 height * self.scale,
275 GraphFormat.BORDER_THICKNESS * self.scale) 288 GraphFormat.BORDER_THICKNESS * self.scale)
276 289
277 def _tag_events(self, selected): 290 def _tag_events(self, selected):
@@ -303,15 +316,27 @@ class GraphArea(gtk.DrawingArea):
303 if event not in coll[event.get_layer()]: 316 if event not in coll[event.get_layer()]:
304 coll[event.get_layer()][event] = {} 317 coll[event.get_layer()][event] = {}
305 318
319 def leave_notify(self, widget, leave_event):
320 self.cursor_pos = None
321 self.emit('update-event-description', None)
322
306 def motion_notify(self, widget, motion_event, data=None): 323 def motion_notify(self, widget, motion_event, data=None):
307 msg = None 324 self.cursor_pos = (motion_event.x, motion_event.y)
325 self._pos_update()
326
327 def _pos_update(self):
328 self.get_graph().update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale)
329
330 if self.cursor_pos is None:
331 return
308 332
309 graph = self.renderer.get_graph() 333 graph = self.renderer.get_graph()
310 334
311 graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y, 335 graph.render_surface(self.renderer.get_schedule(),
312 0, 0)], True) 336 [graph.real_to_virt(self.cursor_pos[0], self.cursor_pos[1], 0, 0)], True)
313 337
314 just_selected = graph.get_intersecting_regions(motion_event.x, motion_event.y, 0, 0) 338 cur_x_v, cur_y_v, w, h = graph.real_to_virt(self.cursor_pos[0], self.cursor_pos[1], 0, 0)
339 just_selected = graph.get_intersecting_regions(cur_x_v, cur_y_v, 0, 0)
315 was_selected = self.renderer.get_schedule().get_selected() 340 was_selected = self.renderer.get_schedule().get_selected()
316 if not just_selected: 341 if not just_selected:
317 msg = '' 342 msg = ''
@@ -323,10 +348,11 @@ class GraphArea(gtk.DrawingArea):
323 the_event = event 348 the_event = event
324 break 349 break
325 350
326 msg = str(the_event) 351 msg = the_event.str_short(graph.get_attrs().unit)
327 352
328 self.emit('update-event-description', the_event, msg) 353 self.emit('update-event-description', the_event)
329 354
355 virt_x, virt_y, w, h = graph.real_to_virt(self.cursor_pos[0], self.cursor_pos[1], 0, 0)
330 if self.band_rect is not None: 356 if self.band_rect is not None:
331 remove_selected = {} 357 remove_selected = {}
332 add_selected = {} 358 add_selected = {}
@@ -334,14 +360,12 @@ class GraphArea(gtk.DrawingArea):
334 # dragging a rectangle 360 # dragging a rectangle
335 x = self.band_rect[0] 361 x = self.band_rect[0]
336 y = self.band_rect[1] 362 y = self.band_rect[1]
337 width = motion_event.x - self.band_rect[0] 363 width = virt_x - self.band_rect[0]
338 height = motion_event.y - self.band_rect[1] 364 height = virt_y - self.band_rect[1]
339 old_x, old_y, old_width, old_height = self.band_rect 365 old_x, old_y, old_width, old_height = self.band_rect
340 366
341 x_p, y_p, width_p, height_p = self._positivify(x, y, width, height) 367 x_p, y_p, width_p, height_p = self._positivify_int(x, y, width, height)
342 old_x_p, old_y_p, old_width_p, old_height_p = self._positivify(old_x, old_y, old_width, old_height) 368 old_x_p, old_y_p, old_width_p, old_height_p = self._positivify_int(old_x, old_y, old_width, old_height)
343 x_p, y_p, width_p, height_p = int(x_p), int(y_p), int(width_p), int(height_p)
344 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)
345 369
346 new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) 370 new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p))
347 old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) 371 old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p))
@@ -354,7 +378,7 @@ class GraphArea(gtk.DrawingArea):
354 dirty_list = [] 378 dirty_list = []
355 for rect in remove_reg.get_rectangles(): 379 for rect in remove_reg.get_rectangles():
356 dirty_list.append((rect.x, rect.y, rect.width, rect.height)) 380 dirty_list.append((rect.x, rect.y, rect.width, rect.height))
357 graph.render_surface(self.renderer.get_schedule(), dirty_list, True) 381 graph.render_surface(self.renderer.get_schedule(), graph.real_to_virt_list(dirty_list), True)
358 for rect in dirty_list: 382 for rect in dirty_list:
359 rx, ry, rwidth, rheight = rect 383 rx, ry, rwidth, rheight = rect
360 i_regs = graph.get_intersecting_regions(rx, ry, rwidth, rheight) 384 i_regs = graph.get_intersecting_regions(rx, ry, rwidth, rheight)
@@ -370,6 +394,7 @@ class GraphArea(gtk.DrawingArea):
370 for rect in add_reg.get_rectangles(): 394 for rect in add_reg.get_rectangles():
371 dirty_list.append((rect.x, rect.y, rect.width, rect.height)) 395 dirty_list.append((rect.x, rect.y, rect.width, rect.height))
372 graph.render_surface(self.renderer.get_schedule(), dirty_list, True) 396 graph.render_surface(self.renderer.get_schedule(), dirty_list, True)
397
373 for rect in dirty_list: 398 for rect in dirty_list:
374 rx, ry, rwidth, rheight = rect 399 rx, ry, rwidth, rheight = rect
375 i_regs = graph.get_intersecting_regions(rx, ry, rwidth, rheight) 400 i_regs = graph.get_intersecting_regions(rx, ry, rwidth, rheight)
@@ -380,8 +405,10 @@ class GraphArea(gtk.DrawingArea):
380 self.band_rect = x, y, width, height 405 self.band_rect = x, y, width, height
381 self.emit('request-refresh-events', self, add_selected, remove_selected, False) 406 self.emit('request-refresh-events', self, add_selected, remove_selected, False)
382 407
383 self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS) 408 x_r, y_r, width_r, height_r = graph.virt_to_real(x, y, width, height)
384 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) 409 old_x_r, old_y_r, old_width_r, old_height_r = graph.virt_to_real(old_x, old_y, old_width, old_height)
410 self._dirty_rect_border(old_x_r, old_y_r, old_width_r, old_height_r, GraphFormat.BAND_THICKNESS * self.scale)
411 self._dirty_rect_border(x_r, y_r, width_r, height_r, GraphFormat.BAND_THICKNESS * self.scale)
385 412
386 def button_press(self, widget, button_event, data=None): 413 def button_press(self, widget, button_event, data=None):
387 graph = self.renderer.get_graph() 414 graph = self.renderer.get_graph()
@@ -391,10 +418,11 @@ class GraphArea(gtk.DrawingArea):
391 if button_event.button == 1: 418 if button_event.button == 1:
392 self.left_button_start_coor = (button_event.x, button_event.y) 419 self.left_button_start_coor = (button_event.x, button_event.y)
393 graph.render_surface(self.renderer.get_schedule(), \ 420 graph.render_surface(self.renderer.get_schedule(), \
394 [(button_event.x, button_event.y, 0, 0)], True) 421 [graph.real_to_virt(button_event.x, button_event.y, 0, 0)], True)
395 422
396 just_selected = graph.get_intersecting_regions(button_event.x, button_event.y, 0, 0) 423 b_x_r, b_y_r, w, h = graph.real_to_virt(button_event.x, button_event.y, 0, 0)
397 424 just_selected = graph.get_intersecting_regions(b_x_r, b_y_r, 0, 0)
425
398 max_layer = self._find_max_layer(just_selected) 426 max_layer = self._find_max_layer(just_selected)
399 427
400 was_selected = self.renderer.get_schedule().get_selected() 428 was_selected = self.renderer.get_schedule().get_selected()
@@ -418,7 +446,7 @@ class GraphArea(gtk.DrawingArea):
418 and event in was_selected[max_layer]): 446 and event in was_selected[max_layer]):
419 self._select_event(new_now_selected, event) 447 self._select_event(new_now_selected, event)
420 break # only pick one event when just clicking 448 break # only pick one event when just clicking
421 449
422 self.emit('request-refresh-events', self, new_now_selected, was_selected, True) 450 self.emit('request-refresh-events', self, new_now_selected, was_selected, True)
423 else: 451 else:
424 remove_selected = {} 452 remove_selected = {}
@@ -435,7 +463,7 @@ class GraphArea(gtk.DrawingArea):
435 self.emit('request-refresh-events', self, add_selected, remove_selected, False) 463 self.emit('request-refresh-events', self, add_selected, remove_selected, False)
436 464
437 if self.band_rect is None: 465 if self.band_rect is None:
438 self.band_rect = (button_event.x, button_event.y, 0, 0) 466 self.band_rect = (b_x_r, b_y_r, 0, 0)
439 467
440 elif button_event.button == 3: 468 elif button_event.button == 3:
441 self._release_band() 469 self._release_band()
@@ -456,7 +484,8 @@ class GraphArea(gtk.DrawingArea):
456 def _release_band(self): 484 def _release_band(self):
457 if self.band_rect is not None: 485 if self.band_rect is not None:
458 x, y, width, height = self.band_rect 486 x, y, width, height = self.band_rect
459 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) 487 x_r, y_r, width_r, height_r = self.get_graph().virt_to_real(x, y, width, height)
488 self._dirty_rect_border(x_r, y_r, width_r, height_r, GraphFormat.BAND_THICKNESS * self.scale)
460 self.band_rect = None 489 self.band_rect = None
461 490
462 def _dirty(self, x, y, width, height): 491 def _dirty(self, x, y, width, height):
@@ -498,6 +527,10 @@ class GraphArea(gtk.DrawingArea):
498 527
499 return x, y, width, height 528 return x, y, width, height
500 529
530 def _positivify_int(self, x, y, width, height):
531 p = self._positivify(x, y, width, height)
532 return (int(p[0]), int(p[1]), int(p[2]), int(p[3]))
533
501class GraphWindow(gtk.ScrolledWindow): 534class GraphWindow(gtk.ScrolledWindow):
502 def __init__(self, renderer): 535 def __init__(self, renderer):
503 super(GraphWindow, self).__init__(None, None) 536 super(GraphWindow, self).__init__(None, None)
@@ -596,7 +629,7 @@ class MainWindow(gtk.Window):
596 629
597 def __init__(self): 630 def __init__(self):
598 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) 631 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL)
599 632
600 self.add_events(gtk.gdk.BUTTON_PRESS_MASK) 633 self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
601 634
602 self.connect('delete_event', self.delete_event) 635 self.connect('delete_event', self.delete_event)
@@ -608,11 +641,11 @@ class MainWindow(gtk.Window):
608 641
609 agr = gtk.AccelGroup() 642 agr = gtk.AccelGroup()
610 self.add_accel_group(agr) 643 self.add_accel_group(agr)
611 644
612 # list of file items that are clickable if and only if at 645 # list of file items that are clickable if and only if at
613 # least one graph is viewable 646 # least one graph is viewable
614 self.sensitive_menu_items = [] 647 self.sensitive_menu_items = []
615 648
616 save_item = gtk.ImageMenuItem('_Save Graph to File') 649 save_item = gtk.ImageMenuItem('_Save Graph to File')
617 img = gtk.Image() 650 img = gtk.Image()
618 img.set_from_stock(gtk.STOCK_SAVE_AS, gtk.ICON_SIZE_MENU) 651 img.set_from_stock(gtk.STOCK_SAVE_AS, gtk.ICON_SIZE_MENU)
@@ -623,7 +656,7 @@ class MainWindow(gtk.Window):
623 save_item.connect('activate', self.save_item_activate) 656 save_item.connect('activate', self.save_item_activate)
624 save_item.set_sensitive(False) 657 save_item.set_sensitive(False)
625 self.sensitive_menu_items.append(save_item) 658 self.sensitive_menu_items.append(save_item)
626 659
627 quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr) 660 quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr)
628 key, mod = gtk.accelerator_parse('<Ctrl>Q') 661 key, mod = gtk.accelerator_parse('<Ctrl>Q')
629 quit_item.add_accelerator('activate', agr, key, mod, 662 quit_item.add_accelerator('activate', agr, key, mod,
@@ -636,8 +669,8 @@ class MainWindow(gtk.Window):
636 file_item = gtk.MenuItem('_File', True) 669 file_item = gtk.MenuItem('_File', True)
637 file_item.set_submenu(file_menu) 670 file_item.set_submenu(file_menu)
638 671
639 move_item = gtk.ImageMenuItem('_Move to Time') 672 move_item = gtk.ImageMenuItem('_Jump to Time')
640 key, mod = gtk.accelerator_parse('<Ctrl>M') 673 key, mod = gtk.accelerator_parse('<Ctrl>J')
641 img = gtk.Image() 674 img = gtk.Image()
642 img.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU) 675 img.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
643 move_item.set_image(img) 676 move_item.set_image(img)
@@ -645,7 +678,7 @@ class MainWindow(gtk.Window):
645 gtk.ACCEL_VISIBLE) 678 gtk.ACCEL_VISIBLE)
646 move_item.set_sensitive(False) 679 move_item.set_sensitive(False)
647 self.sensitive_menu_items.append(move_item) 680 self.sensitive_menu_items.append(move_item)
648 681
649 move_item.connect('activate', self.move_to_time_activate) 682 move_item.connect('activate', self.move_to_time_activate)
650 683
651 self.colors_item = gtk.ImageMenuItem('Co_lors') 684 self.colors_item = gtk.ImageMenuItem('Co_lors')
@@ -656,14 +689,14 @@ class MainWindow(gtk.Window):
656 self.colors_item.add_accelerator('activate', agr, key, mod, 689 self.colors_item.add_accelerator('activate', agr, key, mod,
657 gtk.ACCEL_VISIBLE) 690 gtk.ACCEL_VISIBLE)
658 self.colors_item.set_sensitive(False) 691 self.colors_item.set_sensitive(False)
659 692
660 self.colors_item.connect('activate', self.colors_activate) 693 self.colors_item.connect('activate', self.colors_activate)
661 694
662 edit_menu.append(self.colors_item) 695 edit_menu.append(self.colors_item)
663 696
664 edit_item = gtk.MenuItem('_Edit', True) 697 edit_item = gtk.MenuItem('_Edit', True)
665 edit_item.set_submenu(edit_menu) 698 edit_item.set_submenu(edit_menu)
666 699
667 zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr) 700 zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr)
668 key, mod = gtk.accelerator_parse('<Ctrl>plus') 701 key, mod = gtk.accelerator_parse('<Ctrl>plus')
669 zoom_in_item.add_accelerator('activate', agr, key, mod, 702 zoom_in_item.add_accelerator('activate', agr, key, mod,
@@ -701,20 +734,23 @@ class MainWindow(gtk.Window):
701 self.notebook.last_page = -1 734 self.notebook.last_page = -1
702 self.notebook.connect('switch-page', self.switch_page) 735 self.notebook.connect('switch-page', self.switch_page)
703 736
704 self.desc_label = gtk.Label('') 737 self.event_label = gtk.Label('')
705 self.desc_label.set_alignment(0.0, 0.0) 738 self.job_run_label = gtk.Label('')
739 self.job_rd_label = gtk.Label('')
706 740
707 self.vbox.pack_start(menu_bar, False, False, 0) 741 self.vbox.pack_start(menu_bar, False, False, 0)
708 self.vbox.pack_start(self.notebook, True, True, 0) 742 self.vbox.pack_start(self.notebook, True, True, 0)
709 self.vbox.pack_start(self.desc_label, False, False, 0) 743 self.vbox.pack_start(self.event_label, False, False, 0)
710 744 self.vbox.pack_start(self.job_run_label, False, False, 0)
745 self.vbox.pack_start(self.job_rd_label, False, False, 0)
711 self.add(self.vbox) 746 self.add(self.vbox)
712 747
713 self.info_win = InfoWindow() 748 self.info_win = InfoWindow()
749 self.text_input_dialog = TextInputDialog('Move to Time', 'how is babby formed?', self)
714 self.color_dialog = gtk.ColorSelectionDialog('Choose Item Color') 750 self.color_dialog = gtk.ColorSelectionDialog('Choose Item Color')
715 751
716 self.cur_item_no = None 752 self.cur_item_no = None
717 753
718 self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ) 754 self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ)
719 755
720 self.set_title('Unit-Trace Visualizer') 756 self.set_title('Unit-Trace Visualizer')
@@ -736,7 +772,7 @@ class MainWindow(gtk.Window):
736 self.connect_widgets(gwindow) 772 self.connect_widgets(gwindow)
737 gwindow.show() 773 gwindow.show()
738 self.notebook.append_page(gwindow, gtk.Label(title)) 774 self.notebook.append_page(gwindow, gtk.Label(title))
739 775
740 if self.notebook.get_n_pages() > 0: 776 if self.notebook.get_n_pages() > 0:
741 self.notebook.get_nth_page(0).grab_focus() 777 self.notebook.get_nth_page(0).grab_focus()
742 778
@@ -750,14 +786,14 @@ class MainWindow(gtk.Window):
750 def update_sel_changes(self, widget, selected): 786 def update_sel_changes(self, widget, selected):
751 """Modify the GUI appropriately to reflect a change in selection.""" 787 """Modify the GUI appropriately to reflect a change in selection."""
752 self.selected = selected 788 self.selected = selected
753 789
754 self.cur_item_no = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \ 790 self.cur_item_no = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \
755 .get_graph().all_one_item_selected(selected) 791 .get_graph().all_one_item_selected(selected)
756 if self.cur_item_no is not None: 792 if self.cur_item_no is not None:
757 self.colors_item.set_sensitive(True) 793 self.colors_item.set_sensitive(True)
758 else: 794 else:
759 self.colors_item.set_sensitive(False) 795 self.colors_item.set_sensitive(False)
760 796
761 def switch_page(self, widget, page, page_num): 797 def switch_page(self, widget, page, page_num):
762 if self.notebook.get_nth_page(self.notebook.last_page) is not None: 798 if self.notebook.get_nth_page(self.notebook.last_page) is not None:
763 old_value = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value() 799 old_value = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value()
@@ -765,11 +801,28 @@ class MainWindow(gtk.Window):
765 new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0] 801 new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0]
766 new_value = old_value - old_ofs + new_ofs 802 new_value = old_value - old_ofs + new_ofs
767 self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value) 803 self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value)
804 unit = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_attrs().unit
805 self.info_win.set_unit(unit)
768 806
769 self.notebook.last_page = page_num 807 self.notebook.last_page = page_num
770 808
771 def update_event_description(self, widget, event, msg): 809 def update_event_description(self, widget, event):
772 self.desc_label.set_text(msg) 810 if event is None:
811 self.event_label.set_text('')
812 self.job_run_label.set_text('')
813 self.job_rd_label.set_text('')
814 return
815
816 unit = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \
817 .get_graph().get_attrs().unit
818 self.event_label.set_text(event.str_short(unit))
819 job = event.get_job()
820 if job is None:
821 self.job_run_label.set_text('')
822 self.job_rd_label.set_text('')
823 else:
824 self.job_run_label.set_text(job.str_run_short(unit))
825 self.job_rd_label.set_text(job.str_rd_short(unit))
773 826
774 def request_context_menu(self, widget, gdk_event, selected): 827 def request_context_menu(self, widget, gdk_event, selected):
775 button = 0 828 button = 0
@@ -789,23 +842,34 @@ class MainWindow(gtk.Window):
789 if self.notebook.get_nth_page(i).get_graph_area() is sender: 842 if self.notebook.get_nth_page(i).get_graph_area() is sender:
790 self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) 843 self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace)
791 break 844 break
792 845
793 def move_to_time_activate(self, widget): 846 def move_to_time_activate(self, widget):
794 dialog = TextInputDialog('Move to Time', 'What time to move to?', self) 847 unit = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \
848 .get_graph().get_attrs().unit
849
850 self.text_input_dialog.set_prompt('What time to move to (default ' + str(unit) + ') ?')
795 851
796 err = True 852 err = True
797 while err: 853 while err:
798 ret = dialog.run() 854 ret = self.text_input_dialog.run()
799 855
800 if ret == gtk.RESPONSE_ACCEPT: 856 if ret == gtk.RESPONSE_ACCEPT:
801 err, time = None, None 857 err, time = None, None
802 try: 858 try:
803 time = float(dialog.get_input()) 859 input_tokens = self.text_input_dialog.get_input().split()
804 start, end = self.notebook.get_nth_page(0).get_graph_area().get_schedule().get_time_bounds() 860 if len(input_tokens) > 2:
861 raise ValueError
862
863 if len(input_tokens) > 1:
864 unit = util.parse_unit(input_tokens[1])
865
866 time = unit.native_to_nsec(float(input_tokens[0]))
867 start, end = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() \
868 .get_schedule().get_time_bounds()
805 if time < start or time > end: 869 if time < start or time > end:
806 err = 'Time out of range!' 870 err = 'Time out of range!'
807 except ValueError: 871 except ValueError:
808 err = 'Must input a number!' 872 err = 'Must input a number (with an optional unit)!'
809 873
810 if not err: 874 if not err:
811 for i in xrange(0, self.notebook.get_n_pages()): 875 for i in xrange(0, self.notebook.get_n_pages()):
@@ -820,12 +884,12 @@ class MainWindow(gtk.Window):
820 err) 884 err)
821 err_dialog.set_title('Input Error') 885 err_dialog.set_title('Input Error')
822 err_dialog.run() 886 err_dialog.run()
823 err_dialog.destroy() 887 err_dialog.hide()
824 888
825 else: 889 else:
826 break 890 break
827 891
828 dialog.destroy() 892 self.text_input_dialog.hide()
829 893
830 def zoom_in_item_activate(self, widget): 894 def zoom_in_item_activate(self, widget):
831 for i in range(0, self.notebook.get_n_pages()): 895 for i in range(0, self.notebook.get_n_pages()):
@@ -838,13 +902,13 @@ class MainWindow(gtk.Window):
838 def colors_activate(self, widget): 902 def colors_activate(self, widget):
839 garea = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area() 903 garea = self.notebook.get_nth_page(self.notebook.last_page).get_graph_area()
840 graph = garea.get_renderer().get_graph() 904 graph = garea.get_renderer().get_graph()
841 905
842 906
843 pattern = graph.get_bar_pattern(self.cur_item_no) 907 pattern = graph.get_bar_pattern(self.cur_item_no)
844 908
845 if len(pattern.get_color_list()) > 1: 909 if len(pattern.get_color_list()) > 1:
846 print 'striped patterns not yet supported' 910 print 'striped patterns not yet supported'
847 911
848 r, g, b = pattern.get_color_list()[0] 912 r, g, b = pattern.get_color_list()[0]
849 colorsel = self.color_dialog.get_color_selection() 913 colorsel = self.color_dialog.get_color_selection()
850 cur_color = gtk.gdk.Color(int(r * 65535), int(g * 65535), int(b * 65535)) 914 cur_color = gtk.gdk.Color(int(r * 65535), int(g * 65535), int(b * 65535))
@@ -857,34 +921,34 @@ class MainWindow(gtk.Window):
857 r, g, b = color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0 921 r, g, b = color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0
858 graph.set_bar_pattern(self.cur_item_no, Pattern([(r, g, b)])) 922 graph.set_bar_pattern(self.cur_item_no, Pattern([(r, g, b)]))
859 garea.refresh_all() 923 garea.refresh_all()
860 924
861 self.color_dialog.hide() 925 self.color_dialog.hide()
862 926
863 def save_item_activate(self, widget): 927 def save_item_activate(self, widget):
864 ACCEPTED_TYPES = ('png', 'svg') 928 ACCEPTED_TYPES = ('png', 'svg')
865 929
866 dialog = gtk.FileChooserDialog('Save to Graphics File', self, 930 dialog = gtk.FileChooserDialog('Save to Graphics File', self,
867 gtk.FILE_CHOOSER_ACTION_SAVE, 931 gtk.FILE_CHOOSER_ACTION_SAVE,
868 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, 932 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
869 gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)) 933 gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT))
870 934
871 png_filter = gtk.FileFilter() 935 png_filter = gtk.FileFilter()
872 png_filter.add_mime_type('image/png') 936 png_filter.add_mime_type('image/png')
873 png_filter.set_name('PNG image (*.png)') 937 png_filter.set_name('PNG image (*.png)')
874 dialog.add_filter(png_filter) 938 dialog.add_filter(png_filter)
875 939
876 svg_filter = gtk.FileFilter() 940 svg_filter = gtk.FileFilter()
877 svg_filter.add_mime_type('image/svg+xml') 941 svg_filter.add_mime_type('image/svg+xml')
878 svg_filter.set_name('SVG vector image (*.svg)') 942 svg_filter.set_name('SVG vector image (*.svg)')
879 dialog.add_filter(svg_filter) 943 dialog.add_filter(svg_filter)
880 944
881 filename = None 945 filename = None
882 ftype = None 946 ftype = None
883 while True: 947 while True:
884 if dialog.run() == gtk.RESPONSE_ACCEPT: 948 if dialog.run() == gtk.RESPONSE_ACCEPT:
885 filename = dialog.get_filename() 949 filename = dialog.get_filename()
886 # parse filename for extension 950 # parse filename for extension
887 951
888 parts = filename.split('.') 952 parts = filename.split('.')
889 ftype = None 953 ftype = None
890 if len(parts) == 1: 954 if len(parts) == 1:
@@ -910,7 +974,7 @@ class MainWindow(gtk.Window):
910 filename = None 974 filename = None
911 ftype = None 975 ftype = None
912 break 976 break
913 977
914 if filename is not None: 978 if filename is not None:
915 cursor = gtk.gdk.Cursor(gtk.gdk.WATCH) 979 cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
916 dialog.get_window().set_cursor(cursor) 980 dialog.get_window().set_cursor(cursor)
@@ -919,9 +983,9 @@ class MainWindow(gtk.Window):
919 dialog.get_window().set_cursor(None) 983 dialog.get_window().set_cursor(None)
920 dialog.destroy() 984 dialog.destroy()
921 gobject.idle_add(idle_callback, dialog) # needed to get the cursor to display 985 gobject.idle_add(idle_callback, dialog) # needed to get the cursor to display
922 else: 986 else:
923 dialog.destroy() 987 dialog.destroy()
924 988
925 def quit_item_activate(self, widget): 989 def quit_item_activate(self, widget):
926 self.destroy() 990 self.destroy()
927 991