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