Update CantileverAdjustedExtensionCommand to use flexible column names.
[hooke.git] / hooke / plugin / export.py
1 #!/usr/bin/env python
2
3 '''
4 export.py
5
6 Export commands for Hooke.
7
8 Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)
9
10 This program is released under the GNU General Public License version 2.
11 '''
12
13 import lib.libhooke as lh
14 import wxversion
15 wxversion.select(lh.WX_GOOD)
16
17 import copy
18 import os.path
19 import time
20 import wx
21
22 class exportCommands(object):
23     '''
24     Export force curves, fits and results in different formats
25     '''
26
27     def _plug_init(self):
28         pass
29
30     def do_fits(self):
31         '''
32         Exports all fitting results (if available) in a columnar ASCII format.
33         '''
34         ext = self.GetStringFromConfig('export', 'fits', 'ext')
35         folder = self.GetStringFromConfig('export', 'fits', 'folder')
36         prefix = self.GetStringFromConfig('export', 'fits', 'prefix')
37         separator = self.GetStringFromConfig('export', 'fits', 'separator')
38         #TODO: add list for Tab, Space, Comma, Other
39         #add string for Other
40
41         active_file = self.GetActiveFile()
42         plot = self.GetDisplayedPlot()
43
44         #add empty columns before adding new results if necessary
45         if plot is not None:
46             for results_str, results in plot.results.items():
47                 for curve in results.results:
48                     output = []
49                     header_str = ''
50                     if curve.visible:
51                         header_str += curve.label + '_x (' + curve.units.x + ')' + separator + curve.label + '_y (' + curve.units.y + ')'
52                         output.append(header_str)
53                         for index, row in enumerate(curve.x):
54                             output.append(separator.join([str(curve.x[index]), str(curve.y[index])]))
55                     if output:
56                         #TODO: add option to replace or add the new file extension
57                         #add option to rename file from default.000 to default_000
58                         filename = os.path.basename(active_file.filename)
59                         filename = ''.join([prefix, filename, '_', curve.label, '.', ext])
60                         filename = os.path.join(folder, filename)
61                         output_file = open(filename, 'w')
62                         output_file.write('\n'.join(output))
63                         output_file.close
64
65     def do_force_curve(self):
66         '''
67         TXT
68         Saves the current curve as a text file
69         Columns are, in order:
70         X1 , Y1 , X2 , Y2 , X3 , Y3 ...
71
72         -------------
73         Syntax: txt [filename] {plot to export}
74         '''
75
76         ext = self.GetStringFromConfig('export', 'force_curve', 'ext')
77         folder = self.GetStringFromConfig('export', 'force_curve', 'folder')
78         prefix = self.GetStringFromConfig('export', 'force_curve', 'prefix')
79         separator = self.GetStringFromConfig('export', 'force_curve', 'separator')
80         #TODO: add list for Tab, Space, Comma, Other
81         #add string for Other
82
83         active_file = self.GetActiveFile()
84         #create the header from the raw plot (i.e. only the force curve)
85         plot = self.GetDisplayedPlotRaw()
86
87         output = []
88         header_str = ''
89         for index, curve in enumerate(plot.curves):
90             #TODO: only add labels for original curves (i.e. excluding anything added after the fact)
91             header_str += curve.label + '_x (' + curve.units.x + ')' + separator + curve.label + '_y (' + curve.units.y + ')'
92             if index < len(plot.curves) - 1:
93                 header_str += separator
94         output.append(header_str)
95
96         #export the displayed plot
97         plot = self.GetDisplayedPlot()
98         extension = plot.curves[lh.EXTENSION]
99         retraction = plot.curves[lh.RETRACTION]
100         for index, row in enumerate(extension.x):
101             output.append(separator.join([str(extension.x[index]), str(extension.y[index]), str(retraction.x[index]), str(retraction.y[index])]))
102
103         if output:
104             #TODO: add option to replace or add the new file extension
105             #add option to rename file from default.000 to default_000
106             filename = os.path.basename(active_file.filename)
107             filename = ''.join([prefix, filename, '.', ext])
108             filename = os.path.join(folder, filename)
109             output_file = open(filename, 'w')
110             output_file.write('\n'.join(output))
111             output_file.close
112
113     def do_notes(self):
114         '''
115         Exports the note for all the files in a playlist.
116         '''
117         filename = self.GetStringFromConfig('export', 'notes', 'filename')
118         folder = self.GetStringFromConfig('export', 'notes', 'folder')
119         prefix = self.GetStringFromConfig('export', 'notes', 'prefix')
120         use_playlist_filename = self.GetBoolFromConfig('export', 'notes', 'use_playlist_filename')
121         
122         playlist = self.GetActivePlaylist()
123         output_str = ''
124         for current_file in playlist.files:
125             output_str = ''.join([output_str, current_file.filename, '  |  ', current_file.note, '\n'])
126         if output_str:
127             output_str = ''.join(['Notes taken at ', time.asctime(), '\n', playlist.filename, '\n', output_str])
128             if use_playlist_filename:
129                 path, filename = os.path.split(playlist.filename)
130                 filename = lh.remove_extension(filename)
131             filename = ''.join([prefix, filename, '.txt'])
132             filename = os.path.join(folder, filename)
133             output_file = open(filename, 'w')
134             output_file.write(output_str)
135             output_file.close
136         else:
137             dialog = wx.MessageDialog(None, 'No notes found, file not saved.', 'Info', wx.OK)
138             dialog.ShowModal()
139
140     def do_overlay(self):
141         '''
142         Exports all retraction files in a playlist with the same scale.
143         The files can then be overlaid in a graphing program to see which
144         ones have the same shape.
145         Use this export command only on filtered lists as it takes a long time
146         to complete even with a small number of curves.
147         '''
148         playlist = self.GetActivePlaylist()
149
150         filename_prefix = self.GetStringFromConfig('export', 'overlay', 'prefix')
151
152         differences_x = []
153         differences_y = []
154         number_of_curves = playlist.count
155         message_str = ''.join([str(number_of_curves), ' files to load.\n\n'])
156         progress_dialog = wx.ProgressDialog('Loading', message_str, maximum=number_of_curves, parent=self, style=wx.PD_APP_MODAL|wx.PD_SMOOTH|wx.PD_AUTO_HIDE)
157         for index, current_file in enumerate(playlist.files):
158             current_file.identify(self.drivers)
159             plot = current_file.plot
160
161             plot.raw_curves = copy.deepcopy(plot.curves)
162             #apply all active plotmanipulators and add the 'manipulated' data
163             for plotmanipulator in self.plotmanipulators:
164                 if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name):
165                     plot = plotmanipulator.method(plot, current_file)
166             #add corrected curves to plot
167             plot.corrected_curves = copy.deepcopy(plot.curves)
168
169             curve = current_file.plot.corrected_curves[lh.RETRACTION]
170             differences_x.append(curve.x[0] - curve.x[-1])
171             differences_y.append(curve.x[0] - curve.y[-1])
172             progress_dialog.Update(index, ''.join([message_str, 'Loading ', str(index + 1), '/', str(number_of_curves)]))
173         progress_dialog.Destroy()
174
175         max_x = max(differences_x)
176         max_y = max(differences_y)
177         message_str = ''.join([str(number_of_curves), ' files to export.\n\n'])
178         for index, current_file in enumerate(playlist.files):
179             curve = current_file.plot.corrected_curves[lh.RETRACTION]
180             first_x = curve.x[0]
181             first_y = curve.y[0]
182             new_x = [x - first_x for x in curve.x]
183             new_y = [y - first_y for y in curve.y]
184             new_x.append(-max_x)
185             new_y.append(-max_y)
186             output_str = ''
187             for row_index, row in enumerate(new_x):
188                 output_str += ''.join([str(new_x[row_index]), ', ', str(new_y[row_index]), '\n'])
189
190             if output_str:
191                 filename = ''.join([filename_prefix, current_file.name])
192                 filename = current_file.filename.replace(current_file.name, filename)
193                 output_file = open(filename, 'w')
194                 output_file.write(output_str)
195                 output_file.close
196         progress_dialog.Destroy()
197
198     def do_results(self, append=None, filename='', separator=''):
199         '''
200         Exports all visible fit results in a playlist into a delimited text file
201         append: set append to True if you want to append to an existing results file
202         filename: the filename and path of the results file
203         separator: the separator between columns
204         '''
205         if not append:
206             append = self.GetStringFromConfig('export', 'results', 'append')
207         if filename == '':
208             filename = self.GetStringFromConfig('export', 'results', 'filename')
209         if separator == '':
210             separator = self.GetStringFromConfig('export', 'results', 'separator')
211
212         playlist = self.GetActivePlaylist()
213         output_str = ''
214         header_str = ''
215         for current_file in playlist.files:
216             if len(current_file.plot.results) > 0:
217                 for key in current_file.plot.results.keys():
218                     #if there are different types of fit results in the playlist, the header might have to change
219                     #here, we generate a temporary header and compare it to the current header
220                     #if they are different, the tempeorary header is used
221                     #we get the header from the fit and add the 'filename' column
222                     temporary_header_str = ''.join([current_file.plot.results[key].get_header_as_str(), separator, 'Filename'])
223                     if temporary_header_str != header_str:
224                         header_str = ''.join([current_file.plot.results[key].get_header_as_str(), separator, 'Filename'])
225                         output_str = ''.join([output_str, header_str, '\n'])
226                     for index, result in enumerate(current_file.plot.results[key].results):
227                         if result.visible:
228                             #similar to above, we get the result from the fit and add the filename
229                             line_str = current_file.plot.results[key].get_result_as_string(index)
230                             line_str = ''.join([line_str, separator, current_file.filename])
231                             output_str = ''.join([output_str, line_str, '\n'])
232         if output_str:
233             output_str = ''.join(['Analysis started ', time.asctime(), '\n', output_str])
234
235             if append and os.path.isfile(filename):
236                 output_file = open(filename,'a')
237             else:
238                 output_file = open(filename, 'w')
239                 output_file.write(output_str)
240                 output_file.close
241         else:
242             dialog = wx.MessageDialog(None, 'No results found, file not saved.', 'Info', wx.OK)
243             dialog.ShowModal()