3 Sample command line application
12 from optparse
import OptionParser
14 from oscopy
.readers
.reader
import ReadError
16 from matplotlib
.backends
.backend_gtkagg
import FigureCanvasGTKAgg
as FigureCanvas
17 from matplotlib
.backends
.backend_gtkagg
import NavigationToolbar2GTKAgg
as NavigationToolbar
19 class OscopyApp(object):
20 """ Analyse command arguments and call function from Cmd
24 self
.cmds
= oscopy
.Context()
28 self
.hist_file
= ".oscopy_hist"
30 # Readline configuration
31 if not os
.path
.exists(self
.hist_file
):
32 f
= open(self
.hist_file
, "w")
35 readline
.read_history_file(self
.hist_file
)
37 # Parse command line arguments
39 # -b : batch mode, read commands from file
40 # -i : interactive mode, do not quit at the end of batch file
41 # -q : do not display startup message
42 parser
= OptionParser()
43 parser
.add_option("-b", "--batch", dest
="fn",\
44 help="Execute commands in FILE then exit",\
46 parser
.add_option("-i", "--interactive", action
="store_true",\
48 help="Go to interactive mode after executing batch file")
49 parser
.add_option("-q", "--quiet", action
="store_true",\
51 help="Do not display startup message")
52 (options
, args
) = parser
.parse_args()
53 if options
.fn
is None:
57 f
= open(options
.fn
, 'r')
59 print "Unable to access batch file:", e
61 if options
.inter
== True:
67 if options
.quiet
is None:
68 print "This is oscopy, a program to view electrical simulation results\n\
69 Copyright (C) 2009 Arnaud Gardelein.\n\
70 This is free software, you are invited to redistribute it \n\
71 under certain conditions.\n\
72 There is ABSOLUTELY NO WARRANTY; not even for MERCHANTIBILITY or\n\
73 FITNESS FOR A PARTICULAR PURPOSE."
75 # Current graph and current figure
76 self
._current
_figure
= None
77 self
._current
_graph
= None
81 self
.loop(p
, f
, batch
)
83 def create(self
, args
):
85 print "Usage : create [SIG [, SIG [, SIG]...]]"
86 print " Create a new figure, set_ it as current, add the signals"
88 self
.cmds
.create(self
.get_signames(args
))
89 self
._current
_figure
= self
.cmds
.figures
[len(self
.cmds
.figures
) - 1]
90 if self
._current
_figure
.graphs
:
91 self
._current
_graph
=\
92 self
._current
_figure
.graphs
[len(\
93 self
._current
_figure
.graphs
) - 1]
95 self
._current
_graph
= None
97 def destroy(self
, args
):
99 print "Usage : destroy FIG#"
100 print " Destroy a figure"
102 self
.cmds
.destroy(eval(args
))
103 # Go back to the first graph of the first figure or None
104 if len(self
.cmds
.figures
):
105 self
._current
_figure
= self
.cmds
.figures
[0]
106 if self
._current
_figure
.graphs
:
107 self
._current
_graph
= self
._current
_figure
.graphs
[0]
109 self
._current
_graph
= None
111 self
._current
_figure
= None
112 self
._current
_graph
= None
114 def select(self
, args
):
116 print "Usage: select FIG#-GRAPH#"
117 print " Select the current figure and the current graph"
124 print "Usage: select FIG#-GRAPH#"
126 # self.cmds.current = num, gn
127 self
._current
_figure
= self
.cmds
.figures
[num
- 1]
128 self
._current
_graph
= self
._current
_figure
.graphs
[gn
- 1]
130 def layout(self
, args
):
132 print "Usage : layout horiz|vert|quad"
133 print " Define the layout of the current figure"
135 if self
._current
_figure
is not None:
136 self
._current
_figure
.layout
= args
138 def figlist(self
, args
):
140 print "Usage : figlist"
141 print " Print the list of figures"
145 for fn
, f
in enumerate(self
.cmds
.figures
):
146 print "%s Figure %d: %s" %\
147 ([" ", "*"][f
== self
._current
_figure
],\
149 for gn
, g
in enumerate(f
.graphs
):
150 print " %s Graph %d : (%s) %s" %\
151 ([" ","*"][g
== self
._current
_graph
],\
153 SEPARATOR
.join(g
.signals
.keys()))
155 def plot(self
, args
):
158 print " Draw and show the figures"
160 if self
._figcount
== len(self
.cmds
.figures
):
161 self
._main
_loop
.run()
163 # Create a window for each figure, add navigation toolbar
165 for i
, f
in enumerate(self
.cmds
.figures
):
166 # fig = plt.figure(i + 1)
169 w
.set_title('Figure %d' % self
._figcount
)
172 canvas
= FigureCanvas(f
)
173 canvas
.connect('destroy', self
._window
_destroy
)
174 vbox
.pack_start(canvas
)
175 toolbar
= NavigationToolbar(canvas
, w
)
176 vbox
.pack_start(toolbar
, False, False)
179 self
._main
_loop
= gobject
.MainLoop()
180 self
._main
_loop
.run()
182 def _window_destroy(self
, arg
):
183 self
._figcount
= self
._figcount
- 1
184 if not self
._figcount
:
185 self
._main
_loop
.quit()
188 def read(self
, args
):
190 print "Usage : load DATAFILE"
191 print " Load signal file"
197 print "Failed to read %s:" % fn
, e
199 except NotImplementedError:
200 print "File format not supported"
203 def write(self
, args
):
205 print "Usage: write format [(OPTIONS)] FILE SIG [, SIG [, SIG]...]"
206 print " Write signals to file"
208 # Extract format, options and signal list
209 tmp
= re
.search(r
'(?P<fmt>\w+)\s*(?P<opts>\([^\)]*\))?\s+(?P<fn>[\w\./]+)\s+(?P<sigs>\w+(\s*,\s*\w+)*)', args
)
212 print "What format ? Where ? Which signals ?"
214 fmt
= tmp
.group('fmt')
216 opt
= tmp
.group('opts')
217 sns
= self
.get_signames(tmp
.group('sigs'))
220 for on
in opt
.strip('()').split(','):
221 tmp
= on
.split(':', 1)
223 opts
[tmp
[0]] = tmp
[1]
225 self
.cmds
.write(fn
, fmt
, sns
, opts
)
226 except WriteError
, e
:
227 print "Write error:", e
229 except NotImplementedError:
230 print "File format not supported"
233 def update(self
, args
):
235 print "Usage: update"
236 print " Reread data files"
241 if self
.cmds
.readers
.has_key(args
):
242 self
.cmds
.update(self
.cmds
.readers
[args
])
244 print "%s not found in readers" % args
248 print "Usage : add SIG [, SIG [, SIG]...]"
249 print " Add a graph to the current figure"
251 if self
._current
_figure
is not None:
252 self
._current
_figure
.add(self
.cmds
.names_to_signals(\
253 self
.get_signames(args
)))
254 self
._current
_graph
=\
255 self
._current
_figure
.graphs
[len(\
256 self
._current
_figure
.graphs
) - 1]
258 self
.cmds
.create(self
.get_signames(args
))
259 if self
.cmds
.figures
:
260 self
._current
_figure
= self
.cmds
.figures
[0]
262 print "Create failed"
264 def delete(self
, args
):
266 print "Usage : delete GRAPH#"
267 print " Delete a graph from the current figure"
269 if self
._current
_figure
is not None:
270 self
._current
_figure
.delete(args
)
271 if self
._current
_figure
.graphs
:
272 self
._current
_graph
= self
._current
_figure
.graphs
[0]
274 self
._current
_graph
= None
276 def mode(self
, args
):
278 print "Usage: mode MODE"
279 print " Set the type of the current graph of the current figure"
280 print "Available modes :\n\
282 # fft Fast Fourier Transform (FFT) of signals\n\
283 # ifft Inverse FFT of signals"
285 if self
._current
_graph
is None:
287 idx
= self
._current
_figure
.graphs
.index(self
._current
_graph
)
288 self
._current
_figure
.mode
= self
._current
_graph
, mode
289 self
._current
_graph
= self
._current
_figure
.graphs
[idx
]
291 def scale(self
, args
):
293 print "Usage: scale [lin|logx|logy|loglog]"
294 print " Set the axis scale"
296 if self
._current
_graph
is None:
298 self
._current
_graph
.scale
= args
300 def range(self
, args
):
302 print "Usage: range [x|y min max]|[xmin xmax ymin ymax]|[reset]"
303 print " Set the axis range of the current graph of the current figure"
305 if self
._current
_graph
is None:
310 if range[0] == "reset":
311 self
._current
_graph
.range = range[0]
312 elif len(range) == 3:
313 if range[0] == 'x' or range[0] == 'y':
314 self
._current
_graph
.range = range[0],\
315 [float(range[1]), float(range[2])]
316 elif len(range) == 4:
317 self
._current
_graph
.range = [float(range[0]), float(range[1]),\
318 float(range[2]), float(range[3])]
320 def unit(self
, args
):
322 print "Usage: unit [XUNIT,] YUNIT"
323 print " Set the unit to be displayed on graph axis"
326 units
= args
.split(",", 1)
327 if len(units
) < 1 or self
._current
_graph
is None:
329 elif len(units
) == 1:
330 self
._current
_graph
.unit
= units
[0].strip(),
331 elif len(units
) == 2:
332 self
._current
_graph
.unit
= units
[0].strip(), units
[1].strip()
336 def insert(self
, args
):
338 print "Usage: insert SIG [, SIG [, SIG]...]"
339 print " Insert a list of signals into the current graph"
341 if self
._current
_graph
is None:
343 self
._current
_graph
.insert(self
.cmds
.names_to_signals(\
344 self
.get_signames(args
)))
346 def remove(self
, args
):
348 print "Usage: remove SIG [, SIG [, SIG]...]"
349 print " Delete a list of signals into from current graph"
351 if self
._current
_graph
is None:
353 self
._current
_graph
.remove(self
.cmds
.names_to_signals(\
354 self
.get_signames(args
)))
356 def freeze(self
, args
):
358 print "Usage: freeze SIG [, SIG [, SIG]...]"
359 print " Do not consider signal for subsequent updates"
360 self
.cmds
.freeze(self
.get_signames(args
))
362 def unfreeze(self
, args
):
364 print "Usage: unfreeze SIG [, SIG [, SIG]...]"
365 print " Consider signal for subsequent updates"
366 self
.cmds
.unfreeze(self
.get_signames(args
))
368 def siglist(self
, args
):
370 print "Usage : siglist"
371 print " List loaded signals"
374 HEADER
=["Name", "Unit", "Ref", "Reader","Last updated (sec)"]
375 print SEPARATOR
.join(HEADER
)
377 for reader_name
, reader
in self
.cmds
.readers
.iteritems():
378 for signal_name
, signal
in reader
.signals
.iteritems():
379 print SEPARATOR
.join((signal_name
, \
383 str(int(t
- reader
.info
['last_update']))))
387 print "Usage: destsig=mathexpr"
388 print " Define a new signal destsig using mathematical expression"
393 print "Error creating signal from math expression", e
396 def get_signames(self
, args
):
397 """ Return the signal names list extracted from the commandline
398 The list must be a coma separated list of signal names.
399 If no signals are loaded of no signal are found, return []
405 for sn
in args
.split(","):
406 sns
.append(sn
.strip())
409 def help(self
, args
):
410 """ Display help messages
414 Commands related to figures:\n\
415 create create a new figure\n\
416 destroy delete a figure\n\
417 select define the current figure and the current graph\n\
418 layout set_ the layout (either horiz, vert or quad)\n\
419 figlist list the existing figures\n\
420 plot draw and show the figures\n\
421 Commands related to graphs:\n\
422 add add a graph to the current figure\n\
423 delete delete a graph from the current figure\n\
424 mode set_ the mode of the current graph of the current figure\n\
425 unit set_ the units of the current graph of the current figure\n\
426 scale set_ the scale of the current graph of the current figure\n\
427 range set_ the axis range of the current graph of the current figure\n\
428 Commands related to signals:\n\
429 read read signals from file\n\
430 write write signals to file\n\
431 update reread signals from file(s)\n\
432 insert add a signal to the current graph of the current figure\n\
433 remove delete a signal from the current graph of the current figure\n\
434 (un)freeze toggle signal update\n\
435 siglist list the signals\n\
437 echo print a message\n\
438 pause wait for the user to press enter\n\
439 quit, exit exit the program\n\
440 help display this help message\n\
443 1, 2 Toggle first and second vertical cursors\n\
444 3, 4 Toggle first and second horizontal cursors\n\
446 SIG = EXPR Compute a signal from mathematical expression\n\
448 Compute Fast Fourier Transform (FFT) or inverse FFT from SIG\n\
450 Help for individual command can be obtained with 'help COMMAND'\
453 if args
in dir(cmds
):
454 eval("self." + args
+ "(\"help\")")
456 print "Unknown command", args
458 def echo(self
, args
):
460 print "Usage: echo [TEXT]"
465 def pause(self
, args
):
468 print " Wait for the user to press enter"
470 inp
= raw_input("Press enter")
472 def loop(self
, p
, f
= None, batch
= False):
480 inp
= f
.readline().rstrip("\n")
483 # Batch mode, exit at the end of script
486 # Interactive mode, continue with command line
490 print "Script error:", e
493 # Check if line is a comment
494 if inp
.lstrip().startswith("#"):
496 # Check if command is assignment
497 if inp
.find("=") >= 0:
501 # Separate command from args
502 if inp
.find(" ") >= 0:
503 st
= inp
.lstrip().split(' ', 1)
506 # print "cmd:", cmd, "args:", args
512 if cmd
== "exit" or cmd
== "quit":
515 # Evaluate the command
517 eval("self." + cmd
+ "(args)")
519 print cmd
, "not supported"
525 # except AttributeError, e:
526 # print "Unknown command:", e.message
529 # except NameError, e:
530 # print "Unknown command", e.message
533 except SyntaxError, e
:
534 print "Syntax Error", e
.message
538 print "Error in read :", e
540 readline
.write_history_file(self
.hist_file
)
542 if __name__
== "__main__":