8 from matplotlib
.backends
.backend_gtkagg
import FigureCanvasGTKAgg
as FigureCanvas
9 from matplotlib
.backends
.backend_gtkagg
import NavigationToolbar2GTKAgg
as NavigationToolbar
11 def report_error(parent
, msg
):
12 dlg
= gtk
.MessageDialog(parent
,
13 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
14 gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_OK
, msg
)
15 dlg
.set_title(parent
.get_title())
21 <menubar name="MenuBar">
23 <menuitem action="Add file"/>
24 <menuitem action="Update files"/>
25 <menuitem action="New Math Signal..."/>
26 <menuitem action="Run netlister and simulate..."/>
27 <menuitem action="Quit"/>
33 self
._scale
_to
_str
= {'lin': 'Linear', 'logx': 'LogX', 'logy': 'LogY',\
35 self
._layout
_to
_str
= {'horiz': 'Horizontal', 'vert':'Vertical',\
38 self
._windows
_to
_figures
= {}
39 self
._current
_graph
= None
40 self
._current
_figure
= None
41 self
._TARGET
_TYPE
_SIGNAL
= 10354
42 self
._from
_signal
_list
= [("oscopy-signals", gtk
.TARGET_SAME_APP
,\
43 self
._TARGET
_TYPE
_SIGNAL
)]
44 self
._to
_figure
= [("oscopy-signals", gtk
.TARGET_SAME_APP
,\
45 self
._TARGET
_TYPE
_SIGNAL
)]
46 self
._ctxt
= oscopy
.Context()
47 self
._store
= gtk
.TreeStore(gobject
.TYPE_STRING
, gobject
.TYPE_PYOBJECT
)
48 self
._create
_widgets
()
49 self
._add
_file
('demo/irf540.dat')
50 #self._add_file('demo/ac.dat')
51 #self._add_file('demo/res.dat')
53 def _add_file(self
, filename
):
55 self
._ctxt
.read(filename
)
56 except NotImplementedError:
57 report_error(self
._mainwindow
,
58 'Could not find a reader for %s' % filename
)
60 it
= self
._store
.append(None, (filename
, None))
61 for name
, sig
in self
._ctxt
.readers
[filename
].signals
.iteritems():
62 self
._store
.append(it
, (name
, sig
))
64 def _action_add_file(self
, action
):
65 dlg
= gtk
.FileChooserDialog('Add file', parent
=self
._mainwindow
,
66 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
,
67 gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
))
69 filename
= dlg
.get_filename()
71 if resp
== gtk
.RESPONSE_ACCEPT
:
72 self
._add
_file
(filename
)
74 def _action_update(self
, action
):
77 def _action_new_math(self
, action
):
78 dlg
= gtk
.Dialog('New math signal', parent
=self
._mainwindow
,
79 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
,
80 gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
))
84 label
= gtk
.Label('Expression:')
85 hbox
.pack_start(label
)
87 hbox
.pack_start(entry
)
88 dlg
.vbox
.pack_start(hbox
)
92 if resp
== gtk
.RESPONSE_ACCEPT
:
93 expr
= entry
.get_text()
97 report_error(self
._mainwindow
,
98 'Could not find a reader for %s' % expr
)
100 it
= self
._store
.append(None, (expr
, None))
101 for name
, sig
in self
._ctxt
.readers
[expr
].signals
.iteritems():
102 self
._store
.append(it
, (name
, sig
))
107 def _action_quit(self
, action
):
110 def _create_menubar(self
):
112 # (name, stock-id, label, accelerator, tooltip, callback)
114 ('File', None, '_File'),
115 ('Add file', gtk
.STOCK_ADD
, '_Add file', None, None,
116 self
._action
_add
_file
),
117 ('Update files', gtk
.STOCK_REFRESH
, '_Update', None, None,
118 self
._action
_update
),
119 ("New Math Signal...", gtk
.STOCK_NEW
, '_New Math Signal', None,
120 None, self
._action
_new
_math
),
121 ("Run netlister and simulate...", gtk
.STOCK_MEDIA_FORWARD
,\
122 "_Run netlister and simulate...", None, None,\
123 self
._action
_netlist
_and
_simulate
),
124 ('Quit', gtk
.STOCK_QUIT
, '_Quit', None, None,
127 actiongroup
= gtk
.ActionGroup('App')
128 actiongroup
.add_actions(actions
)
130 uimanager
= gtk
.UIManager()
131 uimanager
.add_ui_from_string(self
.__ui
)
132 uimanager
.insert_action_group(actiongroup
, 0)
133 return uimanager
.get_accel_group(), uimanager
.get_widget('/MenuBar')
135 def _create_treeview(self
):
136 col
= gtk
.TreeViewColumn('Signal', gtk
.CellRendererText(), text
=0)
138 tv
.append_column(col
)
139 tv
.set_model(self
._store
)
140 tv
.connect('row-activated', self
._row
_activated
)
141 tv
.connect('button-press-event', self
._treeview
_button
_press
)
142 tv
.connect('drag_data_get', self
._drag
_data
_get
_cb
)
143 tv
.drag_source_set(gtk
.gdk
.BUTTON1_MASK
,\
144 self
._from
_signal
_list
,\
148 def _create_widgets(self
):
149 accel_group
, self
._menubar
= self
._create
_menubar
()
150 self
._treeview
= self
._create
_treeview
()
152 sw
= gtk
.ScrolledWindow()
153 sw
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
154 sw
.add(self
._treeview
)
157 vbox
.pack_start(self
._menubar
, False)
160 w
= self
._mainwindow
= gtk
.Window(gtk
.WINDOW_TOPLEVEL
)
161 w
.set_title('Oscopy GUI')
163 w
.add_accel_group(accel_group
)
164 w
.connect('destroy', lambda *x
: self
._action
_quit
(None))
165 w
.set_default_size(400, 300)
168 def _create_units_window(self
, figure
, graph
):
169 if self
._current
_graph
is None:
171 dlg
= gtk
.Dialog('Enter graph units',\
172 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
,\
173 gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
))
174 # Label and entry for X axis
176 label_xunits
= gtk
.Label('X axis unit:')
177 hbox_x
.pack_start(label_xunits
)
178 entry_xunits
= gtk
.Entry()
179 entry_xunits
.set_text(self
._current
_graph
.unit
[0])
180 hbox_x
.pack_start(entry_xunits
)
181 dlg
.vbox
.pack_start(hbox_x
)
183 # Label and entry for Y axis
185 label_yunits
= gtk
.Label('Y axis unit:')
186 hbox_y
.pack_start(label_yunits
)
187 entry_yunits
= gtk
.Entry()
188 entry_yunits
.set_text(self
._current
_graph
.unit
[1])
189 hbox_y
.pack_start(entry_yunits
)
190 dlg
.vbox
.pack_start(hbox_y
)
194 if resp
== gtk
.RESPONSE_ACCEPT
:
195 self
._current
_graph
.set_unit((entry_xunits
.get_text(),\
196 entry_yunits
.get_text()))
197 if figure
.canvas
is not None:
201 def _units_menu_item_activated(self
, menuitem
, user_data
):
202 figure
, graph
= user_data
203 self
._create
_units
_window
(figure
, graph
)
205 def _create_range_window(self
, figure
, graph
):
206 if self
._current
_graph
is None:
208 dlg
= gtk
.Dialog('Enter graph range',\
209 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
,\
210 gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
))
211 [xmin
, xmax
], [ymin
, ymax
] = self
._current
_graph
.get_range()
212 # Label and entry for X axis
214 label_xmin
= gtk
.Label('Xmin:')
215 hbox_x
.pack_start(label_xmin
)
216 entry_xmin
= gtk
.Entry()
217 entry_xmin
.set_text(str(xmin
))
218 hbox_x
.pack_start(entry_xmin
)
219 label_xmax
= gtk
.Label('Xmax:')
220 hbox_x
.pack_start(label_xmax
)
221 entry_xmax
= gtk
.Entry()
222 entry_xmax
.set_text(str(xmax
))
223 hbox_x
.pack_start(entry_xmax
)
224 dlg
.vbox
.pack_start(hbox_x
)
226 # Label and entry for Y axis
228 label_ymin
= gtk
.Label('Ymin:')
229 hbox_y
.pack_start(label_ymin
)
230 entry_ymin
= gtk
.Entry()
231 entry_ymin
.set_text(str(ymin
))
232 hbox_y
.pack_start(entry_ymin
)
233 label_ymax
= gtk
.Label('Ymax:')
234 hbox_y
.pack_start(label_ymax
)
235 entry_ymax
= gtk
.Entry()
236 entry_ymax
.set_text(str(ymax
))
237 hbox_y
.pack_start(entry_ymax
)
238 dlg
.vbox
.pack_start(hbox_y
)
242 if resp
== gtk
.RESPONSE_ACCEPT
:
243 r
= [float(entry_xmin
.get_text()),\
244 float(entry_xmax
.get_text()),\
245 float(entry_ymin
.get_text()),\
246 float(entry_ymax
.get_text())]
247 self
._current
_graph
.set_range(r
)
248 if figure
.canvas
is not None:
252 def _range_menu_item_activated(self
, menuitem
, user_data
):
253 figure
, graph
= user_data
254 self
._create
_range
_window
(figure
, graph
)
256 def _signals_menu_item_activated(self
, menuitem
, user_data
):
257 fig
, parent_it
, it
= user_data
258 name
, sig
= self
._store
.get(it
, 0, 1)
259 # print 'Adding signal %s to %s' % (name, fig)
263 if self
._current
_graph
is not None:
264 self
._current
_graph
.insert({name
: sig
})
265 if fig
.canvas
is not None:
268 def _graph_menu_item_activated(self
, menuitem
, user_data
):
273 def _scale_menu_item_activated(self
, menuitem
, user_data
):
274 fig
, scale
= user_data
275 self
._current
_graph
.set_scale(scale
)
276 if fig
.canvas
is not None:
279 def _layout_menu_item_activated(self
, menuitem
, user_data
):
280 fig
, layout
= user_data
282 if fig
.canvas
is not None:
285 def _delete_graph_menu_item_activated(self
, menuitem
, user_data
):
286 figure
, graph
= user_data
287 if self
._current
_graph
is not None:
288 idx
= figure
.graphs
.index(self
._current
_graph
)
289 figure
.delete(idx
+ 1)
290 self
._current
_graph
= None
291 if figure
.canvas
is not None:
294 def _remove_signal_menu_item_activated(self
, menuitem
, user_data
):
295 figure
, graph
, signals
= user_data
298 self
._current
_graph
.remove(signals
)
299 if figure
.canvas
is not None:
302 def _create_scale_menu(self
, data
):
305 for scale
in self
._scale
_to
_str
.keys():
306 item
= gtk
.CheckMenuItem(self
._scale
_to
_str
[scale
])
307 item
.set_active(self
._current
_graph
.scale
== scale
)
308 item
.connect('activate', self
._scale
_menu
_item
_activated
,
313 def _create_layout_menu(self
, fig
):
315 for layout
in self
._layout
_to
_str
.keys():
316 item
= gtk
.CheckMenuItem(self
._layout
_to
_str
[layout
])
317 item
.set_active(fig
.layout
== layout
)
318 item
.connect('activate', self
._layout
_menu
_item
_activated
,
323 def _create_remove_signal_menu(self
, data
):
326 if self
._current
_graph
is None:
327 item_nograph
= gtk
.MenuItem('No graph selected')
328 menu
.append(item_nograph
)
330 for name
, signal
in self
._current
_graph
.signals
.iteritems():
331 item
= gtk
.MenuItem(name
)
332 item
.connect('activate', self
._remove
_signal
_menu
_item
_activated
,
333 (figure
, graph
, {name
: signal
}))
337 def _create_graph_menu(self
, figure
, graph
):
339 item_range
= gtk
.MenuItem('Range...')
340 item_range
.connect('activate', self
._range
_menu
_item
_activated
,\
342 menu
.append(item_range
)
343 item_units
= gtk
.MenuItem('Units...')
344 item_units
.connect('activate', self
._units
_menu
_item
_activated
,\
346 menu
.append(item_units
)
347 item_scale
= gtk
.MenuItem('Scale')
348 item_scale
.set_submenu(self
._create
_scale
_menu
((figure
, graph
)))
349 menu
.append(item_scale
)
350 item_add
= gtk
.MenuItem('Insert signal')
351 item_add
.set_submenu(self
._create
_filename
_menu
((figure
, graph
)))
352 menu
.append(item_add
)
353 item_remove
= gtk
.MenuItem('Remove signal')
354 item_remove
.set_submenu(self
._create
_remove
_signal
_menu
((figure
, graph
)))
355 menu
.append(item_remove
)
358 def _create_figure_menu(self
, fig
, graph
):
360 item_add
= gtk
.MenuItem('Add graph')
361 item_add
.connect('activate', self
._graph
_menu
_item
_activated
,
363 menu
.append(item_add
)
364 item_delete
= gtk
.MenuItem('Delete graph')
365 item_delete
.connect('activate', self
._delete
_graph
_menu
_item
_activated
,
367 menu
.append(item_delete
)
368 item_layout
= gtk
.MenuItem('Layout')
369 item_layout
.set_submenu(self
._create
_layout
_menu
(fig
))
370 menu
.append(item_layout
)
373 def _create_signals_menu(self
, fig
, parent_it
):
375 it
= self
._store
.iter_children(parent_it
)
376 while it
is not None:
377 name
= self
._store
.get_value(it
, 0)
378 item
= gtk
.MenuItem(name
)
379 item
.connect('activate', self
._signals
_menu
_item
_activated
,
380 (fig
, parent_it
, it
))
382 it
= self
._store
.iter_next(it
)
385 def _create_filename_menu(self
, data
):
387 it
= self
._store
.get_iter_root()
392 while it
is not None:
393 filename
= self
._store
.get_value(it
, 0)
394 item
= gtk
.MenuItem(filename
)
395 item
.set_submenu(self
._create
_signals
_menu
(figure
, it
))
397 it
= self
._store
.iter_next(it
)
400 def _create_figure_popup_menu(self
, figure
, graph
):
403 item_nograph
= gtk
.MenuItem('No graph selected')
404 menu
.append(item_nograph
)
406 item_figure
= gtk
.MenuItem('Figure')
407 item_figure
.set_submenu(self
._create
_figure
_menu
(figure
, graph
))
408 menu
.append(item_figure
)
409 item_graph
= gtk
.MenuItem('Graph')
410 item_graph
.set_submenu(self
._create
_graph
_menu
(figure
, graph
))
411 menu
.append(item_graph
)
414 def _create_treeview_popup_menu(self
, signals
):
417 item_none
= gtk
.MenuItem("No signal selected")
418 menu
.append(item_none
)
420 for name
, signal
in signals
.iteritems():
421 item_freeze
= gtk
.CheckMenuItem("Freeze %s" % name
)
422 item_freeze
.set_active(signal
.freeze
)
423 item_freeze
.connect('activate',\
424 self
._signal
_freeze
_menu
_item
_activated
,\
426 menu
.append(item_freeze
)
429 def _signal_freeze_menu_item_activated(self
, menuitem
, signal
):
430 signal
.freeze
= not signal
.freeze
431 # Modify also the signal in the treeview
432 # (italic font? gray font color? a freeze column?)
434 def _treeview_button_press(self
, widget
, event
):
435 if event
.button
== 3:
437 path
, tvc
, x
, y
= tv
.get_path_at_pos(int(event
.x
), int(event
.y
))
441 row
= self
._store
[path
]
442 signals
= {row
[0]: row
[1]}
443 menu
= self
._create
_treeview
_popup
_menu
(signals
)
445 menu
.popup(None, None, None, event
.button
, event
.time
)
447 def _button_press(self
, event
):
448 if event
.button
== 3:
449 menu
= self
._create
_figure
_popup
_menu
(event
.canvas
.figure
, event
.inaxes
)
451 menu
.popup(None, None, None, event
.button
, event
.guiEvent
.time
)
453 #TODO: _windows_to_figures consistency...
454 # think of a better way to map events to Figure objects
455 def _row_activated(self
, widget
, path
, col
):
459 row
= self
._store
[path
]
460 self
._ctxt
.create({row
[0]: row
[1]})
461 fig
= self
._ctxt
.figures
[len(self
._ctxt
.figures
) - 1]
465 self
._windows
_to
_figures
[w
] = fig
466 w
.set_title('Figure %d' % self
._figcount
)
469 canvas
= FigureCanvas(fig
)
470 canvas
.mpl_connect('button_press_event', self
._button
_press
)
471 canvas
.mpl_connect('axes_enter_event', self
._axes
_enter
)
472 canvas
.mpl_connect('axes_leave_event', self
._axes
_leave
)
473 canvas
.mpl_connect('figure_enter_event', self
._figure
_enter
)
474 canvas
.mpl_connect('figure_leave_event', self
._figure
_leave
)
475 w
.connect("drag_data_received", self
._drag
_data
_received
_cb
)
476 w
.drag_dest_set(gtk
.DEST_DEFAULT_MOTION |\
477 gtk
.DEST_DEFAULT_HIGHLIGHT |\
478 gtk
.DEST_DEFAULT_DROP
,
479 self
._to
_figure
, gtk
.gdk
.ACTION_COPY
)
480 vbox
.pack_start(canvas
)
481 toolbar
= NavigationToolbar(canvas
, w
)
482 vbox
.pack_start(toolbar
, False, False)
486 def _axes_enter(self
, event
):
487 self
._current
_graph
= event
.inaxes
489 def _axes_leave(self
, event
):
490 # Unused for better user interaction
491 # self._current_graph = None
494 def _figure_enter(self
, event
):
495 self
._current
_figure
= event
.canvas
.figure
497 def _figure_leave(self
, event
):
498 # self._current_figure = None
501 def update_from_usr1(self
):
504 def _action_netlist_and_simulate(self
, action
):
505 dialog
= gtk
.Dialog("Run netlister and simulate",\
506 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_REJECT
,\
507 gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
))
508 vbox_netl
= gtk
.VBox()
509 ckbutton_netl
= gtk
.CheckButton("Run netlister")
510 ckbutton_netl
.set_active(True)
511 vbox_netl
.pack_start(ckbutton_netl
)
512 entry_netl
= gtk
.Entry()
513 entry_netl
.set_text("gnetlist -s -o demo.cir -g spice-sdb demo.sch")
514 vbox_netl
.pack_start(entry_netl
)
515 dialog
.vbox
.pack_start(vbox_netl
)
516 vbox_netl
= gtk
.VBox()
518 vbox_sim
= gtk
.VBox()
519 ckbutton_sim
= gtk
.CheckButton("Run simulator")
520 ckbutton_sim
.set_active(True)
521 vbox_sim
.pack_start(ckbutton_sim
)
522 entry_sim
= gtk
.Entry()
523 entry_sim
.set_text("gnucap -b demo.cir")
524 vbox_sim
.pack_start(entry_sim
)
525 dialog
.vbox
.pack_start(vbox_sim
)
526 ckbutton_upd
= gtk
.CheckButton("Update readers")
527 ckbutton_upd
.set_active(True)
528 dialog
.vbox
.pack_start(ckbutton_upd
)
531 if resp
== gtk
.RESPONSE_ACCEPT
:
532 if ckbutton_netl
.get_active():
533 res
= commands
.getstatusoutput(entry_netl
.get_text())
535 report_error(self
._mainwindow
, res
[1])
537 if ckbutton_sim
.get_active():
538 res
= commands
.getstatusoutput(entry_sim
.get_text())
540 report_error(self
._mainwindow
, res
[1])
542 if ckbutton_upd
.get_active():
546 def _drag_data_get_cb(self
, widget
, drag_context
, selection
, target_type
,\
548 if target_type
== self
._TARGET
_TYPE
_SIGNAL
:
550 (path
, col
) = tv
.get_cursor()
551 row
= self
._store
[path
]
553 selection
.set(selection
.target
, 8, row
[0])
555 def _drag_data_received_cb(self
, widget
, drag_context
, x
, y
, selection
,\
557 if target_type
== self
._TARGET
_TYPE
_SIGNAL
:
558 if self
._current
_graph
is not None:
559 signals
= {selection
.data
: self
._ctxt
.signals
[selection
.data
]}
560 self
._current
_graph
.insert(signals
)
561 if self
._current
_figure
.canvas
is not None:
562 self
._current
_figure
.canvas
.draw()
564 def usr1_handler(signum
, frame
):
565 app
.update_from_usr1()
567 if __name__
== '__main__':
569 main_loop
= gobject
.MainLoop()
570 signal
.signal(signal
.SIGUSR1
, usr1_handler
)