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