summaryrefslogtreecommitdiffstats
path: root/unit_trace/viz/viewer.py
diff options
context:
space:
mode:
authorGary Bressler <garybressler@nc.rr.com>2010-03-19 12:55:12 -0400
committerGary Bressler <garybressler@nc.rr.com>2010-03-19 12:55:12 -0400
commitee7ae82ae6df1378203957b7fa77066153e13c36 (patch)
treeceb3b420a5ce80b07e9a23bbef0ec9639dbb384f /unit_trace/viz/viewer.py
parente6dbe1605d8ae52ee65a08e929ed1810bd07d45c (diff)
Fixed some graphical glitches, along with streamlining the access to the visualizer.
Diffstat (limited to 'unit_trace/viz/viewer.py')
-rw-r--r--unit_trace/viz/viewer.py182
1 files changed, 109 insertions, 73 deletions
diff --git a/unit_trace/viz/viewer.py b/unit_trace/viz/viewer.py
index 120c7e7..236a467 100644
--- a/unit_trace/viz/viewer.py
+++ b/unit_trace/viz/viewer.py
@@ -9,6 +9,7 @@ from renderer import *
9import pygtk 9import pygtk
10import gtk 10import gtk
11import gobject 11import gobject
12import copy
12 13
13class GraphContextMenu(gtk.Menu): 14class GraphContextMenu(gtk.Menu):
14 MAX_STR_LEN = 80 15 MAX_STR_LEN = 80
@@ -30,8 +31,7 @@ class GraphArea(gtk.DrawingArea):
30 VERT_PAGE_SCROLL_FACTOR = 3.0 31 VERT_PAGE_SCROLL_FACTOR = 3.0
31 VERT_STEP_SCROLL_FACTOR = 0.5 32 VERT_STEP_SCROLL_FACTOR = 0.5
32 33
33 SELECT_THICKNESS = 1.5 34 REFRESH_INFLATION_FACTOR = 20.0
34 SELECT_COLOR = (0.85, 0.0, 0.0)
35 35
36 def __init__(self, renderer): 36 def __init__(self, renderer):
37 super(GraphArea, self).__init__() 37 super(GraphArea, self).__init__()
@@ -46,11 +46,11 @@ class GraphArea(gtk.DrawingArea):
46 self.set_set_scroll_adjustments_signal('set-scroll-adjustments') 46 self.set_set_scroll_adjustments_signal('set-scroll-adjustments')
47 47
48 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | 48 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK |
49 gtk.gdk.BUTTON_RELEASE_MASK) 49 gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK)
50 50
51 self.left_button_start_coor = None 51 self.band_rect = None
52 self.select_rect = None
53 self.ctrl_clicked = False 52 self.ctrl_clicked = False
53 self.dirtied_regions = []
54 54
55 self.connect('expose-event', self.expose) 55 self.connect('expose-event', self.expose)
56 self.connect('size-allocate', self.size_allocate) 56 self.connect('size-allocate', self.size_allocate)
@@ -59,23 +59,34 @@ class GraphArea(gtk.DrawingArea):
59 self.connect('button-release-event', self.button_release) 59 self.connect('button-release-event', self.button_release)
60 self.connect('motion-notify-event', self.motion_notify) 60 self.connect('motion-notify-event', self.motion_notify)
61 61
62 def expose(self, widget, event, data=None): 62 def expose(self, widget, expose_event, data=None):
63 ctx = widget.window.cairo_create() 63 ctx = widget.window.cairo_create()
64 graph = self.renderer.get_graph() 64 graph = self.renderer.get_graph()
65 graph.update_view(self.cur_x, self.cur_y, self.width, self.height, ctx) 65 graph.update_view(self.cur_x, self.cur_y, self.width, self.height, ctx)
66 graph.render_surface(self.renderer.get_schedule(), 0, 0, self.width, self.height)
67 66
68 # render dragging rectangle, if there is one 67 # We ourselves didn't update dirtied_regions, so this means that X or the
69 if self.select_rect is not None: 68 # window manager must have caused the expose event. So just update the
70 x, y, width, height = self.select_rect 69 # expose_event's bounding area.
71 thickness = GraphArea.SELECT_THICKNESS 70 if not self.dirtied_regions or expose_event.send_event:
72 color = GraphArea.SELECT_COLOR 71 print 'forced expose'
72 self.dirtied_regions = [(expose_event.area.x, expose_event.area.y,
73 expose_event.area.width, expose_event.area.height)]
74
75 graph.render_surface(self.renderer.get_schedule(), self.dirtied_regions)
76
77 # render dragging band rectangle, if there is one
78 if self.band_rect is not None:
79 x, y, width, height = self.band_rect
80 thickness = GraphFormat.BAND_THICKNESS
81 color = GraphFormat.BAND_COLOR
73 82
74 ctx.rectangle(x, y, width, height) 83 ctx.rectangle(x, y, width, height)
75 ctx.set_line_width(thickness) 84 ctx.set_line_width(thickness)
76 ctx.set_source_rgb(color[0], color[1], color[2]) 85 ctx.set_source_rgb(color[0], color[1], color[2])
77 ctx.stroke() 86 ctx.stroke()
78 87
88 self.dirtied_regions = []
89
79 def get_renderer(self): 90 def get_renderer(self):
80 return self.renderer 91 return self.renderer
81 92
@@ -100,15 +111,13 @@ class GraphArea(gtk.DrawingArea):
100 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) 111 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width())
101 self.cur_x = max(adjustment.value, 0.0) 112 self.cur_x = max(adjustment.value, 0.0)
102 113
103 rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) 114 self._dirty(0, 0, self.width, self.height)
104 self.window.invalidate_rect(rect, True)
105 115
106 def vertical_value_changed(self, adjustment): 116 def vertical_value_changed(self, adjustment):
107 self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) 117 self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height())
108 self.cur_y = max(adjustment.value, 0.0) 118 self.cur_y = max(adjustment.value, 0.0)
109 119
110 rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) 120 self._dirty(0, 0, self.width, self.height)
111 self.window.invalidate_rect(rect, True)
112 121
113 def size_allocate(self, widget, allocation): 122 def size_allocate(self, widget, allocation):
114 self.width = allocation.width 123 self.width = allocation.width
@@ -133,13 +142,25 @@ class GraphArea(gtk.DrawingArea):
133 if event.get_layer() > max_layer: 142 if event.get_layer() > max_layer:
134 max_layer = event.get_layer() 143 max_layer = event.get_layer()
135 return max_layer 144 return max_layer
136 145
146 def _update_event_changes(self, list1, list2):
147 # if an event changed selected status, update the bounding area
148 for event in list1:
149 if event not in list2:
150 x, y, width, height = list1[event].get_dimensions()
151 self._dirty_inflate(x - self.cur_x, y - self.cur_y, width, height, GraphFormat.BORDER_THICKNESS)
152 for event in list2:
153 if event not in list1:
154 x, y, width, height = list2[event].get_dimensions()
155 self._dirty_inflate(x - self.cur_x, y - self.cur_y, width, height, GraphFormat.BORDER_THICKNESS)
156
137 def motion_notify(self, widget, motion_event, data=None): 157 def motion_notify(self, widget, motion_event, data=None):
138 msg = None 158 msg = None
139 159
140 graph = self.renderer.get_graph() 160 graph = self.renderer.get_graph()
141 161
142 graph.render_surface(self.renderer.get_schedule(), motion_event.x, motion_event.y, 0, 0, True) 162 graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y,
163 0, 0)], True)
143 just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0) 164 just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0)
144 if not just_selected: 165 if not just_selected:
145 msg = '' 166 msg = ''
@@ -156,25 +177,31 @@ class GraphArea(gtk.DrawingArea):
156 177
157 self.emit('update-event-description', the_event, msg) 178 self.emit('update-event-description', the_event, msg)
158 179
159 if self.left_button_start_coor is not None: 180 if self.band_rect is not None:
160 selected = {} 181 selected = {}
182 was_selected = self.renderer.get_schedule().get_selected()
161 if self.ctrl_clicked: 183 if self.ctrl_clicked:
162 selected = dict(self.last_selected) 184 selected = copy.copy(was_selected)
163 185
164 # dragging a rectangle 186 # dragging a rectangle
165 x = min(self.left_button_start_coor[0], motion_event.x) 187 x = self.band_rect[0]
166 y = min(self.left_button_start_coor[1], motion_event.y) 188 y = self.band_rect[1]
167 width = abs(self.left_button_start_coor[0] - motion_event.x) 189 width = motion_event.x - self.band_rect[0]
168 height = abs(self.left_button_start_coor[1] - motion_event.y) 190 height = motion_event.y - self.band_rect[1]
169 191
170 graph.render_surface(self.renderer.get_schedule(), x, y, width, height, True) 192 x_p, y_p, width_p, height_p = self._positivify(x, y, width, height)
171 selected.update(graph.get_selected_regions(x, y, width, height)) 193 graph.render_surface(self.renderer.get_schedule(), [(x_p, y_p, width_p, height_p)], True)
194 selected.update(graph.get_selected_regions(x_p, y_p, width_p, height_p))
172 self.renderer.get_schedule().set_selected(selected) 195 self.renderer.get_schedule().set_selected(selected)
173 196
174 self.select_rect = (x, y, width, height) 197 old_x, old_y, old_width, old_height = self.band_rect
198 self.band_rect = (x, y, width, height)
175 199
176 self._dirty() 200 self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS)
201 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS)
177 202
203 self._update_event_changes(was_selected, selected)
204
178 def button_press(self, widget, button_event, data=None): 205 def button_press(self, widget, button_event, data=None):
179 graph = self.renderer.get_graph() 206 graph = self.renderer.get_graph()
180 207
@@ -183,15 +210,16 @@ class GraphArea(gtk.DrawingArea):
183 if button_event.button == 1: 210 if button_event.button == 1:
184 self.left_button_start_coor = (button_event.x, button_event.y) 211 self.left_button_start_coor = (button_event.x, button_event.y)
185 graph.render_surface(self.renderer.get_schedule(), \ 212 graph.render_surface(self.renderer.get_schedule(), \
186 button_event.x, button_event.y, 0, 0, True) 213 [(button_event.x, button_event.y, 0, 0)], True)
187 214
188 just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0) 215 just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0)
189 216
190 max_layer = self._find_max_layer(just_selected) 217 max_layer = self._find_max_layer(just_selected)
191 218
192 new_now_selected = None 219 new_now_selected = None
220 was_selected = self.renderer.get_schedule().get_selected()
193 if self.ctrl_clicked: 221 if self.ctrl_clicked:
194 new_now_selected = self.renderer.get_schedule().get_selected() 222 new_now_selected = copy.copy(was_selected)
195 else: 223 else:
196 new_now_selected = {} 224 new_now_selected = {}
197 225
@@ -206,45 +234,75 @@ class GraphArea(gtk.DrawingArea):
206 if (len(now_selected) == 1 or self.ctrl_clicked) and event in now_selected: 234 if (len(now_selected) == 1 or self.ctrl_clicked) and event in now_selected:
207 val = not event.is_selected 235 val = not event.is_selected
208 if val: 236 if val:
209 new_now_selected[event] = None 237 new_now_selected[event] = graph.get_sel_region(event)
210 elif event in new_now_selected: 238 elif event in new_now_selected:
211 del new_now_selected[event] 239 del new_now_selected[event]
212 break # only pick one event when just clicking 240 break # only pick one event when just clicking
213 241
214 self.renderer.get_schedule().set_selected(new_now_selected) 242 self.renderer.get_schedule().set_selected(new_now_selected)
215 self.last_selected = dict(new_now_selected) 243 #self.last_selected = new_now_selected
216 244
217 self._dirty() 245 self._update_event_changes(new_now_selected, was_selected)
246
247 if self.band_rect is None:
248 self.band_rect = (button_event.x, button_event.y, 0, 0)
249
218 elif button_event.button == 3: 250 elif button_event.button == 3:
219 self._release_band() 251 self._release_band()
220 self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected()) 252 self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected())
221 253
222 def button_release(self, widget, button_event, data=None): 254 def button_release(self, widget, button_event, data=None):
223 self.ctrl_clicked = False 255 self.ctrl_clicked = False
224 self.last_selected = dict(self.renderer.get_schedule().get_selected()) 256 #self.last_selected = copy.copy(self.renderer.get_schedule().get_selected())
225 257
226 if button_event.button == 1: 258 if button_event.button == 1:
227 self._release_band() 259 self._release_band()
228 260
229 def _release_band(self): 261 def _release_band(self):
230 old_select_rect = self.select_rect 262 if self.band_rect is not None:
231 self.left_button_start_coor = None 263 x, y, width, height = self.band_rect
232 self.select_rect = None 264 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS)
233 if old_select_rect is not None: 265 self.band_rect = None
234 self._dirty()
235 266
236 def _dirty(self, x=None, y=None, width=None, height=None): 267 def _dirty(self, x, y, width, height):
237 if x is None: 268 x = max(int(math.floor(x)), 0)
238 x = 0 269 y = max(int(math.floor(y)), 0)
239 if y is None: 270 width = min(int(math.ceil(width)), self.width)
240 y = 0 271 height = min(int(math.ceil(height)), self.height)
241 if width is None: 272
242 width = self.width 273 self.dirtied_regions.append((x, y, width, height))
243 if height is None: 274
244 height = self.height
245 rect = gtk.gdk.Rectangle(x, y, width, height) 275 rect = gtk.gdk.Rectangle(x, y, width, height)
246 self.window.invalidate_rect(rect, True) 276 self.window.invalidate_rect(rect, True)
247 277
278 def _dirty_inflate(self, x, y, width, height, thickness):
279 t = thickness * GraphArea.REFRESH_INFLATION_FACTOR
280 x -= t / 2.0
281 y -= t / 2.0
282 width += t
283 height += t
284 self._dirty(x, y, width, height)
285
286 def _dirty_rect_border(self, x, y, width, height, thickness):
287 # support rectangles with negative width and height (i.e. -width = width, but going leftwards
288 # instead of rightwards)
289 x, y, width, height = self._positivify(x, y, width, height)
290
291 self._dirty_inflate(x, y, width, 0, thickness)
292 self._dirty_inflate(x, y, 0, height, thickness)
293 self._dirty_inflate(x, y + height, width, 0, thickness)
294 self._dirty_inflate(x + width, y, 0, height, thickness)
295
296 def _positivify(self, x, y, width, height):
297 if width < 0:
298 x += width
299 width = -width
300 if height < 0:
301 y += height
302 height = -height
303
304 return x, y, width, height
305
248class GraphWindow(gtk.ScrolledWindow): 306class GraphWindow(gtk.ScrolledWindow):
249 def __init__(self, renderer): 307 def __init__(self, renderer):
250 super(GraphWindow, self).__init__(None, None) 308 super(GraphWindow, self).__init__(None, None)
@@ -310,20 +368,13 @@ class MainWindow(gtk.Window):
310 WINDOW_WIDTH_REQ = 500 368 WINDOW_WIDTH_REQ = 500
311 WINDOW_HEIGHT_REQ = 300 369 WINDOW_HEIGHT_REQ = 300
312 370
313 def __init__(self, request_renderer_change): 371 def __init__(self):
314 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) 372 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL)
315 373
316 self.connect('delete_event', self.delete_event) 374 self.connect('delete_event', self.delete_event)
317 self.connect('destroy', self.die) 375 self.connect('destroy', self.die)
318
319 self.connect('request-renderer-change', request_renderer_change)
320 376
321 self.file_menu = gtk.Menu() 377 self.file_menu = gtk.Menu()
322 self.open_item = gtk.MenuItem('_Open', True)
323 self.open_item.connect('activate', self.open_item_activate)
324 self.file_menu.append(self.open_item)
325 self.open_item.show()
326
327 self.quit_item = gtk.MenuItem('_Quit', True) 378 self.quit_item = gtk.MenuItem('_Quit', True)
328 self.quit_item.connect('activate', self.quit_item_activate) 379 self.quit_item.connect('activate', self.quit_item_activate)
329 self.quit_item.show() 380 self.quit_item.show()
@@ -397,21 +448,6 @@ class MainWindow(gtk.Window):
397 menu = GraphContextMenu(selected) 448 menu = GraphContextMenu(selected)
398 menu.popup(None, None, None, button, time) 449 menu.popup(None, None, None, button, time)
399 450
400 def open_item_activate(self, widget):
401 dialog = gtk.FileChooserDialog('Open File', self,
402 gtk.FILE_CHOOSER_ACTION_OPEN,
403 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
404 gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT))
405 dialog.set_select_multiple(True)
406
407 if dialog.run() == gtk.RESPONSE_ACCEPT:
408 filenames = dialog.get_filenames()
409 dialog.destroy()
410 self.emit('request-renderer-change', filenames, None)
411 else:
412 dialog.destroy()
413
414
415 def quit_item_activate(self, widget): 451 def quit_item_activate(self, widget):
416 self.destroy() 452 self.destroy()
417 453