(pcluster.py) fixed indentation (ok)
[hooke.git] / tutorial.py
1 #!/usr/bin/env python
2
3 '''
4 TUTORIAL PLUGIN FOR HOOKE
5
6 This plugin contains example commands to teach how to write an Hooke plugin, including description of main Hooke
7 internals.
8 (c)Massimo Sandal 2007
9 '''
10
11 import libhookecurve as lhc
12
13 import numpy as np
14
15 '''
16 SYNTAX OF DATA TYPE DECLARATION:
17     type = type of object
18     [ type ] = list containing objects of type
19     {typekey:typearg} = dictionary with keys of type typekey and args of type typearg
20     ( type ) = tuple containing objects of type
21 '''
22
23
24 class tutorialCommands:
25     '''
26     Here we define the class containing all the Hooke commands we want to define
27     in the plugin.
28     
29     Notice the class name!!
30     The syntax is filenameCommands. That is, if your plugin is pluggy.py, your class
31     name is pluggyCommands.
32     
33     Otherwise, the class will be ignored by Hooke.
34     ''' 
35     
36     def _plug_init(self):
37         '''
38         This is the plugin initialization.
39         When Hooke starts and the plugin is loaded, this function is executed.
40         If there is something you need to do when Hooke starts, code it in this function.
41         '''
42         print 'I am the Tutorial plugin initialization!'
43         
44         #Here we initialize a local configuration variable; see plotmanip_absvalue() for explanation.
45         self.config['tutorial_absvalue']=0 
46         pass
47     
48     def do_nothing(self,args):
49         '''
50         This is a boring but working example of an actual Hooke command.
51         A Hooke command is a function of the xxxxCommands class, which is ALWAYS defined
52         this way:
53         
54         def do_nameofcommand(self,args)
55         
56         *do_            is needed to make Hooke understand this function is a command
57         *nameofcommand  is how the command will be called in the Hooke command line.
58         *self           is, well, self
59         *args           is ALWAYS needed (otherwise Hooke will crash executing the command). We will see
60                         later what args is.
61         
62         Note that if you now start Hooke with this plugin activated and you type in the Hooke command
63         line "help nothing" you will see this very text as output. So the help of a command is a
64         string comment below the function definition, like this one.
65         
66         Commands usually return None.
67         '''
68         print 'I am a Hooke command. I do nothing.'
69         
70     def do_printargs(self,args):
71         '''
72         This command prints the args you give to it.
73         args is always a string, that contains everything you write after the command.
74         So if you issue "mycommand blah blah 12345" args is "blah blah 12345".
75         
76         Again, args is needed in the definition even if your command does not use it.
77         '''
78         print 'You gave me those args: '+args
79         
80     def help_tutorial(self):
81         '''
82         This is a help function. 
83         If you want a help function for something that is not a command, you can write a help
84         function like this. Calling "help tutorial" will execute this function.
85         '''
86         print 'You called help_tutorial()'
87         
88     def do_environment(self,args):
89         '''
90         This plugin contains a panoramic of the Hooke command line environment variables,
91         and prints their current value.
92         '''
93         
94         '''self.current_list
95         TYPE: [ libhookecurve.HookeCurve ], len=variable
96         contains the actual playlist of Hooke curve objects.
97         Each HookeCurve object represents a reference to a data file.
98         We will see later in detail how do they work.
99         '''
100         print 'current_list length:',len(self.current_list)
101         print 'current_list 0th:',self.current_list[0]
102         
103         '''self.pointer
104         TYPE: int
105         contains the index of
106         the current curve in the playlist
107         '''
108         print 'pointer: ',self.pointer
109         
110         '''self.current
111         TYPE: libhookecurve.HookeCurve
112         contains the current curve displayed.
113         We will see later how it works.
114         '''
115         print 'current:',self.current
116         
117         '''self.plots
118         TYPE: [ libhookecurve.PlotObject ], len=1,2
119         contains the current default plots.
120         Each PlotObject contains all info needed to display 
121         the plot: apart from the data vectors, the title, destination
122         etc.
123         Usually self.plots[0] is the default topmost plot, self.plots[1] is the
124         accessory bottom plot.
125         '''
126         print 'plots:',self.plots
127         
128         '''self.config
129         TYPE: { string:anything }
130         contains the current Hooke configuration variables, in form of a dictionary.
131         '''
132         print 'config:',self.config
133         
134         '''self.plotmanip
135         TYPE: [ function ]
136         Contains the ordered plot manipulation functions.
137         These functions are called to modify the default plot by default before it is plotted.
138         self.plots contains the plot passed through the plot manipulators.
139         We will see it better later.
140         *YOU SHOULD NEVER MODIFY THAT*
141         '''
142         print 'plotmanip: ',self.plotmanip
143         
144         '''self.drivers
145         TYPE: [ class ]
146         Contains the plot reading drivers.
147         *YOU SHOULD NEVER MODIFY THAT*
148         '''
149         print 'drivers: ',self.drivers
150         
151         '''self.frame
152         TYPE: wx.Frame
153         Contains the wx Frame of the GUI.
154         ***NEVER, EVER TOUCH THAT.***
155         '''
156         print 'frame: ',self.frame
157         
158         '''self.list_of_events
159         TYPE: { string:wx.Event }
160         Contains the wx.Events to communicate with the GUI.
161         Usually not necessary to use it, unless you want
162         to create a GUI plugin.
163         '''
164         print 'list of events:',self.list_of_events
165         
166         '''self.events_from_gui
167         TYPE: Queue.Queue
168         Contains the Queue where data from the GUI is put.
169         Usually not necessary to use it, unless you want
170         to create a GUI plugin.
171         '''
172         print 'events from gui:',self.events_from_gui
173         
174         '''self.playlist_saved
175         TYPE: Int (0/1) ; Boolean
176         Flag that tells if the playlist has been saved or not.
177         '''
178         print 'playlist saved:',self.playlist_saved
179         
180         '''self.playlist_name
181         TYPE: string
182         Name of current playlist
183         '''
184         print 'playlist name:',self.playlist_name
185         
186         '''self.notes_saved
187         TYPE: Int (0/1) ; Boolean
188         Flag that tells if the playlist has been saved or not.
189         '''
190         print 'notes saved:',self.notes_saved
191         
192
193     def do_myfirstplot(self,args):
194         '''
195         In this function, we see how to create a PlotObject and send it to the screen.
196         ***Read the code of PlotObject in libhookecurve.py before!***.
197         '''
198         
199         #We generate some interesting data to plot for this example.
200         xdata1=np.arange(-5,5,0.1)
201         xdata2=np.arange(-5,5,0.1)
202         ydata1=[item**2 for item in xdata1]
203         ydata2=[item**3 for item in xdata2]
204         
205         #Create the object.
206         #The PlotObject class lives in the libhookecurve library.
207         myplot=lhc.PlotObject()
208         myplot.vectors=[[],[]] #Decide we will have two data sets in this plot
209         '''
210         The *data* of the plot live in the .vectors list. 
211         
212         plot.vectors is a multidimensional array:
213         plot.vectors[0]=set1
214         plot.vectors[1]=set2
215         plot.vectors[2]=sett3
216         etc.
217         
218         2 curves in a x,y plot are:
219         [[[x1],[y1]],[[x2],[y2]]]
220         for example:
221             x1          y1              x2         y2
222         [[[1,2,3,4],[10,20,30,40]],[[3,6,9,12],[30,60,90,120]]]
223         x1 = self.vectors[0][0]
224         y1 = self.vectors[0][1]
225         x2 = self.vectors[1][0]
226         y2 = self.vectors[1][1]
227         '''
228         #Pour 0-th dataset into myplot: 
229         myplot.vectors[0].append(xdata1) #...x
230         myplot.vectors[0].append(ydata1) #...then y
231         
232         #Pour 1-st dataset into myplot: 
233         myplot.vectors[1].append(xdata2) #...x
234         myplot.vectors[1].append(ydata2) #...then y
235         
236         #Add units to x and y axes
237         #units=[string, string]
238         myplot.units=['x axis','y axis']
239         
240         #Where do we want the plot? 0=top, 1=bottom
241         myplot.destination=1
242         
243         '''Send it to the GUI.
244         Note that you *have* to encapsulate it into a list, so you
245         have to send [myplot], not simply myplot.
246         
247         You can also send more two plots at once
248         self.send_plot([plot1,plot2])
249         '''
250         self._send_plot([myplot])
251         
252
253     def do_myfirstscatter(self,args):
254         '''
255         How to draw a scatter plot.
256         '''
257         #We generate some interesting data to plot for this example.
258         xdata1=np.arange(-5,5,1)
259         xdata2=np.arange(-5,5,1)
260         ydata1=[item**2 for item in xdata1]
261         ydata2=[item**3 for item in xdata2]
262         
263         myplot=lhc.PlotObject()
264         myplot.vectors=[[],[]]
265         #Pour 0-th dataset into myplot: 
266         myplot.vectors[0].append(xdata1) #...x
267         myplot.vectors[0].append(ydata1) #...then y
268         
269         #Pour 1-st dataset into myplot: 
270         myplot.vectors[1].append(xdata2) #...x
271         myplot.vectors[1].append(ydata2) #...then y
272         
273         #Add units to x and y axes
274         myplot.units=['x axis','y axis']
275         
276         #Where do we want the plot? 0=top, 1=bottom
277         myplot.destination=1
278         
279         '''None=standard line plot
280         'scatter'=scatter plot
281         By default, the styles attribute is an empty list. If you
282         want to define a scatter plot, you must define all other
283         plots as None or 'scatter', depending on what you want.
284         
285         Here we define the second set to be plotted as scatter,
286         and the first to be plotted as line.
287         '''
288         myplot.styles=[None,'scatter']
289         
290         self._send_plot([myplot])
291         
292
293     def do_clickaround(self,args):
294         '''
295         Here we click two points on the curve and take some parameters from the points
296         we have clicked.
297         '''
298         
299         '''
300         points = self._measure_N_points(N=Int, whatset=Int)
301         *N = number of points to measure(1...n)
302         *whatset = data set to measure (0,1...n)
303         *points = a list of ClickedPoint objects, one for each point requested
304         '''
305         points=self._measure_N_points(N=2,whatset=1)
306         print 'You clicked the following points.'
307         
308         '''
309         These are the absolute coordinates of the
310         point clicked. 
311         [float, float] = x,y
312         '''
313         print 'Absolute coordinates:'
314         print points[0].absolute_coords
315         print points[1].absolute_coords
316         print
317         
318         '''
319         These are the coordinates of the points
320         clicked, remapped on the graph.
321         Hooke looks at the graph point which X
322         coordinate is next to the X coordinate of
323         the point measured, and uses that point
324         as the actual clicked point.
325         [float, float] = x,y
326         '''
327         print 'Coordinates on the graph:'
328         print points[0].graph_coords
329         print points[1].graph_coords
330         print
331         
332         '''
333         These are the indexes of the clicked points
334         on the dataset vector.
335         '''
336         print 'Index of points on the graph:'
337         print points[0].index
338         print points[1].index
339         
340         
341     def help_thedifferentplots(self):
342         '''
343         The *three* different default plots you should be familiar with
344         in Hooke.
345         
346         Each plot contains of course the respective data in their
347         vectors attribute, so here you learn also which data access for
348         each situation.
349         '''
350         print '''
351         1. THE RAW, CURRENT PLOTS
352         
353         self.current
354         ---
355         Contains the current libhookecurve.HookeCurve container object.
356         A HookeCurve object defines only two default attributes:
357         
358         * self.current.path = string
359         The path of the current displayed curve
360         
361         * self.current.curve = libhookecurve.Driver
362         The curve object. This is not only generated by the driver,
363         this IS a driver instance in itself.
364         This means that in self.current.curve you can access the
365         specific driver APIs, if you know them.
366         
367         And defines only one method:
368         * self.current.identify()
369         Fills in the self.current.curve object.
370         See in the cycling tutorial.
371         
372         *****
373         The REAL curve data actually lives in:
374         ---
375         * self.current.curve.default_plots() = [ libhooke.PlotObject ]
376         Contains the raw PlotObject-s, as "spitted out" by the driver, without any
377         intervention.
378         This is as close to the raw data as Hooke gets.
379         
380         One or two plots can be spit out; they are always enclosed in a list.
381         *****
382         
383         Methods of self.current.curve are:
384         ---
385         
386         * self.current.curve.is_me()
387         (Used by identify() only.)
388         
389         * self.current.curve.close_all()
390         Closes all driver open files; see the cycling tutorial.
391         '''
392         
393         print '''
394         2. THE PROCESSED, DEFAULT PLOT
395         
396         The plot that is spitted out by the driver is *not* the usual default plot
397         that is displayed by calling "plot" at the Hooke prompt.
398         
399         This is because the raw, driver-generated plot is usually *processed* by so called
400         *plot processing* functions. We will see in the tutorial how to define
401         them. 
402         
403         For example, in force spectroscopy force curves, raw data are automatically corrected
404         for deflection. Other data can be, say, filtered by default.
405                 
406         The default plots are accessible in 
407         self.plots = [ libhooke.PlotObject ]
408         
409         self.plots[0] is usually the topmost plot
410         self.plots[1] is usually the bottom plot (if present)
411         '''
412         
413         print '''
414         3. THE PLOT DISPLAYED RIGHT NOW.
415         
416         Sometimes the plots you are displaying *right now* is different from the previous
417         two. You may have a fit trace, you may have issued some command that spits out
418         a custom plot and you want to rework that, whatever. 
419         
420         You can obtain in any moment the plot currently displayed by Hooke by issuing
421         
422         PlotObject = self._get_displayed_plot(dest)
423         * dest = Int (0/1)
424         dest=0 : top plot
425         dest=1 : bottom plot
426         '''
427     
428     
429     def do_cycling(self,args):
430         '''
431         Here we cycle through our playlist and print some info on the curves we find.
432         Cycling through the playlist needs a bit of care to avoid memory leaks and dangling
433         open files...
434         
435         Look at the source code for more information.
436         '''
437         
438         def things_when_cycling(item):
439             '''
440             We encapsulate here everything has to open the actual curve file.
441             By doing it all here, we avoid to do acrobacies when deleting objects etc.
442             in the main loop: we do the dirty stuff here.
443             '''
444             
445             '''
446             identify()
447         
448             This method looks for the correct driver in self.drivers to use;
449             and puts the curve content in the .curve attribute.
450             Basically, until identify() is called, the HookeCurve object
451             is just an empty shell. When identify() is called (usually by
452             the Hooke plot routine), the HookeCurve object is "filled" with
453             the actual curve.
454             '''
455           
456             item.identify(self.drivers)
457             
458             '''
459             After the identify(), item.curve contains the curve, and item.curve.default_plots() behaves exactly like
460             self.current.curve.default_plots() -but for the given item.
461             '''
462             itplot=item.curve.default_plots()
463             
464             print 'length of X1 vector:',len(itplot[0].vectors[0][0]) #just to show something
465             
466             '''
467             The following three lines are a magic spell you HAVE to do
468             before closing the function.
469             (Otherwise you will be plagued by unpredicatable, system-dependent bugs.)
470             '''
471             item.curve.close_all() #Avoid open files dangling
472             del item.curve #Avoid memory leaks
473             del item #Just be paranoid. Don't ask.
474             
475             return
476         
477         
478         c=0
479         for item in self.current_list:
480             print 'Looking at curve ',c,'of',len(self.current_list)
481             things_when_cycling(item)
482             c+=1
483         
484         return
485         
486             
487         
488     def plotmanip_absvalue(self, plot, current, customvalue=None):
489         '''
490         This function defines a PLOT MANIPULATOR.
491         A plot manipulator is a function that takes a plot in input, does something to the plot
492         and returns the modified plot in output.
493         The function, once plugged, gets automatically called everytime self.plots is updated
494         
495         For example, in force spectroscopy force curves, raw data are automatically corrected
496         for deflection. Other data can be, say, filtered by default.
497         
498         To create and activate a plot manipulator you have to:
499             * Write a function (like this) which name starts with "plotmanip_" (just like commands
500               start with "do_")
501             * The function must support four arguments:
502               self : (as usual)
503               plot : a plot object
504               current : (usually not used, deprecated)
505               customvalue=None : a variable containing custom value(s) you need for your plot manipulators.
506             * The function must return a plot object.
507             * Add an entry in hooke.conf: if your function is "plotmanip_something" you will have
508               to add <something/> in the plotmanips section: example
509             
510             <plotmanips>
511                 <detriggerize/>
512                 <correct/>
513                 <median/>
514                 <something/>        
515             </plotmanips>
516             
517             Important: Plot manipulators are *in pipe*: each plot manipulator output becomes the input of the next one.
518             The order in hooke.conf *is the order* in which plot manipulators are connected, so in the example above
519             we have:
520             self.current.curve.default_plots() --> detriggerize --> correct --> median --> something --> self.plots
521         '''
522         
523         '''
524         Here we see what is in a configuration variable to enable/disable the plot manipulator as user will using
525         the Hooke "set" command.
526         Typing "set tutorial_absvalue 0" disables the plot manipulator; typing "set tutorial_absvalue 1" will enable it.
527         '''
528         if not self.config['tutorial_absvalue']:
529             return plot
530         
531         #We do something to the plot, for demonstration's sake
532         #If we needed variables, we would have used customvalue.
533         plot.vectors[0][1]=[abs(i) for i in plot.vectors[0][1]]
534         plot.vectors[1][1]=[abs(i) for i in plot.vectors[1][1]]
535         
536         #Return the plot object.
537         return plot
538             
539         
540 #TODO IN TUTORIAL:
541 #how to add lines to an existing plot!!
542 #peaks
543 #configuration files
544 #gui plugins