(hooke:cli.py) changed previously forgotten alphainput in do_exit
[hooke.git] / hooke_cli.py
1 #!/usr/bin/env python
2
3 '''
4 hooke_cli.py
5
6 Command line module of Hooke.
7
8 Copyright (C) 2006 Massimo Sandal (University of Bologna, Italy).
9
10 This program is released under the GNU General Public License version 2.
11 '''
12
13
14 from libhooke import * #FIXME
15 import libhookecurve as lhc
16
17 import libinput as linp
18 import liboutlet as lout
19
20 from libhooke import WX_GOOD
21 from libhooke import HOOKE_VERSION
22
23 import wxversion
24 wxversion.select(WX_GOOD)
25 import wx
26
27 from wx.lib.newevent import NewEvent
28 from matplotlib.numerix import * #FIXME
29
30 import xml.dom.minidom
31 import sys, os, os.path, glob, shutil
32 import Queue
33 import cmd
34 import time
35
36 global __version__
37 global __codename__
38 global __releasedate__
39 __version__ = HOOKE_VERSION[0]
40 __codename__ = HOOKE_VERSION[1]
41 __releasedate__ = HOOKE_VERSION[2]
42
43 from matplotlib import __version__ as mpl_version
44 from wx import __version__ as wx_version
45 from wxmpl import __version__ as wxmpl_version
46 from scipy import __version__ as scipy_version
47 from numpy import __version__ as numpy_version
48 from sys import version as python_version
49 import platform
50
51
52 class HookeCli(cmd.Cmd):
53     
54     def __init__(self,frame,list_of_events,events_from_gui,config,drivers):
55         cmd.Cmd.__init__(self)
56                        
57         self.prompt = 'hooke: '
58         
59         
60         self.current_list=[] #the playlist we're using
61         
62         self.current=None    #the current curve under analysis. 
63         self.plots=None
64         '''
65         The actual hierarchy of the "current curve" is a bit complex:
66         
67         self.current = the lhc.HookeCurve container object of the current curve
68         self.current.curve = the current "real" curve object as defined in the filetype driver class
69         self.current.curve.default_plots() = the default plots of the filetype driver.
70         
71         The plot objects obtained by mean of self.current.curve.default_plots() 
72         then undergoes modifications by the plotmanip
73         modifier functions. The modified plot is saved in self.plots and used if needed by other functions.       
74         '''
75         
76         
77         self.pointer=0       #a pointer to navigate the current list
78                         
79         #Things that come from outside
80         self.frame=frame                        #the wx frame we refer to
81         self.list_of_events=list_of_events      #a list of wx events we use to interact with the GUI
82         self.events_from_gui=events_from_gui    #the Queue object we use to have messages from the GUI
83         self.config=config                      #the configuration dictionary
84         self.drivers=drivers                    #the file format drivers
85         
86         #get plot manipulation functions
87         plotmanip_functions=[]
88         for object_name in dir(self):
89                 if object_name[0:9]=='plotmanip':
90                     plotmanip_functions.append(getattr(self,object_name))
91         #put plotmanips in order
92         self.plotmanip=[None for item in self.config['plotmanips']]
93         for item in plotmanip_functions:
94             namefunction=item.__name__[10:]
95             if namefunction in self.config['plotmanips']:
96                 nameindex=self.config['plotmanips'].index(namefunction) #index of function in plotmanips config
97                 self.plotmanip[nameindex] = item
98             else:
99                 pass
100            
101             
102         self.playlist_saved=0
103         self.playlist_name=''
104         self.notes_saved=1
105         self.notes_filename=None
106
107         #create outlet
108         self.outlet=lout.Outlet()
109         
110         #Data that must be saved in the playlist, related to the whole playlist (not individual curves)
111         self.playlist_generics={} 
112         
113         #make sure we execute _plug_init() for every command line plugin we import
114         for plugin_name in self.config['plugins']:
115             try:
116                 plugin=__import__(plugin_name)
117                 try:
118                     eval('plugin.'+plugin_name+'Commands._plug_init(self)')
119                 except AttributeError:
120                     pass
121             except ImportError:
122                 pass
123
124         
125 #HELPER FUNCTIONS
126 #Everything sending an event should be here
127     def _measure_N_points(self, N, whatset=1):
128         '''
129         general helper function for N-points measures
130         '''
131         measure_points=self.list_of_events['measure_points']
132         wx.PostEvent(self.frame, measure_points(num_of_points=N, set=whatset))
133         while 1:
134             try:
135                 points=self.frame.events_from_gui.get()
136                 break
137             except Empty:
138                 pass
139         return points
140         
141     def _get_displayed_plot(self,dest=0):
142         '''
143         returns the currently displayed plot.
144         '''
145         wx.PostEvent(self.frame, self.list_of_events['get_displayed_plot'](dest=dest))
146         while 1:
147             try:
148                 displayed_plot=self.events_from_gui.get()
149             except Empty:
150                 pass
151             if displayed_plot:
152                 break
153         return displayed_plot
154     
155     def _send_plot(self,plots):
156         '''
157         sends a plot to the GUI
158         '''
159         wx.PostEvent(self.frame, self.list_of_events['plot_graph'](plots=plots))
160         return
161         
162     def _find_plotmanip(self, name):
163         '''
164         returns a plot manipulator function from its name
165         '''
166         return self.plotmanip[self.config['plotmanips'].index(name)]
167     
168 #HERE COMMANDS BEGIN
169     
170     def help_set(self):
171         print '''
172 SET
173 Sets a local configuration variable
174 -------------
175 Syntax: set [variable] [value]
176         '''
177     def do_set(self,args):
178         #FIXME: some variables in self.config should be hidden or intelligently configurated...
179         args=args.split()
180         if len(args)==0:
181             print 'You must specify a variable and a value'
182             print 'Available variables:'
183             print self.config.keys()
184             return
185         if args[0] not in self.config.keys():
186             print 'This is not an internal Hooke variable!'
187             return
188         if len(args)==1:
189             #FIXME:we should reload the config file and reset the config value
190             print self.config[args[0]]
191             return
192         key=args[0]
193         try: #try to have a numeric value
194             value=float(args[1])
195         except ValueError: #if it cannot be converted to float, it's None, or a string...
196             if value.lower()=='none':
197                 value=None
198             else:
199                 value=args[1]
200                 
201         self.config[key]=value
202         self.do_plot(0)
203         
204 #PLAYLIST MANAGEMENT AND NAVIGATION
205 #------------------------------------
206     
207     def help_loadlist(self):
208         print '''
209 LOADLIST
210 Loads a file playlist
211 -----------
212 Syntax: loadlist [playlist file]
213         '''
214     def do_loadlist(self, args):
215         #checking for args: if nothing is given as input, we warn and exit.
216         while len(args)==0:
217             args=linp.safeinput('File to load?')
218         
219         arglist=args.split()
220         play_to_load=arglist[0]
221         
222         #We assume a Hooke playlist has the extension .hkp
223         if play_to_load[-4:] != '.hkp':
224             play_to_load+='.hkp'
225         
226         try:            
227             playxml=PlaylistXML()
228             self.current_list, self.playlist_generics=playxml.load(play_to_load)
229             self.current_playxml=playxml
230         except IOError:
231             print 'File not found.'
232             return
233         
234         print 'Loaded %s curves' %len(self.current_list)
235         
236         if 'pointer' in self.playlist_generics.keys():
237             self.pointer=int(self.playlist_generics['pointer'])
238         else:
239             #if no pointer is found, set the current curve as the first curve of the loaded playlist
240             self.pointer=0
241         print 'Starting at curve ',self.pointer
242             
243         self.current=self.current_list[self.pointer]
244         
245         #resets saved/notes saved state
246         self.playlist_saved=0
247         self.playlist_name=''
248         self.notes_saved=0        
249     
250         self.do_plot(0)
251         
252         
253     def help_genlist(self):
254         print '''
255 GENLIST
256 Generates a file playlist.
257 Note it doesn't *save* it: see savelist for this.
258
259 If [input files] is a directory, it will use all files in the directory for playlist.
260 So:
261 genlist dir
262 genlist dir/
263 genlist dir/*.*
264
265 are all equivalent syntax.
266 ------------
267 Syntax: genlist [input files]
268         
269 '''
270     def do_genlist(self,args):
271         #args list is: input path, output name
272         if len(args)==0:
273             args=linp.safeinput('Input files?')
274                     
275         arglist=args.split()      
276         list_path=arglist[0]
277                   
278         #if it's a directory, is like /directory/*.*
279         #FIXME: probably a bit kludgy.
280         if os.path.isdir(list_path): 
281             if platform.system == 'Windows':
282                 SLASH="\\"
283             else:
284                 SLASH="/"
285             if list_path[-1] == SLASH:
286                 list_path=list_path+'*.*'
287             else:    
288                 list_path=list_path+SLASH+'*.*'
289         
290         #expanding correctly the input list with the glob module :)        
291         list_files=glob.glob(list_path)
292         list_files.sort()
293
294         self.current_list=[]
295         for item in list_files:
296             try:
297                 self.current_list.append(lhc.HookeCurve(os.path.abspath(item))) 
298             except:
299                 pass
300             
301         self.pointer=0    
302         if len(self.current_list)>0:
303             self.current=self.current_list[self.pointer]
304         else:
305             print 'Empty list!'
306             return
307         
308         #resets saved/notes saved state
309         self.playlist_saved=0
310         self.playlist_name=''
311         self.notes_saved=0  
312         
313         self.do_plot(0)
314        
315         
316     def do_savelist(self,args):
317         '''
318         SAVELIST
319         Saves the current file playlist on disk.
320         ------------
321         Syntax: savelist [filename]
322         '''
323         while len(args)==0:
324             args=linp.safeinput('Output file?',['savedlist.txt'])
325     
326         output_filename=args
327         
328         self.playlist_generics['pointer']=self.pointer
329         
330         #autocomplete filename if not specified
331         if output_filename[-4:] != '.hkp':
332             output_filename+='.hkp'
333         
334         playxml=PlaylistXML()
335         playxml.export(self.current_list, self.playlist_generics)
336         playxml.save(output_filename)                  
337         
338         #remembers we have saved playlist
339         self.playlist_saved=1
340         
341     def help_addtolist(self):
342         print '''
343 ADDTOLIST
344 Adds a file to the current playlist
345 --------------
346 Syntax: addtolist [filename]
347 '''
348     def do_addtolist(self,args):
349         #args list is: input path
350         if len(args)==0:
351             print 'You must give the input filename you want to add'
352             self.help_addtolist()
353             return
354           
355         filenames=glob.glob(args)
356         
357         for filename in filenames:
358             self.current_list.append(lhc.HookeCurve(os.path.abspath(filename)))
359         #we need to save playlist
360         self.playlist_saved=0
361     
362     def help_printlist(self):
363         print '''
364 PRINTLIST
365 Prints the list of curves in the current playlist
366 -------------
367 Syntax: printlist
368 '''
369     def do_printlist(self,args):
370         for item in self.current_list:
371             print item.path
372             
373     
374     def help_jump(self):
375         print '''
376 JUMP
377 Jumps to a given curve.
378 ------
379 Syntax: jump {$curve}
380
381 If the curve is not in the current playlist, it politely asks if we want to add it.
382         '''  
383     def do_jump(self,filename):
384         '''
385         jumps to the curve with the given filename.
386         if the filename is not in the playlist, it asks if we must add it or not.
387         '''
388         
389         if filename=='':
390             filename=linp.safeinput('Jump to?')
391             
392         filepath=os.path.abspath(filename)
393         print filepath
394                 
395         c=0
396         item_not_found=1
397         while item_not_found:
398             try:
399                 
400                 if self.current_list[c].path == filepath:
401                     self.pointer=c
402                     self.current=self.current_list[self.pointer]
403                     item_not_found=0
404                     self.do_plot(0)
405                 else:
406                     c+=1  
407             except IndexError:
408                 #We've found the end of the list.
409                 answer=linp.safeinput('Curve not found in playlist. Add it to list?',['y'])
410                 if answer.lower()[0]=='y':
411                     try:
412                         self.do_addtolist(filepath)
413                     except:
414                         print 'Curve file not found.'
415                         return
416                     self.current=self.current_list[-1]
417                     self.pointer=(len(current_list)-1)
418                     self.do_plot(0)
419                     
420                 item_not_found=0
421     
422     
423     def do_index(self,args):
424         '''
425         INDEX
426         Prints the index of the current curve in the list
427         -----
428         Syntax: index
429         '''
430         print self.pointer+1, 'of', len(self.current_list) 
431     
432     
433     def help_next(self):
434         print '''
435 NEXT
436 Go the next curve in the playlist.
437 If we are at the last curve, we come back to the first.
438 -----
439 Syntax: next, n
440         '''
441     def do_next(self,args):
442         self.current.curve.close_all()
443         if self.pointer == (len(self.current_list)-1):
444             self.pointer=0
445             print 'Playlist finished; back to first curve.'
446         else:
447             self.pointer+=1
448         
449         self.current=self.current_list[self.pointer]
450         self.do_plot(0)
451         
452     
453     def help_n(self):
454         self.help_next()
455     def do_n(self,args):
456         self.do_next(args)
457         
458     def help_previous(self,args):
459         print '''
460 PREVIOUS
461 Go to the previous curve in the playlist.
462 If we are at the first curve, we jump to the last.
463 -------
464 Syntax: previous, p
465     '''
466     def do_previous(self,args):
467         self.current.curve.close_all()
468         if self.pointer == 0:
469             self.pointer=(len(self.current_list)-1)
470             print 'Start of playlist; jump to last curve.' 
471         else:
472             self.pointer-=1
473             
474         self.current=self.current_list[self.pointer]
475         self.do_plot(args)
476         
477             
478     def help_p(self):
479         self.help_previous()
480     def do_p(self,args):
481         self.do_previous(args)
482
483         
484 #PLOT INTERACTION COMMANDS
485 #-------------------------------    
486     def help_plot(self):
487         print '''
488 PLOT
489 Plots the current force curve
490 -------
491 Syntax: plot
492         '''
493     def do_plot(self,args):
494         
495         self.current.identify(self.drivers)
496         self.plots=self.current.curve.default_plots()
497         try:
498             self.plots=self.current.curve.default_plots()
499         except Exception, e:
500             print 'Unexpected error occurred in do_plot().'
501             print e
502             return
503             
504         #apply the plotmanip functions eventually present
505         nplots=len(self.plots)
506         c=0
507         while c<nplots:
508             for function in self.plotmanip: #FIXME: something strange happens about self.plotmanip[0]
509                 self.plots[c]=function(self.plots[c], self.current)
510                 
511             self.plots[c].xaxes=self.config['xaxes'] #FIXME: in the future, xaxes and yaxes should be set per-plot
512             self.plots[c].yaxes=self.config['yaxes']
513                 
514             c+=1
515
516         self._send_plot(self.plots)
517         
518     def _delta(self, set=1):
519         '''
520         calculates the difference between two clicked points
521         '''
522         print 'Click two points'
523         points=self._measure_N_points(N=2, whatset=set)
524         dx=abs(points[0].graph_coords[0]-points[1].graph_coords[0])
525         dy=abs(points[0].graph_coords[1]-points[1].graph_coords[1])
526         unitx=self.plots[points[0].dest].units[0]
527         unity=self.plots[points[0].dest].units[1]
528         return dx,unitx,dy,unity
529         
530     def do_delta(self,args):
531         '''
532         DELTA
533         
534         Measures the delta X and delta Y between two points.
535         ----
536         Syntax: delta
537         '''
538         dx,unitx,dy,unity=self._delta()
539         print str(dx)+' '+unitx
540         print str(dy)+' '+unity
541     
542     def _point(self, set=1):
543         '''calculates the coordinates of a single clicked point'''
544
545         print 'Click one point'
546         point=self._measure_N_points(N=1, whatset=set)
547         
548         x=point[0].graph_coords[0]
549         y=point[0].graph_coords[1]
550         unitx=self.plots[point[0].dest].units[0]
551         unity=self.plots[point[0].dest].units[1]
552         return x,unitx,y,unity
553         
554     def do_point(self,args):
555         '''
556         POINT
557         
558         Returns the coordinates of a point on the graph.
559         ----
560         Syntax: point
561         '''
562         x,unitx,y,unity=self._point()
563         print str(x)+' '+unitx
564         print str(y)+' '+unity
565         to_dump='point '+self.current.path+' '+str(x)+' '+unitx+', '+str(y)+' '+unity
566         self.outlet.push(to_dump)    
567    
568         
569     def do_close(self,args=None):
570         '''
571         CLOSE
572         Closes one of the two plots. If no arguments are given, the bottom plot is closed.
573         ------
574         Syntax: close [top,bottom]
575         '''
576         if args=='top':
577             to_close=0
578         elif args=='bottom':
579             to_close=1
580         else:
581             to_close=1
582         
583         close_plot=self.list_of_events['close_plot']
584         wx.PostEvent(self.frame, close_plot(to_close=to_close))
585         
586     def do_show(self,args=None):
587         '''
588         SHOW
589         Shows both plots.
590         ''' 
591         show_plots=self.list_of_events['show_plots']
592         wx.PostEvent(self.frame, show_plots())
593        
594         
595     
596     #PLOT EXPORT AND MANIPULATION COMMANDS
597     def help_export(self):
598         print '''
599 EXPORT
600 Saves the current plot as an image file
601 ---------------
602 Syntax: export [filename] {plot to export}
603
604 The supported formats are PNG and EPS; the file extension of the filename is automatically recognized
605 and correctly exported. Resolution is (for now) fixed at 150 dpi.
606
607 If you have a multiple plot, the optional plot to export argument tells Hooke which plot you want to export. If 0, the top plot is exported. If 1, the bottom plot is exported (Exporting both plots is still to implement)
608         '''
609     def do_export(self,args):
610         #FIXME: the bottom plot doesn't have the title
611         
612         dest=0
613         if args=='':
614             name=linp.safeinput('Filename?',[self.current.path+'.png'])
615         else:
616             args=args.split()
617             name=args[0]
618             if len(args) > 1:
619                 dest=int(args[1]) 
620                 
621         export_image=self.list_of_events['export_image']
622         wx.PostEvent(self.frame, export_image(name=name, dest=dest))
623         
624         
625     def help_txt(self):
626         print '''
627 TXT
628 Saves the current curve as a text file
629 Columns are, in order:
630 X1 , Y1 , X2 , Y2 , X3 , Y3 ...
631
632 -------------
633 Syntax: txt [filename] {plot to export}
634         '''
635     def do_txt(self,args):
636         
637         def transposed2(lists, defval=0):
638             '''
639             transposes a list of lists, i.e. from [[a,b,c],[x,y,z]] to [[a,x],[b,y],[c,z]] without losing
640             elements
641             (by Zoran Isailovski on the Python Cookbook online)
642             '''
643             if not lists: return []
644             return map(lambda *row: [elem or defval for elem in row], *lists)
645         
646         whichplot=0
647         args=args.split()
648         if len(args)==0:
649             filename=linp.safeinput('Filename?',[self.current.path+'.txt'])
650         else:
651             filename=linp.checkalphainput(args[0],self.current.path+'.txt',[])
652             try:
653                 whichplot=int(args[1])
654             except:
655                 pass
656             
657         columns=[]     
658         for dataset in self.plots[whichplot].vectors:
659             for i in range(0,len(dataset)): 
660                 columns.append([])
661                 for value in dataset[i]:
662                     columns[-1].append(str(value))                   
663         
664         rows=transposed2(columns, 'nan')
665         rows=[' , '.join(item) for item in rows]
666         text='\n'.join(rows)
667         
668         txtfile=open(filename,'w+')
669         txtfile.write(text)
670         txtfile.close()
671         
672     
673     #LOGGING, REPORTING, NOTETAKING
674     
675
676     def do_note_old(self,args):
677         '''
678         NOTE_OLD
679         **deprecated**: Use note instead. Will be removed in 0.9
680         
681         Writes or displays a note about the current curve.
682         If [anything] is empty, it displays the note, otherwise it adds a note.
683         The note is then saved in the playlist if you issue a savelist command
684         ---------------
685         Syntax: note_old [anything]        
686
687         '''
688         if args=='':
689             print self.current_list[self.pointer].notes
690         else:
691             #bypass UnicodeDecodeError troubles
692             try:
693                 args=args.decode('ascii')
694             except:
695                 args=args.decode('ascii','ignore')
696                 if len(args)==0:
697                     args='?'
698                     
699             self.current_list[self.pointer].notes=args
700         self.notes_saved=0
701             
702             
703     def do_note(self,args):
704         '''
705         NOTE
706         
707         Writes or displays a note about the current curve.
708         If [anything] is empty, it displays the note, otherwise it adds a note.
709         The note is then saved in the playlist if you issue a savelist command.
710         ---------------
711         Syntax: note_old [anything]        
712
713         '''
714         if args=='':
715             print self.current_list[self.pointer].notes
716         else:
717             if self.notes_filename == None:
718                 self.notes_filename=raw_input('Filename? ')
719                 title_line='Notes taken at '+time.asctime()+'\n'
720                 f=open(self.notes_filename,'w')
721                 f.write(title_line)
722                 f.close()
723                 
724             #bypass UnicodeDecodeError troubles    
725             try:
726                args=args.decode('ascii')
727             except:
728                args=args.decode('ascii','ignore')
729                if len(args)==0:
730                    args='?'
731             self.current_list[self.pointer].notes=args
732             
733             f=open(self.notes_filename,'a+')
734             note_string=(self.current.path+'  |  '+self.current.notes+'\n')
735             f.write(note_string)
736             f.close()
737                            
738     def help_notelog(self):
739         print '''
740 NOTELOG
741 Writes a log of the notes taken during the session for the current
742 playlist
743 --------------        
744 Syntax notelog [filename]
745 '''        
746     def do_notelog(self,args):
747         
748         if len(args)==0:
749             args=linp.safeinput('Notelog filename?',['notelog.txt'])
750             
751         note_lines='Notes taken at '+time.asctime()+'\n'
752         for item in self.current_list:
753             if len(item.notes)>0:
754                 #FIXME: log should be justified
755                 #FIXME: file path should be truncated...
756                 note_string=(item.path+'  |  '+item.notes+'\n')
757                 note_lines+=note_string
758                 
759         try:
760             f=open(args,'a+')
761             f.write(note_lines)
762             f.close()
763         except IOError, (ErrorNumber, ErrorMessage):
764             print 'Error: notes cannot be saved. Catched exception:'
765             print ErrorMessage
766         
767         self.notes_saved=1
768
769     def help_copylog(self):
770         print '''
771 COPYLOG
772 Moves the annotated curves to another directory
773 -----------
774 Syntax copylog [directory]
775         '''
776     def do_copylog(self,args):
777         
778         if len(args)==0:
779             args=linp.safeinput('Destination directory?')  #TODO default
780         
781         mydir=os.path.abspath(args)
782         if not os.path.isdir(mydir):
783             print 'Destination is not a directory.'
784             return
785         
786         for item in self.current_list:
787             if len(item.notes)>0:
788                 try:
789                     shutil.copy(item.path, mydir)
790                 except OSError:
791                     print 'OSError. Cannot copy file. Perhaps you gave me a wrong directory?'
792
793 #OUTLET management
794
795
796     def do_outlet_show(self,args):
797         '''OUTLET_SHOW
798         ---------
799         Shows current content of outlet with index for reference
800         '''
801         self.outlet.printbuf()
802
803     def do_outlet_undo(self, args):
804         '''OUTLET_UNDO
805         ---------
806         Eliminates last entry in outlet
807         '''
808         print 'Erasing last entry'
809         self.outlet.pop()
810
811     def do_outlet_delete(self, args):
812         '''OUTLET_DELETE
813         Eliminates a particular entry from outlet
814         Syntax: outlet_delete n
815         '''
816         if len(args)==0:
817             print 'Index needed!, use outlet_show to know it'
818         else:
819             self.outlet.delete(args)
820
821
822
823
824
825 #OS INTERACTION COMMANDS
826 #-----------------    
827     def help_dir(self):
828         print '''
829 DIR, LS
830 Lists the files in the directory
831 ---------
832 Syntax: dir [path]
833           ls  [path]
834         '''
835     def do_dir(self,args):
836         
837         if len(args)==0:
838             args='*'
839         print glob.glob(args)
840         
841     def help_ls(self):
842         self.help_dir(self)
843     def do_ls(self,args):
844         self.do_dir(args)
845         
846     def help_pwd(self):
847         print '''
848 PWD
849 Gives the current working directory.
850 ------------
851 Syntax: pwd
852         '''
853     def do_pwd(self,args):
854         print os.getcwd()         
855     
856     def help_cd(self):
857         print '''
858 CD
859 Changes the current working directory
860 -----
861 Syntax: cd
862         '''
863     def do_cd(self,args):
864         mypath=os.path.abspath(args)
865         try:
866             os.chdir(mypath)
867         except OSError:
868             print 'I cannot access that directory.'
869     
870     
871     def help_system(self):
872         print '''
873 SYSTEM
874 Executes a system command line and reports the output
875 -----
876 Syntax system [command line]
877         '''
878         pass
879     def do_system(self,args):
880         waste=os.system(args)           
881             
882     def do_debug(self,args):
883         '''
884         this is a dummy command where I put debugging things
885         '''
886         print self.config['plotmanips']
887         pass
888             
889     def help_current(self):
890         print '''
891 CURRENT
892 Prints the current curve path.
893 ------
894 Syntax: current
895         '''
896     def do_current(self,args):
897         print self.current.path
898         
899     def do_version(self,args):
900         '''
901         VERSION
902         ------
903         Prints the current version and codename, plus library version. Useful for debugging.
904         '''     
905         print 'Hooke '+__version__+' ('+__codename__+')'
906         print 'Released on: '+__releasedate__
907         print '---'
908         print 'Python version: '+python_version
909         print 'WxPython version: '+wx_version
910         print 'wxMPL version: '+wxmpl_version
911         print 'Matplotlib version: '+mpl_version
912         print 'SciPy version: '+scipy_version
913         print 'NumPy version: '+numpy_version
914         print '---'
915         print 'Platform: '+str(platform.uname())
916         print '---'
917         print 'Loaded plugins:',self.config['loaded_plugins']
918         
919     def help_exit(self):
920         print '''
921 EXIT, QUIT
922 Exits the program cleanly.
923 ------
924 Syntax: exit
925 Syntax: quit
926 '''    
927     def do_exit(self,args):
928         we_exit='N'
929         
930         if (not self.playlist_saved) or (not self.notes_saved):
931             we_exit=linp.safeinput('You did not save your playlist and/or notes. Exit?',['n'])
932         else:
933             we_exit=linp.safeinput('Exit?',['y'])
934         
935         if we_exit[0].upper()=='Y':
936             wx.CallAfter(self.frame.Close)
937             sys.exit(0)
938         else:
939             return
940     
941     def help_quit(self):
942         self.help_exit()
943     def do_quit(self,args):
944         self.do_exit(args)
945
946
947
948
949
950 if __name__ == '__main__':
951     mycli=HookeCli(0)
952     mycli.cmdloop()