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