From: illysam Date: Fri, 19 Feb 2010 21:07:57 +0000 (+0000) Subject: hooke.py X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=d30fbf5db48d369da4cdaeb97b9a0f1951b41ec8;p=hooke.git hooke.py - added ApplyPlotmanipulators() - added configuration support to _delta() - added linewidth option to UpdatePlot() - 'fixed' GetDisplayedPlot() hooke configspec.ini - added curvetools plugin hooke.ini - added curvetools plugin - added multidistance plugin - removed plot plugin (do_replot() was moved to procplots.py) curve.py - added linewidth playlist.py - fixed the way the filename is retrieved from the playlist file plugin.py - added option section - removed option key - added comments prettyformat.py - fixed pretty_format() for 0 and NaN results.py - added class ResultsMultiDistance() - fixed set_multipliers() autopeak.ini/autopeak.py - renamed 'peak_color' option to 'color' - removed options: peak_show and peak_size (now in flatfilts.py-do_peaks()) - renamed x_values/y_values to retraction.x/retraction.y, respectively - removed flatten plotmanipulator option (now in convfilt) export.ini/export.py - added append option flatfilts.ini/flatfilts.py - added apply_plotmanipulators option - added configuration support to has_peaks() - removed exec_has_peaks() as it is no longer necessary generalvclamp.ini/generalvclamp.py - added options: color, size to [distance] - renamed 'show_points' to 'show' in [distance] - added options: color, show, size to [force] - added options: color, show, size to [forcebase] - added options: point_color, point_show, point_size, slope_color, slope_linewidth, slope_show to [slope] - added configuration support to do_distance(), do_force() procplots.ini/procplots.py - added option centerzero - moved do_replot() form plot.py to procplots.py results.py - fixed do_show_results() to work with configuration options --- diff --git a/config/hooke configspec.ini b/config/hooke configspec.ini index 3bb1cda..f1cd81e 100644 --- a/config/hooke configspec.ini +++ b/config/hooke configspec.ini @@ -27,6 +27,7 @@ active = string(default = Default) [plugins] autopeak = boolean(default = True) +curvetools = boolean(default = True) export = boolean(default = True) fit = boolean(default = True) flatfilts = boolean(default = True) diff --git a/config/hooke.ini b/config/hooke.ini index b803753..6f96553 100644 --- a/config/hooke.ini +++ b/config/hooke.ini @@ -39,12 +39,13 @@ default = Default #this section defines which plugins have to be loaded by Hooke [plugins] autopeak = True +curvetools = True export = True fit = True flatfilts = True generalvclamp = True +multidistance = True playlist = True -plot = True procplots = True results = True diff --git a/hooke.py b/hooke.py index 8be70d1..7c7a311 100644 --- a/hooke.py +++ b/hooke.py @@ -156,6 +156,8 @@ class HookeFrame(wx.Frame): self.plotmanipulators = [] #self.plugins contains: {the name of the plugin: [caption, function]} self.plugins = {} + #self.results_str contains the type of results we want to display + self.results_str = 'wlc' #tell FrameManager to manage this frame self._mgr = aui.AuiManager() @@ -440,11 +442,22 @@ class HookeFrame(wx.Frame): def AppliesPlotmanipulator(self, name): ''' - returns True if the plotmanipulator 'name' is applied, False otherwise + Returns True if the plotmanipulator 'name' is applied, False otherwise name does not contain 'plotmanip_', just the name of the plotmanipulator (e.g. 'flatten') ''' return self.GetBoolFromConfig('core', 'plotmanipulators', name) + def ApplyPlotmanipulators(self, plot, plot_file): + ''' + Apply all active plotmanipulators. + ''' + if plot is not None and plot_file is not None: + manipulated_plot = copy.deepcopy(plot) + for plotmanipulator in self.plotmanipulators: + if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name): + manipulated_plot = plotmanipulator.method(manipulated_plot, plot_file) + return manipulated_plot + def CreateApplicationIcon(self): iconFile = 'resources' + os.sep + 'microscope.ico' icon = wx.Icon(iconFile, wx.BITMAP_TYPE_ICO) @@ -613,8 +626,8 @@ class HookeFrame(wx.Frame): def GetDisplayedPlot(self): plot = copy.deepcopy(self.displayed_plot) - plot.curves = [] - plot.curves = copy.deepcopy(plot.curves) + #plot.curves = [] + #plot.curves = copy.deepcopy(plot.curves) return plot def GetDisplayedPlotCorrected(self): @@ -926,9 +939,10 @@ class HookeFrame(wx.Frame): def OnResultsCheck(self, index, flag): #TODO: fix for multiple results results = self.GetActivePlot().results - fit_function_str = self.GetStringFromConfig('results', 'show_results', 'fit_function') - results[fit_function_str].results[index].visible = flag - self.UpdatePlot() + if results.has_key(self.results_str): + results[self.results_str].results[index].visible = flag + results[self.results_str].update() + self.UpdatePlot() def OnSavePerspective(self, event): @@ -1047,35 +1061,6 @@ class HookeFrame(wx.Frame): self.panelFolders.Fit() self._mgr.Update() - def _measure_N_points(self, N, message='', whatset=lh.RETRACTION): - ''' - General helper function for N-points measurements - By default, measurements are done on the retraction - ''' - if message != '': - dialog = wx.MessageDialog(None, message, 'Info', wx.OK) - dialog.ShowModal() - - figure = self.GetActiveFigure() - - xvector = self.displayed_plot.curves[whatset].x - yvector = self.displayed_plot.curves[whatset].y - - clicked_points = figure.ginput(N, timeout=-1, show_clicks=True) - - points = [] - for clicked_point in clicked_points: - point = lh.ClickedPoint() - point.absolute_coords = clicked_point[0], clicked_point[1] - point.dest = 0 - #TODO: make this optional? - #so far, the clicked point is taken, not the corresponding data point - point.find_graph_coords(xvector, yvector) - point.is_line_edge = True - point.is_marker = True - points.append(point) - return points - def _clickize(self, xvector, yvector, index): ''' returns a ClickedPoint() object from an index and vectors of x, y coordinates @@ -1086,10 +1071,25 @@ class HookeFrame(wx.Frame): point.find_graph_coords(xvector, yvector) return point - def _delta(self, color='black', message='Click 2 points', show=True, whatset=1): + def _delta(self, message='Click 2 points', plugin=None): ''' calculates the difference between two clicked points ''' + if plugin is None: + color = 'black' + show_points = True + size = 20 + whatset = lh.RETRACTION + else: + color = self.GetColorFromConfig(plugin.name, plugin.section, plugin.prefix + 'color') + show_points = self.GetBoolFromConfig(plugin.name, plugin.section, plugin.prefix + 'show_points') + size = self.GetIntFromConfig(plugin.name, plugin.section, plugin.prefix + 'size') + whatset_str = self.GetStringFromConfig(plugin.name, plugin.section, plugin.prefix + 'whatset') + if whatset_str == 'extension': + whatset = lh.EXTENSION + if whatset_str == 'retraction': + whatset = lh.RETRACTION + clicked_points = self._measure_N_points(N=2, message=message, whatset=whatset) dx = abs(clicked_points[0].graph_coords[0] - clicked_points[1].graph_coords[0]) dy = abs(clicked_points[0].graph_coords[1] - clicked_points[1].graph_coords[1]) @@ -1101,14 +1101,14 @@ class HookeFrame(wx.Frame): unity = curve.units.y #TODO: move this to clicked_points? - if show: + if show_points: for point in clicked_points: points = copy.deepcopy(curve) points.x = point.graph_coords[0] points.y = point.graph_coords[1] points.color = color - points.size = 20 + points.size = size points.style = 'scatter' plot.curves.append(points) @@ -1116,6 +1116,35 @@ class HookeFrame(wx.Frame): return dx, unitx, dy, unity + def _measure_N_points(self, N, message='', whatset=lh.RETRACTION): + ''' + General helper function for N-points measurements + By default, measurements are done on the retraction + ''' + if message != '': + dialog = wx.MessageDialog(None, message, 'Info', wx.OK) + dialog.ShowModal() + + figure = self.GetActiveFigure() + + xvector = self.displayed_plot.curves[whatset].x + yvector = self.displayed_plot.curves[whatset].y + + clicked_points = figure.ginput(N, timeout=-1, show_clicks=True) + + points = [] + for clicked_point in clicked_points: + point = lh.ClickedPoint() + point.absolute_coords = clicked_point[0], clicked_point[1] + point.dest = 0 + #TODO: make this optional? + #so far, the clicked point is taken, not the corresponding data point + point.find_graph_coords(xvector, yvector) + point.is_line_edge = True + point.is_marker = True + points.append(point) + return points + def do_plotmanipulators(self): ''' Please select the plotmanipulators you would like to use @@ -1215,7 +1244,7 @@ class HookeFrame(wx.Frame): axes_list[destination].set_xlabel(curve.units.x) axes_list[destination].set_ylabel(curve.units.y) if curve.style == 'plot': - axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, zorder=1) + axes_list[destination].plot(curve.x, curve.y, color=curve.color, label=curve.label, lw=curve.linewidth, zorder=1) if curve.style == 'scatter': axes_list[destination].scatter(curve.x, curve.y, color=curve.color, label=curve.label, s=curve.size, zorder=2) @@ -1226,10 +1255,8 @@ class HookeFrame(wx.Frame): self.displayed_plot = copy.deepcopy(active_file.plot) #add raw curves to plot self.displayed_plot.raw_curves = copy.deepcopy(self.displayed_plot.curves) - #apply all active plotmanipulators and add the 'manipulated' data - for plotmanipulator in self.plotmanipulators: - if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name): - self.displayed_plot = plotmanipulator.method(self.displayed_plot, active_file) + #apply all active plotmanipulators + self.displayed_plot = self.ApplyPlotmanipulators(self.displayed_plot, active_file) #add corrected curves to plot self.displayed_plot.corrected_curves = copy.deepcopy(self.displayed_plot.curves) else: @@ -1256,17 +1283,14 @@ class HookeFrame(wx.Frame): figure.subplots_adjust(hspace=0.3) #TODO: add multiple results support to fit in curve.results: - #get the fit_function results to display - fit_function_str = self.GetStringFromConfig('results', 'show_results', 'fit_function') + #display results self.panelResults.ClearResults() - plot = self.GetActivePlot() - if plot is not None: - if plot.results.has_key(fit_function_str): - for curve in plot.results[fit_function_str].results: - add_to_plot(curve) - self.panelResults.DisplayResults(plot.results[fit_function_str]) - else: - self.panelResults.ClearResults() + if self.displayed_plot.results.has_key(self.results_str): + for curve in self.displayed_plot.results[self.results_str].results: + add_to_plot(curve) + self.panelResults.DisplayResults(self.displayed_plot.results[self.results_str]) + else: + self.panelResults.ClearResults() figure.canvas.draw() diff --git a/lib/curve.py b/lib/curve.py index 176d7bf..0d81a96 100644 --- a/lib/curve.py +++ b/lib/curve.py @@ -16,6 +16,7 @@ class Curve(object): self.color = 'blue' self.destination = Destination() self.label = '' + self.linewidth = 1 self.size = 0.5 self.style = 'plot' self.title = '' diff --git a/lib/libhooke.py b/lib/libhooke.py index f69f2d2..11d8565 100644 --- a/lib/libhooke.py +++ b/lib/libhooke.py @@ -74,9 +74,8 @@ class ClickedPoint(object): self.index=dists.index(min(dists)) self.graph_coords=(xvector[self.index], yvector[self.index]) -#----------------------------------------- -#CSV-HELPING FUNCTIONS +#CSV-HELPING FUNCTIONS def transposed2(lists, defval=0): ''' transposes a list of lists, i.e. from [[a,b,c],[x,y,z]] to [[a,x],[b,y],[c,z]] without losing diff --git a/lib/playlist.py b/lib/playlist.py index c82e697..92b9629 100644 --- a/lib/playlist.py +++ b/lib/playlist.py @@ -24,7 +24,7 @@ class Playlist(object): self.figure = None self.files = [] self.generics_dict = {} - self.hidden_attributes = ['curve', 'data', 'driver', 'fits', 'name', 'plots'] + self.hidden_attributes = ['curve', 'data', 'driver', 'fits', 'name', 'plot', 'plots'] self.index = -1 self.name = None self.path = None @@ -75,7 +75,7 @@ class Playlist(object): def load(self, filename): ''' - loads a playlist file + Loads a playlist file ''' self.filename = filename self.path, self.name = os.path.split(filename) @@ -91,9 +91,9 @@ class Playlist(object): #rebuild a data structure from the xml attributes #the next two lines are here for backwards compatibility, newer playlist files use 'filename' instead of 'path' if element.hasAttribute('path'): - filename = lib.libhooke.get_file_path(element.getAttribute('path')) + filename = element.getAttribute('path') if element.hasAttribute('filename'): - filename = lib.libhooke.get_file_path(element.getAttribute('filename')) + filename = element.getAttribute('filename') if os.path.isfile(filename): data_file = lib.file.File(filename) self.files.append(data_file) diff --git a/lib/plugin.py b/lib/plugin.py index a4641a6..24364cb 100644 --- a/lib/plugin.py +++ b/lib/plugin.py @@ -11,8 +11,19 @@ This program is released under the GNU General Public License version 2. ''' class Plugin(object): + ''' + Plugin is a class that is used to facilitate accessing + configuration parameters in a ConfigObj from different plugins. + ''' def __init__(self): - self.key = '' self.name = '' + #if the calling plugin uses a prefix, this can be added to the name + #e.g. autopeak.ini: [[peak_color]] + #flatfilts.ini [[color]] + #are both used to set the color of the peak plot (scatter plot) + #in order to access 'peak_color' rather than 'color', the prefix needs to + #be set to 'peak_' + #if the names are identical, set prefix to '' self.prefix = '' + self.section = '' diff --git a/lib/prettyformat.py b/lib/prettyformat.py index b346115..7297e66 100644 --- a/lib/prettyformat.py +++ b/lib/prettyformat.py @@ -19,22 +19,24 @@ import math from numpy import isnan def pretty_format(fValue, sUnit='', iDecimals=-1, iMultiplier=1, bLeadingSpaces=False): - if fValue != 0: - iLeadingSpaces = 0 - if bLeadingSpaces: - iLeadingSpaces = 5 - if iMultiplier == 1: - iMultiplier=get_multiplier(fValue) - sUnitString = '' - if sUnit != '': - sUnitString = ' ' + get_prefix(iMultiplier) + sUnit - if iDecimals >= 0: - formatString = '% ' + repr(iLeadingSpaces + iDecimals) + '.' + repr(iDecimals) + 'f' - return formatString % (fValue / iMultiplier) + sUnitString - else: - return str(fValue / iMultiplier) + sUnitString - else: + if fValue == 0: return '0' + if isnan(fValue): + return 'NaN' + + iLeadingSpaces = 0 + if bLeadingSpaces: + iLeadingSpaces = 5 + if iMultiplier == 1: + iMultiplier=get_multiplier(fValue) + sUnitString = '' + if sUnit != '': + sUnitString = ' ' + get_prefix(iMultiplier) + sUnit + if iDecimals >= 0: + formatString = '% ' + repr(iLeadingSpaces + iDecimals) + '.' + repr(iDecimals) + 'f' + return formatString % (fValue / iMultiplier) + sUnitString + else: + return str(fValue / iMultiplier) + sUnitString return str(fValue / iMultiplier) + ' ' + get_prefix(fValue / iMultiplier) + sUnit def get_multiplier(fValue): diff --git a/lib/results.py b/lib/results.py index 7535ac9..644fc85 100644 --- a/lib/results.py +++ b/lib/results.py @@ -10,6 +10,8 @@ Copyright 2009 by Dr. Rolf Schmidt (Concordia University, Canada) This program is released under the GNU General Public License version 2. ''' +from numpy import nan + import prettyformat import lib.curve @@ -127,16 +129,18 @@ class Results(object): def set_multipliers(self, index=0): if self.has_results(): - if index >= 0 and index < len(self.results): - for column in self.columns: - #result will contain the results dictionary at 'index' - result = self.results[index][0] - #in position 0 of the result we find the value - self.multipliers[column] = prettyformat.get_multiplier(result[column][0]) - self.has_multipliers = True + for column in self.columns: + #result will contain the results dictionary at 'index' + result = self.results[index].result + #in position 0 of the result we find the value + self.multipliers[column] = prettyformat.get_multiplier(result[column]) + self.has_multipliers = True else: self.has_multipliers = False + def update(self): + pass + class ResultsFJC(Results): def __init__(self): @@ -153,21 +157,42 @@ class ResultsFJC(Results): def set_multipliers(self, index=0): if self.has_results(): - if index >= 0 and index < len(self.results): - for column in self.columns: - #result will contain the results dictionary at 'index' - result = self.results[index].result - #in position 0 of the result we find the value - if column == 'sigma contour length': - self.multipliers[column] = self.multipliers['Contour length'] - elif column == 'sigma Kuhn length': - self.multipliers[column] = self.multipliers['Kuhn length'] - else: - self.multipliers[column] = prettyformat.get_multiplier(result[column]) - self.has_multipliers = True + for column in self.columns: + #result will contain the results dictionary at 'index' + result = self.results[index].result + #in position 0 of the result we find the value + if column == 'sigma contour length': + self.multipliers[column] = self.multipliers['Contour length'] + elif column == 'sigma Kuhn length': + self.multipliers[column] = self.multipliers['Kuhn length'] + else: + self.multipliers[column] = prettyformat.get_multiplier(result[column]) + self.has_multipliers = True else: self.has_multipliers = False +class ResultsMultiDistance(Results): + def __init__(self): + Results.__init__(self) + self.columns = ['Distance'] + self.units['Distance'] = 'm' + self.set_decimals(2) + + def update(self): + if (self.results) > 0: + for result in self.results: + if result.visible: + reference_peak = result.x + break + + for result in self.results: + if result.visible: + result.result['Distance'] = reference_peak - result.x + reference_peak = result.x + else: + result.result['Distance'] = nan + + class ResultsWLC(Results): def __init__(self): Results.__init__(self) @@ -183,17 +208,16 @@ class ResultsWLC(Results): def set_multipliers(self, index=0): if self.has_results(): - if index >= 0 and index < len(self.results): - for column in self.columns: - #result will contain the results dictionary at 'index' - result = self.results[index].result - #in position 0 of the result we find the value - if column == 'sigma contour length': - self.multipliers[column] = self.multipliers['Contour length'] - elif column == 'sigma persistence length': - self.multipliers[column] = self.multipliers['Persistence length'] - else: - self.multipliers[column] = prettyformat.get_multiplier(result[column]) - self.has_multipliers = True + for column in self.columns: + #result will contain the results dictionary at 'index' + result = self.results[index].result + #in position 0 of the result we find the value + if column == 'sigma contour length': + self.multipliers[column] = self.multipliers['Contour length'] + elif column == 'sigma persistence length': + self.multipliers[column] = self.multipliers['Persistence length'] + else: + self.multipliers[column] = prettyformat.get_multiplier(result[column]) + self.has_multipliers = True else: self.has_multipliers = False diff --git a/plugins/autopeak.ini b/plugins/autopeak.ini index d2c5b23..88fb6c0 100644 --- a/plugins/autopeak.ini +++ b/plugins/autopeak.ini @@ -47,6 +47,11 @@ type = enum value = contact point + [[color]] + default = black + type = color + value = "(255,0,128)" + [[fit_function]] default = wlc elements = wlc, fjc, fjcPEG @@ -63,23 +68,6 @@ type = boolean value = False - [[peak_color]] - default = black - type = color - value = "(255,128,0)" - - [[peak_show]] - default = False - type = boolean - value = False - - [[peak_size]] - default = 20 - maximum = 10000 - minimum = 1 - type = integer - value = 50 - [[persistence_length]] default = 0.35e-9 minimum = 0 diff --git a/plugins/autopeak.py b/plugins/autopeak.py index 6e6d5a2..2951ca4 100644 --- a/plugins/autopeak.py +++ b/plugins/autopeak.py @@ -21,7 +21,6 @@ from numpy import mean, RankWarning import warnings warnings.simplefilter('ignore', RankWarning) -#import config import lib.plugin import lib.results @@ -69,49 +68,47 @@ class autopeakCommands: usepoints : fit interval by number of points instead than by nanometers - noflatten : does not use the "flatten" plot manipulator - - When you first issue the command, it will ask for the filename. If you are giving the filename - of an existing file, autopeak will resume it and append measurements to it. If you are giving - a new filename, it will create the file and append to it until you close Hooke. - - - Useful variables (to set with SET command): - --- - fit_function = type of function to use for elasticity. If "wlc" worm-like chain is used, if "fjc" freely jointed - chain is used - - temperature= temperature of the system for wlc/fjc fit (in K) - - auto_slope_span = number of points on which measure the slope, for slope - - auto_fit_nm = number of nm to fit before the peak maximum, for WLC/FJC (if usepoints false) - auto_fit_points = number of points to fit before the peak maximum, for WLC/FJC (if usepoints true) - - baseline_clicks = contact point: no baseline, f=0 at the contact point (whether hand-picked or automatically found) - automatic: automatic baseline - 1 point: decide baseline with a single click and length defined in auto_left_baseline - 2 points: let user click points of baseline - auto_left_baseline = length in nm to use as baseline from the right point (if baseline_clicks = automatic , 1 point) - auto_right_baseline = distance in nm of peak-most baseline point from last peak (if baseline_clicks = automatic) - - auto_min_p ; auto_max_p = Minimum and maximum persistence length (if using WLC) or Kuhn length (if using FJC) - outside of which the peak is automatically discarded (in nm) + Configuration + ------------- + apply_plotmanipulators: + - all (all selected plotmanipulators will be applied) + - flatten (only the flatten plotmanipulator will be applied) + - none (no plotmanipulators will be applied) + fit_function: type of function to use for elasticity. If "wlc" worm-like chain is used, if "fjc" freely jointed + chain is used + + temperature: temperature of the system for WLC/FJC fit (in K) + + auto_slope_span: number of points on which measure the slope, for slope + + auto_fit_nm: number of nm to fit before the peak maximum, for WLC/FJC (if usepoints false) + auto_fit_points: number of points to fit before the peak maximum, for WLC/FJC (if usepoints true) + + baseline_clicks: + contact point: no baseline, f=0 at the contact point (whether hand-picked or automatically found) + automatic: automatic baseline + 1 point: decide baseline with a single click and length defined in auto_left_baseline + 2 points: let user click points of baseline + auto_left_baseline: length in nm to use as baseline from the right point (if baseline_clicks = automatic , 1 point) + auto_right_baseline: distance in nm of peak-most baseline point from last peak (if baseline_clicks = automatic) + + auto_max_p: Maximum persistence length (if using WLC) or Kuhn length (if using FJC) + outside of which the peak is automatically discarded (in nm) + auto_min_p: Minimum persistence length (if using WLC) or Kuhn length (if using FJC) + outside of which the peak is automatically discarded (in nm) ''' - #default fit etc. variables + #default variables auto_fit_nm = self.GetFloatFromConfig('autopeak', 'auto_fit_nm') auto_left_baseline = self.GetFloatFromConfig('autopeak', 'auto_left_baseline') auto_max_p = self.GetFloatFromConfig('autopeak', 'auto_max_p') auto_min_p = self.GetFloatFromConfig('autopeak', 'auto_min_p') auto_right_baseline = self.GetFloatFromConfig('autopeak', 'auto_right_baseline') baseline_clicks = self.GetStringFromConfig('autopeak', 'baseline_clicks') + color = self.GetColorFromConfig('autopeak', 'color') fit_function = self.GetStringFromConfig('autopeak', 'fit_function') fit_points = self.GetIntFromConfig('autopeak', 'auto_fit_points') noauto = self.GetBoolFromConfig('autopeak', 'noauto') - #noflatten: if true we do not flatten the curve - noflatten = self.GetBoolFromConfig('autopeak', 'noflatten') - peak_show = self.GetBoolFromConfig('autopeak', 'peak_show') persistence_length = self.GetFloatFromConfig('autopeak', 'persistence_length') #rebase: redefine the baseline rebase = self.GetBoolFromConfig('autopeak', 'rebase') @@ -154,24 +151,19 @@ class autopeakCommands: forces = [] slopes = [] - #pick up plot + #pick up plot and filename if plot is None: - plot = copy.deepcopy(self.GetActivePlot()) + plot = self.GetDisplayedPlotCorrected() filename = self.GetActiveFile().name - #apply all active plotmanipulators and add the 'manipulated' data - for plotmanipulator in self.plotmanipulators: - if self.GetBoolFromConfig('core', 'plotmanipulators', plotmanipulator.name): - if plotmanipulator.name == 'flatten': - if not noflatten: - plot = plotmanipulator.method(plot, self.GetActiveFile()) - else: - plot = plotmanipulator.method(plot, self.GetActiveFile()) - + TODO: add convfilt option? #--Using points instead of nm interval if not usepoints: fit_points = None + extension = plot.curves[lh.EXTENSION] + retraction = plot.curves[lh.RETRACTION] + #--Contact point arguments if reclick: contact_point, contact_point_index = self.pickup_contact_point(filename=filename) @@ -184,10 +176,10 @@ class autopeakCommands: else: #Automatically find contact point cindex = self.find_contact_point(plot) - contact_point = self._clickize(plot.curves[lh.RETRACTION].x, plot.curves[lh.EXTENSION].y, cindex) + contact_point = self._clickize(retraction.x, extension.y, cindex) #peak_size comes from convolution curve - peak_location, peak_size = self.find_current_peaks(plot=plot, noflatten=noflatten) + peak_location, peak_size = self.has_peaks(plot) if len(peak_location) == 0: self.AppendToOutput('No peaks to fit.') @@ -196,39 +188,37 @@ class autopeakCommands: #Pick up force baseline if baseline_clicks == 'contact point': try: - avg = plot.curves[lh.RETRACTION].y[contact_point_index] + avg = retraction.y[contact_point_index] except: - avg = plot.curves[lh.RETRACTION].y[cindex] + avg = retraction.y[cindex] if rebase or (self.basecurrent != filename) or self.basepoints is None: if baseline_clicks == 'automatic': self.basepoints = [] - base_index_0 = peak_location[-1] + self.fit_interval_nm(peak_location[-1], plot.curves[lh.RETRACTION].x, auto_right_baseline, False) - self.basepoints.append(self._clickize(plot.curves[lh.RETRACTION].x, plot.curves[lh.RETRACTION].y, base_index_0)) - base_index_1 = self.basepoints[0].index + self.fit_interval_nm(self.basepoints[0].index, plot.curves[lh.RETRACTION].x, auto_left_baseline, False) - self.basepoints.append(self._clickize(plot.curves[lh.RETRACTION].x, plot.curves[lh.RETRACTION].y, base_index_1)) + base_index_0 = peak_location[-1] + self.fit_interval_nm(peak_location[-1], retraction.x, auto_right_baseline, False) + self.basepoints.append(self._clickize(retraction.x, retraction.y, base_index_0)) + base_index_1 = self.basepoints[0].index + self.fit_interval_nm(self.basepoints[0].index, retraction.x, auto_left_baseline, False) + self.basepoints.append(self._clickize(retraction.x, retraction.y, base_index_1)) if baseline_clicks == '1 point': self.basepoints=self._measure_N_points(N=1, message='Click on 1 point to select the baseline.', whatset=whatset) - base_index_1 = self.basepoints[0].index + self.fit_interval_nm(self.basepoints[0].index, plot.curves[lh.RETRACTION].x, auto_left_baseline, False) - self.basepoints.append(self._clickize(plot.curves[lh.RETRACTION].x, plot.curves[lh.RETRACTION].y, base_index_1)) + base_index_1 = self.basepoints[0].index + self.fit_interval_nm(self.basepoints[0].index, retraction.x, auto_left_baseline, False) + self.basepoints.append(self._clickize(retraction.x, retraction.y, base_index_1)) if baseline_clicks == '2 points': self.basepoints=self._measure_N_points(N=2, message='Click on 2 points to select the baseline.', whatset=whatset) if baseline_clicks != 'contact point': boundaries=[self.basepoints[0].index, self.basepoints[1].index] boundaries.sort() - to_average = plot.curves[lh.RETRACTION].y[boundaries[0]:boundaries[1]] #y points to average + to_average = retraction.y[boundaries[0]:boundaries[1]] #y points to average avg = mean(to_average) self.basecurrent = filename - x_values = plot.curves[lh.RETRACTION].x - y_values = plot.curves[lh.RETRACTION].y for index, peak in enumerate(peak_location): #WLC FITTING #define fit interval if not usepoints: - fit_points = self.fit_interval_nm(peak, plot.curves[lh.RETRACTION].x, auto_fit_nm, True) - peak_point = self._clickize(x_values, y_values, peak) - other_fit_point=self._clickize(x_values, y_values, peak - fit_points) + fit_points = self.fit_interval_nm(peak, retraction.x, auto_fit_nm, True) + peak_point = self._clickize(retraction.x, retraction.y, peak) + other_fit_point=self._clickize(retraction.x, retraction.y, peak - fit_points) #points for the fit points = [contact_point, peak_point, other_fit_point] @@ -237,18 +227,20 @@ class autopeakCommands: continue if fit_function == 'wlc': - params, yfit, xfit, fit_errors = self.wlc_fit(points, x_values, y_values, pl_value, T, return_errors=True) + params, yfit, xfit, fit_errors = self.wlc_fit(points, retraction.x, retraction.y, pl_value, T, return_errors=True) elif fit_function == 'fjc': - params, yfit, xfit, fit_errors = self.fjc_fit(points, x_values, y_values, pl_value, T, return_errors=True) + params, yfit, xfit, fit_errors = self.fjc_fit(points, retraction.x, retraction.y, pl_value, T, return_errors=True) elif fit_function == 'fjcPEG': - params, yfit, xfit, fit_errors = self.fjcPEG_fit(points, x_values, y_values, pl_value, T, return_errors=True) + params, yfit, xfit, fit_errors = self.fjcPEG_fit(points, retraction.x, retraction.y, pl_value, T, return_errors=True) + + self.results_str = fit_function #Measure forces - delta_to_measure = y_values[peak - delta_force:peak + delta_force] + delta_to_measure = retraction.y[peak - delta_force:peak + delta_force] y = min(delta_to_measure) #save force values (pN) #Measure slopes - slope = self.linefit_between(peak - slope_span, peak, whatset=lh.RETRACTION)[0] + slope = self.linefit_between(peak - slope_span, peak, whatset=whatset)[0] #check fitted data and, if right, add peak to the measurement fit_result = lib.results.Result() @@ -289,10 +281,11 @@ class autopeakCommands: fit_result.result = {} if len(fit_result.result) > 0: + fit_result.color = color fit_result.label = fit_function + '_' + str(index) - fit_result.title = plot.curves[lh.RETRACTION].title - fit_result.units.x = plot.curves[lh.RETRACTION].units.x - fit_result.units.y = plot.curves[lh.RETRACTION].units.y + fit_result.title = retraction.title + fit_result.units.x = retraction.units.x + fit_result.units.y = retraction.units.y fit_result.visible = True fit_result.x = xfit fit_result.y = yfit @@ -302,57 +295,9 @@ class autopeakCommands: fit_results.set_multipliers(0) plot = self.GetActivePlot() plot.results[fit_function] = fit_results - if peak_show: - plugin = lib.plugin.Plugin() - plugin.name = 'autopeak' - plugin.section = 'autopeak' - plugin.prefix = 'peak_' - self.do_peaks(plugin=plugin, peak_location=peak_location, peak_size=peak_size) - else: - self.UpdatePlot() - + self.UpdatePlot() else: self.AppendToOutput('No peaks found.') #TODO: #self.do_note('autopeak') - - def find_current_peaks(self, plot=None, noflatten=True): - if not noflatten: - plot_temp = self.plotmanip_flatten(plot, self.GetActiveFile(), customvalue=1) - peak_location, peak_size = self.has_peaks(plot_temp) - return peak_location, peak_size - - def fit_interval_nm(self, start_index, x_vect, nm, backwards): - ''' - Calculates the number of points to fit, given a fit interval in nm - start_index: index of point - plot: plot to use - backwards: if true, finds a point backwards. - ''' - - c = 0 - i = start_index - maxlen=len(x_vect) - while abs(x_vect[i] - x_vect[start_index]) * (10**9) < nm: - if i == 0 or i == maxlen-1: #we reached boundaries of vector! - return c - if backwards: - i -= 1 - else: - i += 1 - c += 1 - return c - - def pickup_contact_point(self, filename=''): - ''' - macro to pick up the contact point by clicking - ''' - contact_point = self._measure_N_points(N=1, message='Please click on the contact point.')[0] - contact_point_index = contact_point.index - self.wlccontact_point = contact_point - self.wlccontact_index = contact_point.index - self.wlccurrent = filename - return contact_point, contact_point_index - - diff --git a/plugins/export.ini b/plugins/export.ini index da8db68..a46f0bb 100644 --- a/plugins/export.ini +++ b/plugins/export.ini @@ -47,6 +47,11 @@ value = _ [results] + [[append]] + default = True + type = boolean + value = True + [[filename]] default = fit export.txt type = filename @@ -56,5 +61,3 @@ default = "," type = string value = ", " - - diff --git a/plugins/export.py b/plugins/export.py index a111c0d..171f943 100644 --- a/plugins/export.py +++ b/plugins/export.py @@ -27,14 +27,9 @@ class exportCommands(object): def _plug_init(self): pass - def do_fits(self):#, ext='', folder='', prefix='', separator=''): + def do_fits(self): ''' - Exports all approach and retraction files in a playlist and - all fitting results (if available) in a columnar ASCII format. - Please make sure that the number of points in the fit is smaller - or equal to the number of points in the approach/retraction. - For the time being, exports only one set of results (e.g. WLC - or FJC, not both). + Exports all fitting results (if available) in a columnar ASCII format. ''' ext = self.GetStringFromConfig('export', 'fits', 'ext') @@ -47,7 +42,6 @@ class exportCommands(object): active_file = self.GetActiveFile() plot = self.GetDisplayedPlot() - #TODO: fix for multiple results #add empty columns before adding new results if necessary if plot is not None: for results_str, results in plot.results.items(): @@ -88,18 +82,22 @@ class exportCommands(object): #add string for Other active_file = self.GetActiveFile() - plot = self.GetActivePlot() - extension = plot.curves[lh.EXTENSION] - retraction = plot.curves[lh.RETRACTION] + #create the header from the raw plot (i.e. only the force curve) + plot = self.GetDisplayedPlotRaw() output = [] header_str = '' for index, curve in enumerate(plot.curves): + #TODO: only add labels for original curves (i.e. excluding anything added after the fact) header_str += curve.label + '_x (' + curve.units.x + ')' + separator + curve.label + '_y (' + curve.units.y + ')' if index < len(plot.curves) - 1: header_str += separator output.append(header_str) - #TODO: add units + + #export the displayed plot + plot = self.GetDisplayedPlot() + extension = plot.curves[lh.EXTENSION] + retraction = plot.curves[lh.RETRACTION] for index, row in enumerate(extension.x): output.append(separator.join([str(extension.x[index]), str(extension.y[index]), str(retraction.x[index]), str(retraction.y[index])])) @@ -171,11 +169,15 @@ class exportCommands(object): output_file.close progress_dialog.Destroy() - def do_results(self, filename='', separator=''): + def do_results(self, append=None, filename='', separator=''): ''' - EXPORTFITS Exports all visible fit results in a playlist into a delimited text file + append: set append to True if you want to append to an existing results file + filename: the filename and path of the results file + separator: the separator between columns ''' + if not append: + append = self.GetStringFromConfig('export', 'results', 'append') if filename == '': filename = self.GetStringFromConfig('export', 'results', 'filename') if separator == '': @@ -203,7 +205,11 @@ class exportCommands(object): output_str = ''.join([output_str, line_str, '\n']) if output_str != '': output_str = ''.join(['Analysis started ', time.asctime(), '\n', output_str]) - output_file = open(filename, 'w') + + if append and os.path.isfile(filename): + output_file = open(filename,'a') + else: + output_file = open(filename, 'w') output_file.write(output_str) output_file.close else: diff --git a/plugins/flatfilts.ini b/plugins/flatfilts.ini index 3a6f604..3a0168b 100644 --- a/plugins/flatfilts.ini +++ b/plugins/flatfilts.ini @@ -1,4 +1,10 @@ [convfilt] + [[apply_plotmanipulators]] + default = all + elements = all, flatten, none + type = enum + value = all + [[blindwindow]] default = 20 maximum = 10000 @@ -86,7 +92,7 @@ [[color]] default = black type = color - value = "(255,0,128)" + value = "(128,128,128)" [[size]] default = 20 diff --git a/plugins/flatfilts.py b/plugins/flatfilts.py index 9ce8a93..d1814e1 100644 --- a/plugins/flatfilts.py +++ b/plugins/flatfilts.py @@ -129,35 +129,46 @@ class flatfiltsCommands: #-----Convolution-based peak recognition and filtering. #Requires the peakspot.py library - def has_peaks(self, plot=None): + def has_peaks(self, plot=None, plugin=None): ''' Finds peak position in a force curve. FIXME: should be moved to peakspot.py + #TODO: should this really be moved? this is obviously tied into flatfilts/convfilt + #flatfilts.py is where 'has_peaks' belongs ''' - blindwindow = self.GetFloatFromConfig('flatfilts', 'convfilt', 'blindwindow') - #need to convert the string that contains the list into a list - convolution = eval(self.GetStringFromConfig('flatfilts', 'convfilt', 'convolution')) - maxcut = self.GetFloatFromConfig('flatfilts', 'convfilt', 'maxcut') - mindeviation = self.GetFloatFromConfig('flatfilts', 'convfilt', 'mindeviation') - positive = self.GetBoolFromConfig('flatfilts', 'convfilt', 'positive') - seedouble = self.GetIntFromConfig('flatfilts', 'convfilt', 'seedouble') - stable = self.GetFloatFromConfig('flatfilts', 'convfilt', 'stable') + if plugin is None: + blindwindow = self.GetFloatFromConfig('flatfilts', 'convfilt', 'blindwindow') + #need to convert the string that contains the list into a list + convolution = eval(self.GetStringFromConfig('flatfilts', 'convfilt', 'convolution')) + maxcut = self.GetFloatFromConfig('flatfilts', 'convfilt', 'maxcut') + mindeviation = self.GetFloatFromConfig('flatfilts', 'convfilt', 'mindeviation') + positive = self.GetBoolFromConfig('flatfilts', 'convfilt', 'positive') + seedouble = self.GetIntFromConfig('flatfilts', 'convfilt', 'seedouble') + stable = self.GetFloatFromConfig('flatfilts', 'convfilt', 'stable') + else: + blindwindow = self.GetFloatFromConfig(plugin.name, plugin.section, plugin.prefix + 'blindwindow') + #need to convert the string that contains the list into a list + convolution = eval(self.GetStringFromConfig(plugin.name, plugin.section, plugin.prefix + 'convolution')) + maxcut = self.GetFloatFromConfig(plugin.name, plugin.section, plugin.prefix + 'maxcut') + mindeviation = self.GetFloatFromConfig(plugin.name, plugin.section, plugin.prefix + 'mindeviation') + positive = self.GetBoolFromConfig(plugin.name, plugin.section, plugin.prefix + 'positive') + seedouble = self.GetIntFromConfig(plugin.name, plugin.section, plugin.prefix + 'seedouble') + stable = self.GetFloatFromConfig(plugin.name, plugin.section, plugin.prefix + 'stable') if plot is None: - plot = self.GetActivePlot() + plot = self.GetDisplayedPlotCorrected() - xret = plot.curves[lh.RETRACTION].x - yret = plot.curves[lh.RETRACTION].y + retraction = plot.curves[lh.RETRACTION] #Calculate convolution - convoluted = lps.conv_dx(yret, convolution) + convoluted = lps.conv_dx(retraction.y, convolution) #surely cut everything before the contact point cut_index = self.find_contact_point(plot) #cut even more, before the blind window - start_x = xret[cut_index] + start_x = retraction.x[cut_index] blind_index = 0 - for value in xret[cut_index:]: + for value in retraction.x[cut_index:]: if abs((value) - (start_x)) > blindwindow * (10 ** -9): break blind_index += 1 @@ -169,32 +180,15 @@ class flatfiltsCommands: #take the maximum for i in range(len(peak_location)): peak = peak_location[i] - maxpk = min(yret[peak - 10:peak + 10]) - index_maxpk = yret[peak - 10:peak + 10].index(maxpk) + (peak - 10) + maxpk = min(retraction.y[peak - 10:peak + 10]) + index_maxpk = retraction.y[peak - 10:peak + 10].index(maxpk) + (peak - 10) peak_location[i] = index_maxpk return peak_location, peak_size - def exec_has_peaks(self, item): - ''' - encapsulates has_peaks for the purpose of correctly treating the curve objects in the convfilt loop, - to avoid memory leaks - ''' - #TODO: things have changed, check for the memory leak again - - if self.HasPlotmanipulator('plotmanip_flatten'): - #If flatten is present, use it for better recognition of peaks... - flat_plot = self.plotmanip_flatten(item.plot, item, customvalue=1) - - peak_location, peak_size = self.has_peaks(flat_plot) - - return peak_location, peak_size - def do_peaks(self, plugin=None, peak_location=None, peak_size=None): ''' - PEAKS - (flatfilts.py) - Test command for convolution filter / test. + Test command for convolution filter. ---- Syntax: peaks [deviations] absolute deviation = number of times the convolution signal is above the noise absolute deviation. @@ -222,17 +216,15 @@ class flatfiltsCommands: self.AppendToOutput('Found ' + str(len(peak_location)) + peak_str) if peak_location: - xplotted_ret = plot.curves[lh.RETRACTION].x - yplotted_ret = plot.curves[lh.RETRACTION].y - xgood = [xplotted_ret[index] for index in peak_location] - ygood = [yplotted_ret[index] for index in peak_location] + retraction = plot.curves[lh.RETRACTION] peaks = lib.curve.Curve() peaks.color = color peaks.size = size peaks.style = 'scatter' - peaks.x = xgood - peaks.y = ygood + peaks.title = 'Peaks' + peaks.x = [retraction.x[index] for index in peak_location] + peaks.y = [retraction.y[index] for index in peak_location] plot.curves.append(peaks) @@ -240,26 +232,17 @@ class flatfiltsCommands: def do_convfilt(self): ''' - CONVFILT - (flatfilts.py) Filters out flat (featureless) files of the current playlist, creating a playlist containing only the files with potential features. ------------ - Syntax: - convfilt [min_npks min_deviation] - - min_npks = minmum number of peaks - (to set the default, see convfilt.conf file; CONVCONF and SETCONF commands) - - min_deviation = minimum signal/noise ratio *in the convolution* - (to set the default, see convfilt.conf file; CONVCONF and SETCONF commands) - - If called without arguments, it uses default values. + min_npks: minmum number of peaks + min_deviation: minimum signal/noise ratio *in the convolution* ''' self.AppendToOutput('Processing playlist...') self.AppendToOutput('(Please wait)') + apply_plotmanipulators = self.GetStringFromConfig('flatfilts', 'convfilt', 'apply_plotmanipulators') minpeaks = self.GetIntFromConfig('flatfilts', 'convfilt', 'minpeaks') features = [] playlist = self.GetActivePlaylist() @@ -271,7 +254,8 @@ class flatfiltsCommands: file_index += 1 try: current_file.identify(self.drivers) - peak_location, peak_size = self.exec_has_peaks(copy.deepcopy(current_file)) + plot = self.ApplyPlotmanipulators(current_file.plot, current_file) + peak_location, peak_size = self.has_peaks(plot) number_of_peaks = len(peak_location) if number_of_peaks != 1: if number_of_peaks > 0: @@ -293,10 +277,7 @@ class flatfiltsCommands: current_file.peak_size = peak_size features.append(file_index - 1) - #Warn that no flattening had been done. - if not self.HasPlotmanipulator('plotmanip_flatten'): - self.AppendToOutput('Flatten manipulator was not found. Processing was done without flattening.') - self.AppendToOutput('Try to enable it in the configuration file for better results.') + #TODO: warn when flatten is not applied? if not features: self.AppendToOutput('Found nothing interesting. Check the playlist, could be a bug or criteria could be too stringent.') else: diff --git a/plugins/generalvclamp.ini b/plugins/generalvclamp.ini index 3d930e9..5968d03 100644 --- a/plugins/generalvclamp.ini +++ b/plugins/generalvclamp.ini @@ -1,9 +1,21 @@ [distance] - [[show_points]] + [[color]] + default = black + type = color + value = "(128,0,128)" + + [[show]] default = True type = boolean value = True + [[size]] + default = 20 + maximum = 10000 + minimum = 1 + type = integer + value = 100 + [[whatset]] default = retraction elements = extension, retraction @@ -11,6 +23,23 @@ value = retraction [force] + [[color]] + default = black + type = color + value = "(0,255,0)" + + [[show]] + default = True + type = boolean + value = False + + [[size]] + default = 20 + maximum = 10000 + minimum = 1 + type = integer + value = 100 + [[whatset]] default = retraction elements = extension, retraction @@ -18,6 +47,15 @@ value = retraction [forcebase] + [[baseline]] + type = none + value = 1 + + [[color]] + default = black + type = color + value = "(255,0,128)" + [[max]] default = False type = boolean @@ -28,15 +66,23 @@ type = boolean value = False + [[show]] + default = True + type = boolean + value = True + + [[size]] + default = 20 + maximum = 10000 + minimum = 1 + type = integer + value = 50 + [[whatset]] default = retraction elements = extension, retraction type = enum value = retraction - - [[baseline]] - type = none - value = 1 [generalvclamp] [[flatten]] @@ -65,3 +111,43 @@ minimum = 0 type = integer value = 60 + + [[point_color]] + default = black + type = color + value = "(0,255,0)" + + [[point_show]] + default = False + type = boolean + value = False + + [[point_size]] + default = 20 + maximum = 10000 + minimum = 1 + type = integer + value = 10 + + [[slope_color]] + default = black + type = color + value = "(255,0,128)" + + [[slope_linewidth]] + default = 1 + maximum = 10000 + minimum = 1 + type = integer + value = 2 + + [[slope_show]] + default = True + type = boolean + value = True + + [[whatset]] + default = retraction + elements = extension, retraction + type = enum + value = retraction diff --git a/plugins/generalvclamp.py b/plugins/generalvclamp.py index 9f97136..9425880 100644 --- a/plugins/generalvclamp.py +++ b/plugins/generalvclamp.py @@ -15,6 +15,7 @@ import lib.libhooke as lh import wxversion wxversion.select(lh.WX_GOOD) +from copy import deepcopy import numpy as np import scipy as sp @@ -42,20 +43,15 @@ class generalvclampCommands: ----------------- Syntax: distance ''' - show_points = self.GetBoolFromConfig('generalvclamp', 'distance', 'show_points') - whatset_str = self.GetStringFromConfig('generalvclamp', 'distance', 'whatset') - whatset = 'retraction' - if whatset_str == 'extension': - whatset = lh.EXTENSION - if whatset_str == 'retraction': - whatset = lh.RETRACTION - active_file = self.GetActiveFile() plot = self.GetActivePlot() if active_file.driver.experiment == 'clamp': self.AppendToOutput('You wanted to use zpiezo perhaps?') return - dx, unitx, dy, unity = self._delta(message='Click 2 points to measure the distance.', show=show_points, whatset=whatset) + plugin = lib.plugin.Plugin() + plugin.name = 'generalvclamp' + plugin.section = 'distance' + dx, unitx, dy, unity = self._delta(message='Click 2 points to measure the distance.', plugin=plugin) #TODO: pretty format self.AppendToOutput(str(dx * (10 ** 9)) + ' nm') @@ -67,19 +63,15 @@ class generalvclampCommands: --------------- Syntax: force ''' - whatset_str = self.GetStringFromConfig('generalvclamp', 'force', 'whatset') - whatset = 'retraction' - if whatset_str == 'extension': - whatset = lh.EXTENSION - if whatset_str == 'retraction': - whatset = lh.RETRACTION - active_file = self.GetActiveFile() plot = self.GetActivePlot() if active_file.driver.experiment == 'clamp': self.AppendToOutput('This command makes no sense for a force clamp experiment.') return - dx, unitx, dy, unity = self._delta(whatset=whatset, message='Click 2 points to measure the force.') + plugin = lib.plugin.Plugin() + plugin.name = 'generalvclamp' + plugin.section = 'force' + dx, unitx, dy, unity = self._delta(message='Click 2 points to measure the force.', plugin=plugin) #TODO: pretty format self.AppendToOutput(str(dy * (10 ** 12)) + ' pN') @@ -98,33 +90,48 @@ class generalvclampCommands: max: Instead of asking for a point to measure, asks for two points and use the maximum peak in between ''' + color = self.GetColorFromConfig('generalvclamp', 'forcebase', 'color') maxpoint = self.GetBoolFromConfig('generalvclamp', 'forcebase', 'max') rebase = self.GetBoolFromConfig('generalvclamp', 'forcebase', 'rebase') + show_points = self.GetBoolFromConfig('generalvclamp', 'forcebase', 'show_points') + size = self.GetIntFromConfig('generalvclamp', 'forcebase', 'size') whatset_str = self.GetStringFromConfig('generalvclamp', 'forcebase', 'whatset') whatset = 'retraction' if whatset_str == 'extension': whatset = lh.EXTENSION if whatset_str == 'retraction': whatset = lh.RETRACTION + plot = self.GetDisplayedPlotCorrected() + clicked_points = [] + filename = self.GetActiveFile().name if rebase or (self.basecurrent != filename): self.basepoints = self._measure_N_points(N=2, message='Click on 2 points to select the baseline.', whatset=whatset) self.basecurrent = filename + clicked_points = self.basepoints + #TODO: maxpoint does not seem to be picking up the 'real' minimum (at least not with test.hkp/default.000) if maxpoint: boundpoints = [] points = self._measure_N_points(N=2, message='Click 2 points to select the range for maximum detection.', whatset=whatset) boundpoints = [points[0].index, points[1].index] boundpoints.sort() + clicked_points += points try: - y = min(plot.curves[whatset].y[boundpoints[0]:boundpoints[1]]) + vector_x = plot.curves[whatset].x[boundpoints[0]:boundpoints[1]] + vector_y = plot.curves[whatset].y[boundpoints[0]:boundpoints[1]] + y = min(vector_y) + index = vector_y.index(y) + clicked_points += [self._clickize(vector_x, vector_y, index)] except ValueError: self.AppendToOutput('Chosen interval not valid. Try picking it again. Did you pick the same point as begin and end of the interval?') + return else: points = self._measure_N_points(N=1, message='Click on the point to measure.', whatset=whatset) y = points[0].graph_coords[1] + clicked_points += [points[0]] boundaries = [self.basepoints[0].index, self.basepoints[1].index] boundaries.sort() @@ -132,12 +139,26 @@ class generalvclampCommands: avg = np.mean(to_average) forcebase = abs(y - avg) + + if show_points: + curve = plot.curves[whatset] + for point in clicked_points: + points = deepcopy(curve) + points.x = point.graph_coords[0] + points.y = point.graph_coords[1] + + points.color = color + points.size = size + points.style = 'scatter' + plot.curves.append(points) + + self.UpdatePlot(plot) #TODO: pretty format self.AppendToOutput(str(forcebase * (10 ** 12)) + ' pN') - + def plotmanip_multiplier(self, plot, current, customvalue=False): ''' - Multiplies all the Y values of an SMFS curve by a value stored in the 'force_multiplier' + Multiplies all the Y values of an SMFS curve by a value stored in the 'force_multiplier' configuration variable. Useful for calibrations and other stuff. ''' @@ -147,12 +168,12 @@ class generalvclampCommands: force_multiplier = self.GetFloatFromConfig('generalvclamp', 'force_multiplier') if force_multiplier == 1: - return plot + return plot plot.curves[lh.EXTENSION].y = [element * force_multiplier for element in plot.curves[lh.EXTENSION].y] plot.curves[lh.RETRACTION].y = [element * force_multiplier for element in plot.curves[lh.RETRACTION].y] - - return plot + + return plot def plotmanip_flatten(self, plot, current, customvalue=0): ''' @@ -237,24 +258,36 @@ class generalvclampCommands: ''' fitspan = self.GetIntFromConfig('generalvclamp', 'slope', 'fitspan') + point_color = self.GetColorFromConfig('generalvclamp', 'slope', 'point_color') + point_show = self.GetBoolFromConfig('generalvclamp', 'slope', 'point_show') + point_size = self.GetIntFromConfig('generalvclamp', 'slope', 'point_size') + slope_color = self.GetColorFromConfig('generalvclamp', 'slope', 'slope_color') + slope_linewidth = self.GetIntFromConfig('generalvclamp', 'slope', 'slope_linewidth') + slope_show = self.GetBoolFromConfig('generalvclamp', 'slope', 'slope_show') + whatset_str = self.GetStringFromConfig('generalvclamp', 'forcebase', 'whatset') + whatset = 'retraction' + if whatset_str == 'extension': + whatset = lh.EXTENSION + if whatset_str == 'retraction': + whatset = lh.RETRACTION + # Decides between the two forms of user input #TODO: add an option 'mode' with options 'chunk' and 'point' - #TODO: add 'whatset' as option, too if fitspan == 0: # Gets the Xs of two clicked points as indexes on the curve curve vector clickedpoints = [] - points = self._measure_N_points(N=2, message='Click 2 points to select the chunk.', whatset=1) + points = self._measure_N_points(N=2, message='Click 2 points to select the chunk.', whatset=whatset) clickedpoints = [points[0].index, points[1].index] clickedpoints.sort() else: clickedpoints = [] - points = self._measure_N_points(N=1, message='Click on the leftmost point of the chunk (i.e.usually the peak).', whatset=1) + points = self._measure_N_points(N=1, message='Click on the leftmost point of the chunk (i.e.usually the peak).', whatset=whatset) clickedpoints = [points[0].index - fitspan, points[0].index] # Calls the function linefit_between parameters = [0, 0, [], []] try: - parameters = self.linefit_between(clickedpoints[0], clickedpoints[1]) + parameters = self.linefit_between(clickedpoints[0], clickedpoints[1], whatset=whatset) except: self.AppendToOutput('Cannot fit. Did you click the same point twice?') return @@ -279,25 +312,27 @@ class generalvclampCommands: clickvector_x.append(item.graph_coords[0]) clickvector_y.append(item.graph_coords[1]) - #add the slope to the plot - slope = lib.curve.Curve() - slope.color = 'red' - slope.label = 'slope' - slope.style = 'plot' - slope.x = xtoplot - slope.y = ytoplot - - #add the clicked points to the plot - points = lib.curve.Curve() - points.color = 'black' - points.label = 'points' - points.size = 10 - points.style = 'scatter' - points.x = clickvector_x - points.y = clickvector_y - - plot.curves.append(slope) - plot.curves.append(points) + if slope_show: + #add the slope to the plot + slope = lib.curve.Curve() + slope.color = slope_color + slope.label = 'slope' + slope.linewidth = slope_linewidth + slope.style = 'plot' + slope.x = xtoplot + slope.y = ytoplot + plot.curves.append(slope) + + if point_show: + #add the clicked points to the plot + points = lib.curve.Curve() + points.color = point_color + points.label = 'points' + points.size = point_size + points.style = 'scatter' + points.x = clickvector_x + points.y = clickvector_y + plot.curves.append(points) self.UpdatePlot(plot) diff --git a/plugins/procplots.ini b/plugins/procplots.ini index 842abb9..90c11e1 100644 --- a/plugins/procplots.ini +++ b/plugins/procplots.ini @@ -77,14 +77,19 @@ value = both [procplots] + [[centerzero]] + default = True + type = boolean + value = True + + [[correct]] + default = True + type = boolean + value = True + [[median]] default = 0 maximum = 100 minimum = 0 type = integer value = 7 - - [[correct]] - default = True - type = boolean - value = True diff --git a/plugins/procplots.py b/plugins/procplots.py index b7dd12c..748c96c 100644 --- a/plugins/procplots.py +++ b/plugins/procplots.py @@ -72,11 +72,7 @@ class procplotsCommands: def do_derivplot(self): ''' - DERIVPLOT - (procplots.py plugin) - Plots the discrete differentiation of the currently displayed force curve retraction - --------- - Syntax: derivplot + Plots the discrete differentiation of the currently displayed force curve. ''' column = self.GetIntFromConfig('procplots', 'derivplot', 'column') row = self.GetIntFromConfig('procplots', 'derivplot', 'row') @@ -107,6 +103,12 @@ class procplotsCommands: self.UpdatePlot(plot) + def do_replot(self): + ''' + Replots the current force curve from scratch eliminating any secondary plots. + ''' + self.UpdatePlot() + def do_subtplot(self): ''' SUBTPLOT @@ -198,7 +200,7 @@ class procplotsCommands: #use only for force spectroscopy experiments! if current.driver.experiment != 'smfs': return plot - + if not customvalue: customvalue = self.GetBoolFromConfig('procplots', 'centerzero') if not customvalue: @@ -206,16 +208,14 @@ class procplotsCommands: levelapp = float(median(plot.curves[lh.EXTENSION].y)) levelret = float(median(plot.curves[lh.RETRACTION].y)) - - level = (levelapp + levelret)/2 - + + level = (levelapp + levelret)/2 + plot.curves[lh.EXTENSION].y = [i-level for i in plot.curves[lh.EXTENSION].y] plot.curves[lh.RETRACTION].y = [i-level for i in plot.curves[lh.RETRACTION].y] - -# plot.vectors[0][1]=approach -# plot.vectors[1][1]=retract + return plot - + #FFT--------------------------- def fft_plot(self, curve, boundaries=[0, -1]): ''' diff --git a/plugins/results.py b/plugins/results.py index 10851bc..e7b67ce 100644 --- a/plugins/results.py +++ b/plugins/results.py @@ -30,6 +30,7 @@ class resultsCommands(object): def do_show_results(self): ''' - Select which fitting results should be displayed on the plot. + Select which type of result should be displayed on the plot. ''' + self.results_str = self.GetStringFromConfig('results', 'show_results', 'result_type') self.UpdatePlot()