1 # Copyright (C) 2007-2010 Massimo Sandal <devicerandom@gmail.com>
2 # W. Trevor King <wking@drexel.edu>
4 # This file is part of Hooke.
6 # Hooke is free software: you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation, either
9 # version 3 of the License, or (at your option) any later version.
11 # Hooke is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with Hooke. If not, see
18 # <http://www.gnu.org/licenses/>.
20 """This plugin contains example commands to teach how to write an
21 Hooke plugin, including description of main Hooke internals.
26 from .. import curve as lhc
29 SYNTAX OF DATA TYPE DECLARATION:
31 [ type ] = list containing objects of type
32 {typekey:typearg} = dictionary with keys of type typekey and args of type typearg
33 ( type ) = tuple containing objects of type
37 class tutorialCommands(object):
39 Here we define the class containing all the Hooke commands we want to define
42 Notice the class name!!
43 The syntax is filenameCommands. That is, if your plugin is pluggy.py, your class
44 name is pluggyCommands.
46 Otherwise, the class will be ignored by Hooke.
51 This is the plugin initialization.
52 When Hooke starts and the plugin is loaded, this function is executed.
53 If there is something you need to do when Hooke starts, code it in this function.
55 print 'I am the Tutorial plugin initialization!'
57 #Here we initialize a local configuration variable; see plotmanip_absvalue() for explanation.
58 self.config['tutorial_absvalue']=0
61 def do_nothing(self,args):
63 This is a boring but working example of an actual Hooke command.
64 A Hooke command is a function of the xxxxCommands class, which is ALWAYS defined
67 def do_nameofcommand(self,args)
69 *do_ is needed to make Hooke understand this function is a command
70 *nameofcommand is how the command will be called in the Hooke command line.
72 *args is ALWAYS needed (otherwise Hooke will crash executing the command). We will see
75 Note that if you now start Hooke with this plugin activated and you type in the Hooke command
76 line "help nothing" you will see this very text as output. So the help of a command is a
77 string comment below the function definition, like this one.
79 Commands usually return None.
81 print 'I am a Hooke command. I do nothing.'
83 def do_printargs(self,args):
85 This command prints the args you give to it.
86 args is always a string, that contains everything you write after the command.
87 So if you issue "mycommand blah blah 12345" args is "blah blah 12345".
89 Again, args is needed in the definition even if your command does not use it.
91 print 'You gave me those args: '+args
93 def help_tutorial(self):
95 This is a help function.
96 If you want a help function for something that is not a command, you can write a help
97 function like this. Calling "help tutorial" will execute this function.
99 print 'You called help_tutorial()'
101 def do_environment(self,args):
103 This plugin contains a panoramic of the Hooke command line environment variables,
104 and prints their current value.
108 TYPE: [ curve.HookeCurve ], len=variable
109 contains the actual playlist of Hooke curve objects.
110 Each HookeCurve object represents a reference to a data file.
111 We will see later in detail how do they work.
113 print 'current_list length:',len(self.current_list)
114 print 'current_list 0th:',self.current_list[0]
118 contains the index of
119 the current curve in the playlist
121 print 'pointer: ',self.pointer
124 TYPE: curve.HookeCurve
125 contains the current curve displayed.
126 We will see later how it works.
128 print 'current:',self.current
131 TYPE: [ curve.PlotObject ], len=1,2
132 contains the current default plots.
133 Each PlotObject contains all info needed to display
134 the plot: apart from the data vectors, the title, destination
136 Usually self.plots[0] is the default topmost plot, self.plots[1] is the
137 accessory bottom plot.
139 print 'plots:',self.plots
142 TYPE: { string:anything }
143 contains the current Hooke configuration variables, in form of a dictionary.
145 print 'config:',self.config
149 Contains the ordered plot manipulation functions.
150 These functions are called to modify the default plot by default before it is plotted.
151 self.plots contains the plot passed through the plot manipulators.
152 We will see it better later.
153 *YOU SHOULD NEVER MODIFY THAT*
155 print 'plotmanip: ',self.plotmanip
159 Contains the plot reading drivers.
160 *YOU SHOULD NEVER MODIFY THAT*
162 print 'drivers: ',self.drivers
166 Contains the wx Frame of the GUI.
167 ***NEVER, EVER TOUCH THAT.***
169 print 'frame: ',self.frame
171 '''self.list_of_events
172 TYPE: { string:wx.Event }
173 Contains the wx.Events to communicate with the GUI.
174 Usually not necessary to use it, unless you want
175 to create a GUI plugin.
177 print 'list of events:',self.list_of_events
179 '''self.events_from_gui
181 Contains the Queue where data from the GUI is put.
182 Usually not necessary to use it, unless you want
183 to create a GUI plugin.
185 print 'events from gui:',self.events_from_gui
187 '''self.playlist_saved
188 TYPE: Int (0/1) ; Boolean
189 Flag that tells if the playlist has been saved or not.
191 print 'playlist saved:',self.playlist_saved
193 '''self.playlist_name
195 Name of current playlist
197 print 'playlist name:',self.playlist_name
200 TYPE: Int (0/1) ; Boolean
201 Flag that tells if the playlist has been saved or not.
203 print 'notes saved:',self.notes_saved
206 def do_myfirstplot(self,args):
208 In this function, we see how to create a PlotObject and send it to the screen.
209 ***Read the code of PlotObject in curve.py before!***.
212 #We generate some interesting data to plot for this example.
213 xdata1=np.arange(-5,5,0.1)
214 xdata2=np.arange(-5,5,0.1)
215 ydata1=[item**2 for item in xdata1]
216 ydata2=[item**3 for item in xdata2]
219 #The PlotObject class lives in the curve library.
220 myplot=lhc.PlotObject()
222 The *data* of the plot live in the .vectors list.
224 plot.vectors is a multidimensional array:
227 plot.vectors[2]=sett3
230 2 curves in a x,y plot are:
231 [[[x1],[y1]],[[x2],[y2]]]
234 [[[1,2,3,4],[10,20,30,40]],[[3,6,9,12],[30,60,90,120]]]
235 x1 = self.vectors[0][0]
236 y1 = self.vectors[0][1]
237 x2 = self.vectors[1][0]
238 y2 = self.vectors[1][1]
240 #Pour 0-th dataset into myplot:
241 myplot.add_set(xdata1,ydata1)
243 #Pour 1-st dataset into myplot:
244 myplot.add_set(xdata2,ydata2)
246 #Add units to x and y axes
247 #units=[string, string]
248 myplot.units=['x axis','y axis']
250 #Where do we want the plot? 0=top, 1=bottom
253 '''Send it to the GUI.
254 Note that you *have* to encapsulate it into a list, so you
255 have to send [myplot], not simply myplot.
257 You can also send more two plots at once
258 self.send_plot([plot1,plot2])
260 self._send_plot([myplot])
263 def do_myfirstscatter(self,args):
265 How to draw a scatter plot.
267 #We generate some interesting data to plot for this example.
268 xdata1=np.arange(-5,5,1)
269 xdata2=np.arange(-5,5,1)
270 ydata1=[item**2 for item in xdata1]
271 ydata2=[item**3 for item in xdata2]
273 myplot=lhc.PlotObject()
274 myplot.add_set(xdata1,ydata1)
275 myplot.add_set(xdata2,ydata2)
278 #Add units to x and y axes
279 myplot.units=['x axis','y axis']
281 #Where do we want the plot? 0=top, 1=bottom
284 '''None=standard line plot
285 'scatter'=scatter plot
286 By default, the styles attribute is an empty list. If you
287 want to define a scatter plot, you must define all other
288 plots as None or 'scatter', depending on what you want.
290 Here we define the second set to be plotted as scatter,
291 and the first to be plotted as line.
293 Here we define also the colors to be the default Matplotlib colors
295 myplot.styles=[None,'scatter']
296 myplot.colors=[None,None]
297 self._send_plot([myplot])
300 def do_clickaround(self,args):
302 Here we click two points on the curve and take some parameters from the points
307 points = self._measure_N_points(N=Int, whatset=Int)
308 *N = number of points to measure(1...n)
309 *whatset = data set to measure (0,1...n)
310 *points = a list of ClickedPoint objects, one for each point requested
312 points=self._measure_N_points(N=2,whatset=1)
313 print 'You clicked the following points.'
316 These are the absolute coordinates of the
320 print 'Absolute coordinates:'
321 print points[0].absolute_coords
322 print points[1].absolute_coords
326 These are the coordinates of the points
327 clicked, remapped on the graph.
328 Hooke looks at the graph point which X
329 coordinate is next to the X coordinate of
330 the point measured, and uses that point
331 as the actual clicked point.
334 print 'Coordinates on the graph:'
335 print points[0].graph_coords
336 print points[1].graph_coords
340 These are the indexes of the clicked points
341 on the dataset vector.
343 print 'Index of points on the graph:'
344 print points[0].index
345 print points[1].index
348 def help_thedifferentplots(self):
350 The *three* different default plots you should be familiar with
353 Each plot contains of course the respective data in their
354 vectors attribute, so here you learn also which data access for
358 1. THE RAW, CURRENT PLOTS
362 Contains the current curve.HookeCurve container object.
363 A HookeCurve object defines only two default attributes:
365 * self.current.path = string
366 The path of the current displayed curve
368 * self.current.curve = curve.Driver
369 The curve object. This is not only generated by the driver,
370 this IS a driver instance in itself.
371 This means that in self.current.curve you can access the
372 specific driver APIs, if you know them.
374 And defines only one method:
375 * self.current.identify()
376 Fills in the self.current.curve object.
377 See in the cycling tutorial.
380 The REAL curve data actually lives in:
382 * self.current.curve.default_plots() = [ libhooke.PlotObject ]
383 Contains the raw PlotObject-s, as "spitted out" by the driver, without any
385 This is as close to the raw data as Hooke gets.
387 One or two plots can be spit out; they are always enclosed in a list.
390 Methods of self.current.curve are:
393 * self.current.curve.is_me()
394 (Used by identify() only.)
396 * self.current.curve.close_all()
397 Closes all driver open files; see the cycling tutorial.
401 2. THE PROCESSED, DEFAULT PLOT
403 The plot that is spitted out by the driver is *not* the usual default plot
404 that is displayed by calling "plot" at the Hooke prompt.
406 This is because the raw, driver-generated plot is usually *processed* by so called
407 *plot processing* functions. We will see in the tutorial how to define
410 For example, in force spectroscopy force curves, raw data are automatically corrected
411 for deflection. Other data can be, say, filtered by default.
413 The default plots are accessible in
414 self.plots = [ libhooke.PlotObject ]
416 self.plots[0] is usually the topmost plot
417 self.plots[1] is usually the bottom plot (if present)
421 3. THE PLOT DISPLAYED RIGHT NOW.
423 Sometimes the plots you are displaying *right now* is different from the previous
424 two. You may have a fit trace, you may have issued some command that spits out
425 a custom plot and you want to rework that, whatever.
427 You can obtain in any moment the plot currently displayed by Hooke by issuing
429 PlotObject = self._get_displayed_plot(dest)
436 def do_cycling(self,args):
438 Here we cycle through our playlist and print some info on the curves we find.
439 Cycling through the playlist needs a bit of care to avoid memory leaks and dangling
442 Look at the source code for more information.
445 def things_when_cycling(item):
447 We encapsulate here everything has to open the actual curve file.
448 By doing it all here, we avoid to do acrobacies when deleting objects etc.
449 in the main loop: we do the dirty stuff here.
455 This method looks for the correct driver in self.drivers to use;
456 and puts the curve content in the .curve attribute.
457 Basically, until identify() is called, the HookeCurve object
458 is just an empty shell. When identify() is called (usually by
459 the Hooke plot routine), the HookeCurve object is "filled" with
463 item.identify(self.drivers)
466 After the identify(), item.curve contains the curve, and item.curve.default_plots() behaves exactly like
467 self.current.curve.default_plots() -but for the given item.
469 itplot=item.curve.default_plots()
471 print 'length of X1 vector:',len(itplot[0].vectors[0][0]) #just to show something
474 The following three lines are a magic spell you HAVE to do
475 before closing the function.
476 (Otherwise you will be plagued by unpredicatable, system-dependent bugs.)
478 item.curve.close_all() #Avoid open files dangling
479 del item.curve #Avoid memory leaks
480 del item #Just be paranoid. Don't ask.
486 for item in self.current_list:
487 print 'Looking at curve ',c,'of',len(self.current_list)
488 things_when_cycling(item)
495 def plotmanip_absvalue(self, plot, current, customvalue=None):
497 This function defines a PLOT MANIPULATOR.
498 A plot manipulator is a function that takes a plot in input, does something to the plot
499 and returns the modified plot in output.
500 The function, once plugged, gets automatically called everytime self.plots is updated
502 For example, in force spectroscopy force curves, raw data are automatically corrected
503 for deflection. Other data can be, say, filtered by default.
505 To create and activate a plot manipulator you have to:
506 * Write a function (like this) which name starts with "plotmanip_" (just like commands
508 * The function must support four arguments:
511 current : (usually not used, deprecated)
512 customvalue=None : a variable containing custom value(s) you need for your plot manipulators.
513 * The function must return a plot object.
514 * Add an entry in hooke.conf: if your function is "plotmanip_something" you will have
515 to add <something/> in the plotmanips section: example
524 Important: Plot manipulators are *in pipe*: each plot manipulator output becomes the input of the next one.
525 The order in hooke.conf *is the order* in which plot manipulators are connected, so in the example above
527 self.current.curve.default_plots() --> detriggerize --> correct --> median --> something --> self.plots
531 Here we see what is in a configuration variable to enable/disable the plot manipulator as user will using
532 the Hooke "set" command.
533 Typing "set tutorial_absvalue 0" disables the plot manipulator; typing "set tutorial_absvalue 1" will enable it.
535 if not self.config['tutorial_absvalue']:
538 #We do something to the plot, for demonstration's sake
539 #If we needed variables, we would have used customvalue.
540 plot.vectors[0][1]=[abs(i) for i in plot.vectors[0][1]]
541 plot.vectors[1][1]=[abs(i) for i in plot.vectors[1][1]]
543 #Return the plot object.
548 #how to add lines to an existing plot!!