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