summaryrefslogtreecommitdiffstats
path: root/viz/viewer.py
diff options
context:
space:
mode:
Diffstat (limited to 'viz/viewer.py')
-rw-r--r--viz/viewer.py253
1 files changed, 212 insertions, 41 deletions
diff --git a/viz/viewer.py b/viz/viewer.py
index a695473..a7871a7 100644
--- a/viz/viewer.py
+++ b/viz/viewer.py
@@ -3,20 +3,36 @@
3"""GUI stuff.""" 3"""GUI stuff."""
4 4
5from schedule import * 5from schedule import *
6
6from renderer import * 7from renderer import *
7 8
8import pygtk 9import pygtk
9import gtk 10import gtk
10import gobject 11import gobject
12
13class GraphContextMenu(gtk.Menu):
14 MAX_STR_LEN = 80
11 15
16 def __init__(self, selected):
17 super(GraphContextMenu, self).__init__()
18
19 for event in selected:
20 string = str(event)
21 if len(string) > GraphContextMenu.MAX_STR_LEN - 3:
22 string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...'
23 item = gtk.MenuItem(string)
24 self.append(item)
25 item.show()
26
12class GraphArea(gtk.DrawingArea): 27class GraphArea(gtk.DrawingArea):
13 DAREA_WIDTH_REQ = 500
14 DAREA_HEIGHT_REQ = 300
15 HORIZ_PAGE_SCROLL_FACTOR = 4.8 28 HORIZ_PAGE_SCROLL_FACTOR = 4.8
16 HORIZ_STEP_SCROLL_FACTOR = 0.8 29 HORIZ_STEP_SCROLL_FACTOR = 0.8
17 VERT_PAGE_SCROLL_FACTOR = 3.0 30 VERT_PAGE_SCROLL_FACTOR = 3.0
18 VERT_STEP_SCROLL_FACTOR = 0.5 31 VERT_STEP_SCROLL_FACTOR = 0.5
19 32
33 SELECT_THICKNESS = 1.5
34 SELECT_COLOR = (0.85, 0.0, 0.0)
35
20 def __init__(self, renderer): 36 def __init__(self, renderer):
21 super(GraphArea, self).__init__() 37 super(GraphArea, self).__init__()
22 38
@@ -27,25 +43,38 @@ class GraphArea(gtk.DrawingArea):
27 self.width = 0 43 self.width = 0
28 self.height = 0 44 self.height = 0
29 45
30 self.now_selected = []
31
32 self.set_set_scroll_adjustments_signal('set-scroll-adjustments') 46 self.set_set_scroll_adjustments_signal('set-scroll-adjustments')
33 47
34 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)
50
51 self.left_button_start_coor = None
52 self.select_rect = None
53 self.ctrl_clicked = False
35 54
36 self.connect('expose-event', self.expose) 55 self.connect('expose-event', self.expose)
37 self.connect('size-allocate', self.size_allocate) 56 self.connect('size-allocate', self.size_allocate)
38 self.connect('set-scroll-adjustments', self.set_scroll_adjustments) 57 self.connect('set-scroll-adjustments', self.set_scroll_adjustments)
39 self.connect('button-press-event', self.button_press) 58 self.connect('button-press-event', self.button_press)
59 self.connect('button-release-event', self.button_release)
40 self.connect('motion-notify-event', self.motion_notify) 60 self.connect('motion-notify-event', self.motion_notify)
41 61
42 self.set_size_request(GraphArea.DAREA_WIDTH_REQ, GraphArea.DAREA_HEIGHT_REQ)
43
44 def expose(self, widget, event, data=None): 62 def expose(self, widget, event, data=None):
45 ctx = widget.window.cairo_create() 63 ctx = widget.window.cairo_create()
46 graph = self.renderer.get_graph() 64 graph = self.renderer.get_graph()
47 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)
48 graph.render_surface(self.renderer.get_schedule()) 66 graph.render_surface(self.renderer.get_schedule(), 0, 0, self.width, self.height)
67
68 # render dragging rectangle, if there is one
69 if self.select_rect is not None:
70 x, y, width, height = self.select_rect
71 thickness = GraphArea.SELECT_THICKNESS
72 color = GraphArea.SELECT_COLOR
73
74 ctx.rectangle(x, y, width, height)
75 ctx.set_line_width(thickness)
76 ctx.set_source_rgb(color[0], color[1], color[2])
77 ctx.stroke()
49 78
50 def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): 79 def set_scroll_adjustments(self, widget, horizontal, vertical, data=None):
51 graph = self.renderer.get_graph() 80 graph = self.renderer.get_graph()
@@ -55,9 +84,11 @@ class GraphArea(gtk.DrawingArea):
55 self.horizontal = horizontal 84 self.horizontal = horizontal
56 self.vertical = vertical 85 self.vertical = vertical
57 self.config_scrollbars(self.cur_x, self.cur_y) 86 self.config_scrollbars(self.cur_x, self.cur_y)
58 87
59 self.horizontal.connect('value-changed', self.horizontal_value_changed) 88 if self.horizontal is not None:
60 self.vertical.connect('value-changed', self.vertical_value_changed) 89 self.horizontal.connect('value-changed', self.horizontal_value_changed)
90 if self.vertical is not None:
91 self.vertical.connect('value-changed', self.vertical_value_changed)
61 92
62 def horizontal_value_changed(self, adjustment): 93 def horizontal_value_changed(self, adjustment):
63 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) 94 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width())
@@ -85,7 +116,7 @@ class GraphArea(gtk.DrawingArea):
85 116
86 if self.horizontal is not None: 117 if self.horizontal is not None:
87 self.horizontal.set_all(hvalue, 0.0, width, graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR, 118 self.horizontal.set_all(hvalue, 0.0, width, graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR,
88 graph.get_attrs().maj_sep * GraphArea.HORIZ_PAGE_SCROLL_FACTOR, self.width) 119 graph.get_attrs().maj_sep * GraphArea.HORIZ_PAGE_SCROLL_FACTOR, self.width)
89 if self.vertical is not None: 120 if self.vertical is not None:
90 self.vertical.set_all(vvalue, 0.0, height, graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR, 121 self.vertical.set_all(vvalue, 0.0, height, graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR,
91 graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height) 122 graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height)
@@ -101,7 +132,9 @@ class GraphArea(gtk.DrawingArea):
101 msg = None 132 msg = None
102 133
103 graph = self.renderer.get_graph() 134 graph = self.renderer.get_graph()
104 just_selected = graph.get_selected_regions(motion_event.x, motion_event.y) 135
136 graph.render_surface(self.renderer.get_schedule(), motion_event.x, motion_event.y, 0, 0, True)
137 just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0)
105 if not just_selected: 138 if not just_selected:
106 msg = '' 139 msg = ''
107 the_event = None 140 the_event = None
@@ -116,36 +149,96 @@ class GraphArea(gtk.DrawingArea):
116 msg = str(the_event) 149 msg = str(the_event)
117 150
118 self.emit('update-event-description', the_event, msg) 151 self.emit('update-event-description', the_event, msg)
152
153 if self.left_button_start_coor is not None:
154 selected = {}
155 if self.ctrl_clicked:
156 selected = dict(self.last_selected)
157
158 # dragging a rectangle
159 x = min(self.left_button_start_coor[0], motion_event.x)
160 y = min(self.left_button_start_coor[1], motion_event.y)
161 width = abs(self.left_button_start_coor[0] - motion_event.x)
162 height = abs(self.left_button_start_coor[1] - motion_event.y)
163
164 graph.render_surface(self.renderer.get_schedule(), x, y, width, height, True)
165 selected.update(graph.get_selected_regions(x, y, width, height))
166 self.renderer.get_schedule().set_selected(selected)
167
168 self.select_rect = (x, y, width, height)
119 169
170 self._dirty()
171
120 def button_press(self, widget, button_event, data=None): 172 def button_press(self, widget, button_event, data=None):
121 graph = self.renderer.get_graph() 173 graph = self.renderer.get_graph()
122 174
175 self.ctrl_clicked = button_event.state & gtk.gdk.CONTROL_MASK
176
123 if button_event.button == 1: 177 if button_event.button == 1:
124 just_selected = graph.get_selected_regions(button_event.x, button_event.y) 178 self.left_button_start_coor = (button_event.x, button_event.y)
179 graph.render_surface(self.renderer.get_schedule(), \
180 button_event.x, button_event.y, 0, 0, True)
181
182 just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0)
125 183
126 max_layer = self._find_max_layer(just_selected) 184 max_layer = self._find_max_layer(just_selected)
127 185
186 new_now_selected = None
187 if self.ctrl_clicked:
188 new_now_selected = self.renderer.get_schedule().get_selected()
189 else:
190 new_now_selected = {}
191
128 # only select those events which were in the top layer (it's 192 # only select those events which were in the top layer (it's
129 # not intuitive to click something and then have something 193 # not intuitive to click something and then have something
130 # below it get selected). Also, clicking something that 194 # below it get selected). Also, clicking something that
131 # is selected deselects it 195 # is selected deselects it, if it's the only thing selected
132 new_now_selected = {}
133 for event in just_selected: 196 for event in just_selected:
134 if event.get_layer() == max_layer: 197 if event.get_layer() == max_layer:
135 if not event.is_selected(): 198 val = True
199 now_selected = self.renderer.get_schedule().get_selected()
200 if (len(now_selected) == 1 or self.ctrl_clicked) and event in now_selected:
201 val = not event.is_selected
202 if val:
136 new_now_selected[event] = None 203 new_now_selected[event] = None
137 event.set_selected(not event.is_selected()) 204 elif event in new_now_selected:
138 break 205 del new_now_selected[event]
206 break # only pick one event when just clicking
139 207
140 for event in self.now_selected: 208 self.renderer.get_schedule().set_selected(new_now_selected)
141 if event not in new_now_selected: 209 self.last_selected = dict(new_now_selected)
142 event.set_selected(False)
143
144 self.now_selected = new_now_selected
145 210
146 rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) 211 self._dirty()
147 self.window.invalidate_rect(rect, True) 212 elif button_event.button == 3:
148 213 self._release_band()
214 self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected())
215
216 def button_release(self, widget, button_event, data=None):
217 self.ctrl_clicked = False
218 self.last_selected = dict(self.renderer.get_schedule().get_selected())
219
220 if button_event.button == 1:
221 self._release_band()
222
223 def _release_band(self):
224 old_select_rect = self.select_rect
225 self.left_button_start_coor = None
226 self.select_rect = None
227 if old_select_rect is not None:
228 self._dirty()
229
230 def _dirty(self, x=None, y=None, width=None, height=None):
231 if x is None:
232 x = 0
233 if y is None:
234 y = 0
235 if width is None:
236 width = self.width
237 if height is None:
238 height = self.height
239 rect = gtk.gdk.Rectangle(x, y, width, height)
240 self.window.invalidate_rect(rect, True)
241
149class GraphWindow(gtk.ScrolledWindow): 242class GraphWindow(gtk.ScrolledWindow):
150 def __init__(self, renderer): 243 def __init__(self, renderer):
151 super(GraphWindow, self).__init__(None, None) 244 super(GraphWindow, self).__init__(None, None)
@@ -157,37 +250,115 @@ class GraphWindow(gtk.ScrolledWindow):
157 def get_graph_area(self): 250 def get_graph_area(self):
158 return self.garea 251 return self.garea
159 252
160class MainWindow(object): 253class MainWindow(gtk.Window):
161 def __init__(self, renderer): 254 WINDOW_WIDTH_REQ = 500
162 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 255 WINDOW_HEIGHT_REQ = 300
256
257 def __init__(self, request_renderer_change):
258 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL)
259
260 self.connect('delete_event', self.delete_event)
261 self.connect('destroy', self.die)
262
263 self.connect('request-renderer-change', request_renderer_change)
264
265 self.file_menu = gtk.Menu()
266 self.open_item = gtk.MenuItem('_Open', True)
267 self.open_item.connect('activate', self.open_item_activate)
268 self.file_menu.append(self.open_item)
269 self.open_item.show()
270
271 self.quit_item = gtk.MenuItem('_Quit', True)
272 self.quit_item.connect('activate', self.quit_item_activate)
273 self.quit_item.show()
274 self.file_menu.append(self.quit_item)
163 275
164 self.window.connect('delete_event', self.delete_event) 276 self.file_item = gtk.MenuItem('_File', True)
165 self.window.connect('destroy', self.destroy) 277 self.file_item.set_submenu(self.file_menu)
278 self.file_item.show()
279
280 self.menu_bar = gtk.MenuBar()
281 self.menu_bar.append(self.file_item)
282
283 self.menu_bar.show()
166 284
167 self.vbox = gtk.VBox(False, 0) 285 self.vbox = gtk.VBox(False, 0)
168 286
169 self.gwindow = GraphWindow(renderer) 287 self.notebook = gtk.Notebook()
170 self.gwindow.get_graph_area().connect('update-event-description', 288
171 self.update_event_description) 289 self.notebook.last_page = -1
172 self.gwindow.show() 290 self.notebook.connect('switch-page', self.switch_page)
291
292 self.notebook.show()
173 293
174 self.desc_label = gtk.Label('') 294 self.desc_label = gtk.Label('')
175 self.desc_label.set_justify(gtk.JUSTIFY_LEFT) 295 self.desc_label.set_justify(gtk.JUSTIFY_LEFT)
176 self.desc_label.show() 296 self.desc_label.show()
177 297
178 self.vbox.pack_start(self.gwindow, True, True, 0) 298 self.vbox.pack_start(self.menu_bar, False, False, 0)
299 self.vbox.pack_start(self.notebook, True, True, 0)
179 self.vbox.pack_start(self.desc_label, False, False, 0) 300 self.vbox.pack_start(self.desc_label, False, False, 0)
180 self.vbox.show() 301 self.vbox.show()
181 302
182 self.window.add(self.vbox) 303 self.add(self.vbox)
183 self.window.show() 304
184 305 self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ)
306
307 self.show()
308
309 def connect_widgets(self, garea):
310 garea.connect('update-event-description', self.update_event_description)
311 garea.connect('request-context-menu', self.request_context_menu)
312
313 def set_renderers(self, renderers):
314 for i in range(0, self.notebook.get_n_pages()):
315 self.notebook.remove_page(0)
316 for title in renderers:
317 gwindow = GraphWindow(renderers[title])
318 self.connect_widgets(gwindow.get_graph_area())
319 gwindow.show()
320 self.notebook.append_page(gwindow, gtk.Label(title))
321
322 def switch_page(self, widget, page, page_num):
323 if self.notebook.last_page >= 0:
324 hvalue = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value()
325 self.notebook.get_nth_page(page_num).get_hadjustment().set_value(hvalue)
326
327 self.notebook.last_page = page_num
328
185 def update_event_description(self, widget, event, msg): 329 def update_event_description(self, widget, event, msg):
186 self.desc_label.set_text(msg) 330 self.desc_label.set_text(msg)
187 331
332 def request_context_menu(self, widget, gdk_event, selected):
333 button = 0
334 if hasattr(gdk_event, 'button'):
335 button = gdk_event.button
336 time = gdk_event.time
337
338 menu = GraphContextMenu(selected)
339 menu.popup(None, None, None, button, time)
340
341 def open_item_activate(self, widget):
342 dialog = gtk.FileChooserDialog('Open File', self,
343 gtk.FILE_CHOOSER_ACTION_OPEN,
344 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
345 gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT))
346 dialog.set_select_multiple(True)
347
348 if dialog.run() == gtk.RESPONSE_ACCEPT:
349 filenames = dialog.get_filenames()
350 dialog.destroy()
351 self.emit('request-renderer-change', filenames, None)
352 else:
353 dialog.destroy()
354
355
356 def quit_item_activate(self, widget):
357 self.destroy()
358
188 def delete_event(self, widget, event, data=None): 359 def delete_event(self, widget, event, data=None):
189 return False 360 return False
190 361
191 def destroy(self, widget, data=None): 362 def die(self, widget, data=None):
192 gtk.main_quit() 363 gtk.main_quit()
193 364