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