Added README and reorganized directory structure (breaks code)
[hooke.git] / hooke / driver / tutorialdriver.py
1 #!/usr/bin/env python
2
3 '''
4 tutorialdriver.py
5
6 TUTORIAL DRIVER FOR HOOKE
7
8 Example driver to teach how to write a driver for data types.
9 (c)Massimo Sandal 2008
10 '''
11
12 '''
13 Here we define a (fake) file format that is read by this driver. The file format is as following:
14
15 TUTORIAL_FILE
16 PLOT1
17 X1
18 n1
19 n2
20 ...
21 nN
22 Y1
23 n1
24 n2
25 ...
26 nN
27 X2
28 n1
29 n2
30 ..
31 nN
32 Y2
33 n1
34 n2
35 ..
36 nN
37 PLOT2
38 X1
39 ...
40 Y1
41 ...
42 X2
43 ...
44 Y2
45 ...
46 END
47 that is, two plots with two datasets each.
48 '''
49
50 import libhookecurve as lhc #We need to import this library to define some essential data types
51
52 class tutorialdriverDriver(lhc.Driver):
53     '''
54     This is a *generic* driver, not a specific force spectroscopy driver.
55     See the written documentation to see what a force spectroscopy driver must be defined to take into account Hooke facilities.
56     
57     Our driver is a class with the name convention nameofthedriverDriver, where "nameofthedriver" is the filename.
58     The driver must inherit from the parent class lhc.Driver, so the syntax is
59     class nameofthedriverDriver(lhc.Driver)
60     '''
61     def __init__(self, filename):
62         '''
63         THIS METHOD MUST BE DEFINED.
64         The __init__ method MUST call the filename, so that it can open the file.
65         '''
66         self.filename=filename #self.filename can always be useful, and should be defined
67         self.filedata = open(filename,'r') #We open the file
68         '''
69         In this case, we have a data format that is just a list of ASCII values, so we can just divide that in rows, and generate a list
70         with each item being a row.
71         Of course if your data files are binary, or follow a different approach, do whatever you need. :) 
72         '''
73         self.data = list(self.filedata)
74         self.filedata.close() #remember to close the file
75         
76         '''These are two strings that can be used by Hooke commands/plugins to understand what they are looking at. They have no other
77         meaning. They have to be somehow defined however - commands often look for those variables.
78         
79         self.filetype should contain the name of the exact filetype defined by the driver (so that filetype-specific commands can know
80                       if they're dealing with the correct filetype)
81         self.experiment should contain instead the type of data involved (for example, various drivers can be used for force-clamp experiments,
82                       but hooke commands could like to know if we're looking at force clamp data, regardless of their origin, and not other 
83                       kinds of data)
84         
85         Of course, all other variables you like can be defined in the class.
86         '''
87         self.filetype = 'tutorial'
88         self.experiment = 'generic'
89     
90     def is_me(self):
91         '''
92         THIS METHOD MUST BE DEFINED.
93         RETURNS: Boolean (True or False)
94         This method must be an heuristic that looks at the file content and decides if the file can be opened by the driver itself. 
95         It returns True if the file opened can be interpreted by the current driver, False otherwise. 
96         Defining this method allows Hooke to understand what kind of files we're looking at automatically.
97         
98         We have to re-open/re-close the file here.
99         '''
100         
101         myfile=open(self.filename, 'r')
102         headerline=myfile.readlines()[0] #we take the first line
103         myfile.close()
104             
105         '''
106         Here, our "magic fingerprint" is the TUTORIAL_FILE header. Of course, depending on the data file, you can have interesting
107         headers, or patterns, etc. that you can use to guess the data format. What matters is successful recognizing, and returning
108         a boolean (True/False).
109         '''
110         if headerline[:-1]=='TUTORIAL_FILE': #[:-1], otherwise the return character is included in the line
111             return True
112         else:
113             return False
114         
115     def _generate_vectors(self):
116         '''
117         Here we parse the data and generate the raw vectors. This method has only to do with the peculiar file format here, so it's of
118         no big interest (I just wrote it to present a functional driver). 
119         
120         Only thing to remember, it can be nice to call methods that are used only "internally" by the driver (or by plugins) with a
121         "_" prefix, so to have a visual remark. But it's just an advice.
122         '''
123         vectors={'PLOT1':[[],[],[],[]] , 'PLOT2':[[],[],[],[]]}
124         positions={'X1':0,'Y1':1,'X2':2,'Y2':3}
125         whatplot=''
126         pos=0
127         for item in self.data:
128             try:
129                 num=float(item)
130                 vectors[whatplot][pos].append(num)
131             except ValueError:
132                 if item[:-1]=='PLOT1':
133                     whatplot=item[:-1]
134                 elif item[:-1]=='PLOT2':
135                     whatplot=item[:-1]
136                 elif item[0]=='X' or item[0]=='Y':
137                     pos=positions[item[:-1]]         
138                 else:
139                     pass
140         
141         return vectors
142
143     def close_all(self):
144         '''
145         THIS METHOD MUST BE DEFINED.
146         This method is a precaution method that is invoked when cycling to avoid eventually dangling open files.
147         In this method, all file objects defined in the driver must be closed.
148         '''
149         self.filename.close()
150     
151     
152     def default_plots(self):
153         '''
154         THIS METHOD MUST BE DEFINED.
155         RETURNS: [ lhc.PlotObject ] or [ lhc.PlotObject, lhc.PlotObject]
156         
157         This is the method that returns the plots to Hooke.
158         It must return a list with *one* or *two* PlotObjects.
159         
160         See the libhookecurve.py source code to see how PlotObjects are defined and work in detail.
161         '''
162         gen_vectors=self._generate_vectors()
163         
164         #Here we create the main plot PlotObject and initialize its vectors.
165         main_plot=lhc.PlotObject()
166         main_plot.vectors=[]
167         #The same for the other plot.
168         other_plot=lhc.PlotObject()
169         other_plot.vectors=[]
170         
171         '''
172         Now we fill the plot vectors with our data.
173                                                            set 1                           set 2
174         The "correct" shape of the vector is [ [[x1,x2,x3...],[y1,y2,y3...]] , [[x1,x2,x3...],[y1,y2,y3...]] ], so we have to put stuff in this way into it.
175         
176         The add_set() method takes care of this , just use plot.add_set(x,y).
177         '''
178         main_plot.add_set(gen_vectors['PLOT1'][0],gen_vectors['PLOT1'][1])
179         main_plot.add_set(gen_vectors['PLOT1'][2],gen_vectors['PLOT1'][3])
180         
181         other_plot.add_set(gen_vectors['PLOT2'][0],gen_vectors['PLOT2'][1])
182         other_plot.add_set(gen_vectors['PLOT2'][2],gen_vectors['PLOT2'][3])
183                 
184         '''
185         normalize_vectors() trims the vectors, so that if two x/y couples are of different lengths, the latest
186         points are trimmed (otherwise we have a python error). Always a good idea to run it, to avoid crashes.
187         '''
188         main_plot.normalize_vectors()
189         other_plot.normalize_vectors()
190         
191         '''
192         Here we define:
193         - units: [string, string], define the measure units of X and Y axes
194         - destination: 0/1 , defines where to plot the plot (0=top, 1=bottom), default=0
195         - title: string , the plot title.
196         
197         for each plot.
198         Again, see libhookecurve.py comments for details.
199         '''
200         main_plot.units=['unit of x','unit of y']
201         main_plot.destination=0
202         main_plot.title=self.filename+' main'
203         
204         other_plot.units=['unit of x','unit of y']
205         other_plot.destination=1
206         other_plot.title=self.filename+' other'
207         
208         return [main_plot, other_plot]