def __init__(self, parent, counter, modelid, active_parms, tau=None): """ Initialize with given parameters. """ wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY) self.parent = parent self.corr = Correlation(fit_model=modelid) if tau is not None: self.corr.lag_time = tau # active_parameters: # [0] labels # [1] values # [2] bool values to fit self.corr.fit_parameters = active_parms[1] self.corr.fit_parameters_variable = active_parms[2] self._bgselected = None self._bg2selected = None self.FitKnots = 5 # number of knots for spline fit or similiars self.weighted_fittype_id = 0 # integer (drop down item) self.weighted_nuvar = 3 # bins for std-dev. (left and rigth) # The weights that are plotted in the page # This is set by the PlotAll function self.weights_plot_fill_area = None # A list containing page numbers that share parameters with this page. # This parameter is defined by the global fitting tool and is saved in # sessions. self.GlobalParameterShare = [] # Counts number of Pages already created: self.counter = counter # Has inital plot been performed? # Call PlotAll("init") to set this to true. If it is true, then # nothing will be plotted if called with "init" self.InitialPlot = False # Model we are using # Tool statistics uses this list: self.StatisticsCheckboxes = None # Splitter window # Sizes size = parent.notebook.GetSize() tabsize = 33 size[1] = size[1] - tabsize self.sizepanelx = 270 canvasx = size[0] - self.sizepanelx + 5 sizepanel = (self.sizepanelx, size[1]) sizecanvas = (canvasx, size[1]) self.sp = wx.SplitterWindow(self, size=size, style=wx.SP_3DSASH) # This is necessary to prevent "Unsplit" of the SplitterWindow: self.sp.SetMinimumPaneSize(1) # Settings Section (left side) #self.panelsettings = wx.Panel(self.sp, size=sizepanel) self.panelsettings = scrolled.ScrolledPanel(self.sp, size=sizepanel) self.panelsettings.SetupScrolling(scroll_x=False) # Setting up Plot (correlation + chi**2) self.spcanvas = wx.SplitterWindow(self.sp, size=sizecanvas, style=wx.SP_3DSASH) # This is necessary to prevent "Unsplit" of the SplitterWindow: self.spcanvas.SetMinimumPaneSize(1) # y difference in pixels between Auocorrelation and Residuals cupsizey = size[1] * 4 / 5 # Calculate initial data self.calculate_corr() # Draw the settings section self.settings() # Load default values self.apply_parameters_reverse() # Upper Plot for plotting of Correlation Function self.canvascorr = plot.PlotCanvas(self.spcanvas) self.canvascorr.logScale = (True, False) self.canvascorr.enableZoom = True self.PlotAll(event="init", trigger="tab_init") self.canvascorr.SetSize((canvasx, cupsizey)) # Lower Plot for plotting of the residuals self.canvaserr = plot.PlotCanvas(self.spcanvas) self.canvaserr.logScale = (True, False) self.canvaserr.enableZoom = True self.canvaserr.SetSize((canvasx, size[1] - cupsizey)) self.spcanvas.SplitHorizontally(self.canvascorr, self.canvaserr, cupsizey) self.sp.SplitVertically(self.panelsettings, self.spcanvas, self.sizepanelx) # Bind resizing to resizing function. wx.EVT_SIZE(self, self.OnSize)
def __init__(self, parent, counter, modelid, active_parms, tau=None): """ Initialize with given parameters. """ wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY) self.parent = parent self.corr = Correlation(fit_model=modelid) if tau is not None: self.corr.lag_time = tau # active_parameters: # [0] labels # [1] values # [2] bool values to fit self.corr.fit_parameters = active_parms[1] self.corr.fit_parameters_variable = active_parms[2] self._bgselected = None self._bg2selected = None self.FitKnots = 5 # number of knots for spline fit or similiars self.weighted_fittype_id = 0 # integer (drop down item) self.weighted_nuvar = 3 # bins for std-dev. (left and rigth) # The weights that are plotted in the page # This is set by the PlotAll function self.weights_plot_fill_area = None # A list containing page numbers that share parameters with this page. # This parameter is defined by the global fitting tool and is saved in # sessions. self.GlobalParameterShare = [] # Counts number of Pages already created: self.counter = counter # Has inital plot been performed? # Call PlotAll("init") to set this to true. If it is true, then # nothing will be plotted if called with "init" self.InitialPlot = False # Model we are using # Tool statistics uses this list: self.StatisticsCheckboxes = None ### Splitter window # Sizes size = parent.notebook.GetSize() tabsize = 33 size[1] = size[1] - tabsize self.sizepanelx = 270 canvasx = size[0]-self.sizepanelx+5 sizepanel = (self.sizepanelx, size[1]) sizecanvas = (canvasx, size[1]) self.sp = wx.SplitterWindow(self, size=size, style=wx.SP_3DSASH) # This is necessary to prevent "Unsplit" of the SplitterWindow: self.sp.SetMinimumPaneSize(1) ## Settings Section (left side) #self.panelsettings = wx.Panel(self.sp, size=sizepanel) self.panelsettings = scrolled.ScrolledPanel(self.sp, size=sizepanel) self.panelsettings.SetupScrolling(scroll_x=False) ## Setting up Plot (correlation + chi**2) self.spcanvas = wx.SplitterWindow(self.sp, size=sizecanvas, style=wx.SP_3DSASH) # This is necessary to prevent "Unsplit" of the SplitterWindow: self.spcanvas.SetMinimumPaneSize(1) # y difference in pixels between Auocorrelation and Residuals cupsizey = size[1]*4/5 # Calculate initial data self.calculate_corr() # Draw the settings section self.settings() # Load default values self.apply_parameters_reverse() # Upper Plot for plotting of Correlation Function self.canvascorr = plot.PlotCanvas(self.spcanvas) self.canvascorr.setLogScale((True, False)) self.canvascorr.SetEnableZoom(True) self.PlotAll(event="init", trigger="tab_init") self.canvascorr.SetSize((canvasx, cupsizey)) # Lower Plot for plotting of the residuals self.canvaserr = plot.PlotCanvas(self.spcanvas) self.canvaserr.setLogScale((True, False)) self.canvaserr.SetEnableZoom(True) self.canvaserr.SetSize((canvasx, size[1]-cupsizey)) self.spcanvas.SplitHorizontally(self.canvascorr, self.canvaserr, cupsizey) self.sp.SplitVertically(self.panelsettings, self.spcanvas, self.sizepanelx) # Bind resizing to resizing function. wx.EVT_SIZE(self, self.OnSize)
class FittingPanel(wx.Panel): """ Those are the Panels that show the fitting dialogs with the Plots. """ def __init__(self, parent, counter, modelid, active_parms, tau=None): """ Initialize with given parameters. """ wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY) self.parent = parent self.corr = Correlation(fit_model=modelid) if tau is not None: self.corr.lag_time = tau # active_parameters: # [0] labels # [1] values # [2] bool values to fit self.corr.fit_parameters = active_parms[1] self.corr.fit_parameters_variable = active_parms[2] self._bgselected = None self._bg2selected = None self.FitKnots = 5 # number of knots for spline fit or similiars self.weighted_fittype_id = 0 # integer (drop down item) self.weighted_nuvar = 3 # bins for std-dev. (left and rigth) # The weights that are plotted in the page # This is set by the PlotAll function self.weights_plot_fill_area = None # A list containing page numbers that share parameters with this page. # This parameter is defined by the global fitting tool and is saved in # sessions. self.GlobalParameterShare = [] # Counts number of Pages already created: self.counter = counter # Has inital plot been performed? # Call PlotAll("init") to set this to true. If it is true, then # nothing will be plotted if called with "init" self.InitialPlot = False # Model we are using # Tool statistics uses this list: self.StatisticsCheckboxes = None # Splitter window # Sizes size = parent.notebook.GetSize() tabsize = 33 size[1] = size[1] - tabsize self.sizepanelx = 270 canvasx = size[0] - self.sizepanelx + 5 sizepanel = (self.sizepanelx, size[1]) sizecanvas = (canvasx, size[1]) self.sp = wx.SplitterWindow(self, size=size, style=wx.SP_3DSASH) # This is necessary to prevent "Unsplit" of the SplitterWindow: self.sp.SetMinimumPaneSize(1) # Settings Section (left side) #self.panelsettings = wx.Panel(self.sp, size=sizepanel) self.panelsettings = scrolled.ScrolledPanel(self.sp, size=sizepanel) self.panelsettings.SetupScrolling(scroll_x=False) # Setting up Plot (correlation + chi**2) self.spcanvas = wx.SplitterWindow(self.sp, size=sizecanvas, style=wx.SP_3DSASH) # This is necessary to prevent "Unsplit" of the SplitterWindow: self.spcanvas.SetMinimumPaneSize(1) # y difference in pixels between Auocorrelation and Residuals cupsizey = size[1] * 4 / 5 # Calculate initial data self.calculate_corr() # Draw the settings section self.settings() # Load default values self.apply_parameters_reverse() # Upper Plot for plotting of Correlation Function self.canvascorr = plot.PlotCanvas(self.spcanvas) self.canvascorr.logScale = (True, False) self.canvascorr.enableZoom = True self.PlotAll(event="init", trigger="tab_init") self.canvascorr.SetSize((canvasx, cupsizey)) # Lower Plot for plotting of the residuals self.canvaserr = plot.PlotCanvas(self.spcanvas) self.canvaserr.logScale = (True, False) self.canvaserr.enableZoom = True self.canvaserr.SetSize((canvasx, size[1] - cupsizey)) self.spcanvas.SplitHorizontally(self.canvascorr, self.canvaserr, cupsizey) self.sp.SplitVertically(self.panelsettings, self.spcanvas, self.sizepanelx) # Bind resizing to resizing function. wx.EVT_SIZE(self, self.OnSize) @property def active_parms(self): names = self.corr.fit_model.parameters[0] parms = self.corr.fit_parameters bools = self.corr.fit_parameters_variable return [names, parms, bools] @property def IsCrossCorrelation(self): return self.corr.is_cc @property def modelid(self): return self.corr.fit_model.id @property def prevent_batch_modification(self): return self.wxCBPreventBatchParms.GetValue() @prevent_batch_modification.setter def prevent_batch_modification(self, value): self.wxCBPreventBatchParms.SetValue(value) @property def title(self): return self.tabtitle.GetValue() @title.setter def title(self, title): self.tabtitle.SetValue(title.strip()) self.corr.title = title.strip() @property def traceavg(self): warnings.warn("Trace average always set to none!") return None @property def tracecc(self): if self.corr.is_cc and len(self.corr.traces) != 0: return self.corr.traces else: return None @property def bgselected(self): return self._bgselected @bgselected.setter def bgselected(self, value): if value is None: self.corr.backgrounds = [] return # check paren.Background and get id background = self.parent.Background[value] self.corr.background_replace(0, background) self._bgselected = value @property def bg2selected(self): return self._bg2selected @bg2selected.setter def bg2selected(self, value): if value is None: if self.corr.is_cc: self.corr.backgrounds = [] return # check paren.Background and get id background = self.parent.Background[value] self.corr.background_replace(1, background) self._bg2selected = value def apply_parameters(self, event=None): """ Read the values from the GUI form and write it to the pages parameters / correlation class. This function is called when the "Apply" button is hit. """ modelid = self.corr.fit_model.id parameters = list() parameters_variable = list() # Read parameters from form and update self.active_parms[1] for i in np.arange(len(self.spincontrol)): parameters.append(1 * self.spincontrol[i].GetValue()) parameters_variable.append(self.checkboxes[i].GetValue()) self.corr.fit_parameters_variable = np.array(parameters_variable, dtype=bool) # As of version 0.7.5: we want the units to be displayed # human readable - the way they are displayed # in the Page info tool. # Here: Convert human readable units to program internal # units parmsconv = mdls.GetInternalFromHumanReadableParm( modelid, np.array(parameters))[1] self.corr.fit_parameters = parmsconv # Fitting parameters self.weighted_nuvar = self.Fitbox[5].GetValue() self.weighted_fittype_id = self.Fitbox[1].GetSelection() fitbox_value = self.Fitbox[1].GetValue() if self.weighted_fittype_id == -1: # User edited knot number Knots = fitbox_value Knots = "".join(filter(lambda x: x.isdigit(), Knots)) if Knots == "": Knots = "5" self.weighted_fittype_id = 1 self.FitKnots = str(Knots) fit_weight_type = "spline{}".format(self.FitKnots) fit_weight_data = self.weighted_nuvar elif self.weighted_fittype_id == 1: Knots = fitbox_value Knots = "".join(filter(lambda x: x.isdigit(), Knots)) self.FitKnots = int(Knots) fit_weight_type = "spline{}".format(self.FitKnots) fit_weight_data = self.weighted_nuvar elif self.weighted_fittype_id == 0: fit_weight_type = "none" fit_weight_data = None elif self.weighted_fittype_id == 2: fit_weight_type = "model function" fit_weight_data = self.weighted_nuvar else: # fitbox_selection > 2: fit_weight_type = fitbox_value self.corr.fit_weight_type = fitbox_value fit_weight_data = self.corr.fit_weight_data # Fitting algorithm keys = fit.GetAlgorithmStringList()[0] idalg = self.AlgorithmDropdown.GetSelection() self.corr.fit_algorithm = keys[idalg] self.corr.fit_weight_type = fit_weight_type self.corr.fit_weight_data = fit_weight_data # If parameters have been changed because of the check_parms # function, write them back. self.apply_parameters_reverse() def apply_parameters_reverse(self, event=None): """ Read the values from the pages parameters and write it to the GUI form. """ modelid = self.corr.fit_model.id # # As of version 0.7.5: we want the units to be displayed # human readable - the way they are displayed # in the Page info tool. # # Here: Convert program internal units to # human readable units parameters = mdls.GetHumanReadableParms(modelid, self.corr.fit_parameters)[1] parameters_variable = self.corr.fit_parameters_variable # Write parameters to the form on the Page for i in np.arange(len(self.active_parms[1])): self.spincontrol[i].SetValue(parameters[i]) self.checkboxes[i].SetValue(parameters_variable[i]) # Fitting parameters self.Fitbox[5].SetValue(self.weighted_nuvar) idf = self.weighted_fittype_id List = self.Fitbox[1].GetItems() List[1] = "spline (" + str(self.FitKnots) + " knots)" self.Fitbox[1].SetItems(List) self.Fitbox[1].SetSelection(idf) # Normalization if self.corr.normparm is None: normsel = 0 else: normsel = self.corr.normparm + 1 self.AmplitudeInfo[2].SetSelection(normsel) # Fitting algorithm keys = fit.GetAlgorithmStringList()[0] idalg = keys.index(self.corr.fit_algorithm) self.AlgorithmDropdown.SetSelection(idalg) self.updateChi2() def calculate_corr(self): """ Calculate model correlation function """ return self.corr.modeled def Fit_enable_fitting(self): """ Enable the fitting button and the weighted fit control""" # self.Fitbox = [ fitbox, weightedfitdrop, fittext, fittext2, # fittextvar, fitspin, buttonfit, textalg, # self.AlgorithmDropdown] self.Fitbox[0].Enable() self.Fitbox[1].Enable() self.Fitbox[6].Enable() self.Fitbox[7].Enable() self.Fitbox[8].Enable() def Fit_function(self, event=None, noplots=False, trigger=None): """ Calls the fit function. `noplots=True` prevents plotting of spline fits `trigger` is passed to page.PlotAll. If trigger is "fit_batch", then `noplots` is set to `True`. """ tools.batchcontrol.FitProgressDlg(self, self) def Fit_finalize(self, trigger): """ Things that need be done after fitting """ # Reset list of globally shared parameters, because we are only # fitting this single page now. # TODO: # - also remove this page from the GlobalParameterShare list of # the other pages self.GlobalParameterShare = [] # Update spin-control values self.apply_parameters_reverse() # Plot everything try: self.PlotAll(trigger=trigger) except OverflowError: # Sometimes parameters are just bad and # we still want the user to use the # program. warnings.warn("Could not plot canvas.") # update displayed chi2 self.updateChi2() def Fit_WeightedFitCheck(self, event=None): """ Enable Or disable variance calculation, dependent on "Weighted Fit" checkbox """ # self.Fitbox=[ fitbox, weightedfitdrop, fittext, fittext2, fittextvar, # fitspin, buttonfit ] weighted = (self.Fitbox[1].GetSelection() != 0) # In the case of "Average" we do not enable the # "Calculation of variance" part. if weighted is True and self.Fitbox[1].GetValue() != "Average": self.Fitbox[2].Enable() self.Fitbox[3].Enable() self.Fitbox[4].Enable() self.Fitbox[5].Enable() else: self.Fitbox[2].Disable() self.Fitbox[3].Disable() self.Fitbox[4].Disable() self.Fitbox[5].Disable() def MakeStaticBoxSizer(self, boxlabel): """ Create a Box with check boxes (fit yes/no) and possibilities to change initial values for fitting. Parameters: *boxlabel*: The name of the box (is being displayed) *self.active_parms[0]*: A list of things to put into the box Returns: *sizer*: The static Box *check*: The (un)set checkboxes *spin*: The spin text fields """ modelid = self.corr.fit_model.id box = wx.StaticBox(self.panelsettings, label=boxlabel) sizer = wx.StaticBoxSizer(box, wx.VERTICAL) check = list() spin = list() # # As of version 0.7.5: we want the units to be displayed # human readable - the way they are displayed # in the Page info tool. # labels = mdls.GetHumanReadableParms(modelid, self.corr.fit_parameters)[0] sizerh = wx.GridSizer(len(labels), 2, 2, 2) for label in labels: checkbox = wx.CheckBox(self.panelsettings, label=label) # We needed to "from wx.lib.agw import floatspin" to get this: spinctrl = wxutils.PCFFloatTextCtrl(self.panelsettings) sizerh.Add(spinctrl) sizerh.Add(checkbox) # Put everything into lists to be able to refer to it later check.append(checkbox) spin.append(spinctrl) sizer.Add(sizerh) return sizer, check, spin def OnAmplitudeCheck(self, event=None): """ Enable/Disable BG rate text line. New feature introduced in 0.7.8 """ modelid = self.corr.fit_model.id # Normalization to a certain parameter in plots # Find all parameters that start with an "N" # ? and "C" ? # Create List normlist = list() normlist.append("None") # Add parameters parameterlist = list() for i in np.arange(len(self.active_parms[0])): label = self.active_parms[0][i] if label[0].lower() == "n": normlist.append("*" + label) parameterlist.append(i) # Add supplementary parameters # Get them from models supplement = mdls.GetMoreInfo(modelid, self) if supplement is not None: for i in np.arange(len(supplement)): label = supplement[i][0] if label[0].lower() == "n": normlist.append("*" + label) # Add the id of the supplement starting at the # number of fitting parameters of current page. parameterlist.append(i + len(self.active_parms[0])) normsel = self.AmplitudeInfo[2].GetSelection() if normsel in [0, -1]: # init or no normalization selected self.corr.normparm = None normsel = 0 else: self.corr.normparm = parameterlist[normsel - 1] if len(parameterlist) > 0: self.AmplitudeInfo[2].Enable() self.AmplitudeInfo[3].Enable() else: self.AmplitudeInfo[2].Disable() self.AmplitudeInfo[3].Disable() # Set dropdown values self.AmplitudeInfo[2].SetItems(normlist) self.AmplitudeInfo[2].SetSelection(normsel) # Plot intensities # Quick reminder: # self.AmplitudeInfo = [ [intlabel1, intlabel2], # [bgspin1, bgspin2], # normtoNDropdown, textnor] # Signal self.AmplitudeInfo[0][0].SetValue("{:.4f}".format(0)) self.AmplitudeInfo[0][1].SetValue("{:.4f}".format(0)) for i in range(len(self.corr.traces)): S = self.corr.traces[i].countrate self.AmplitudeInfo[0][i].SetValue("{:.4f}".format(S)) if self.corr.is_cc: self.AmplitudeInfo[0][1].Enable() else: self.AmplitudeInfo[0][1].Disable() # Background # self.parent.Background[self.bgselected][i] # [0] average signal [kHz] # [1] signal name (edited by user) # [2] signal trace (tuple) ([ms], [kHz]) if len(self.corr.backgrounds) >= 1: self.AmplitudeInfo[1][0].SetValue( self.corr.backgrounds[0].countrate) else: self.AmplitudeInfo[1][0].SetValue(0) self.AmplitudeInfo[1][1].SetValue(0) if len(self.corr.backgrounds) == 2: self.AmplitudeInfo[1][1].SetValue( self.corr.backgrounds[1].countrate) else: self.AmplitudeInfo[1][1].SetValue(0) # Disable the second line in amplitude correction, if we have # autocorrelation only. boolval = self.corr.is_cc for item in self.WXAmplitudeCCOnlyStuff: item.Enable(boolval) def OnBGSpinChanged(self, e): """ Calls tools.background.ApplyAutomaticBackground to update background information """ # Quick reminder: # self.AmplitudeInfo = [ [intlabel1, intlabel2], # [bgspin1, bgspin2], # normtoNDropdown, textnor] if self.corr.is_cc: # update both self.bgselected and self.bg2selected bg = [ float(self.AmplitudeInfo[1][0].GetValue()), float(self.AmplitudeInfo[1][1].GetValue()) ] sig = [ float(self.AmplitudeInfo[0][0].GetValue()), float(self.AmplitudeInfo[0][1].GetValue()) ] # Make sure bg < sig for ii in range(len(bg)): if sig[ii] != 0: if bg[ii] > .99 * sig[ii]: bg[ii] = .99 * sig[ii] self.AmplitudeInfo[1][ii].SetValue(bg[ii]) else: # Only update self.bgselected bg = float(self.AmplitudeInfo[1][0].GetValue()) sig = float(self.AmplitudeInfo[0][0].GetValue()) # Make sure bg < sig if sig != 0: if bg > .99 * sig: bg = .99 * sig self.AmplitudeInfo[1][0].SetValue(bg) tools.background.ApplyAutomaticBackground(self, bg, self.parent) e.Skip() def OnTitleChanged(self, e): modelid = self.corr.fit_model.id pid = self.parent.notebook.GetPageIndex(self) if self.tabtitle.GetValue() == "": text = self.counter + mdls.modeldict[modelid][1] else: # How many characters of the the page title should be displayed # in the tab? We choose 9: AC1-012 plus 2 whitespaces text = self.counter + self.tabtitle.GetValue()[-9:] self.parent.notebook.SetPageText(pid, text) def OnSetRange(self, e): """ Open a new Frame where the parameter range can be set. Rewrites self.parameter_range Parameter ranges are treated like parameters: They are saved in sessions and applied in batch mode. """ # TODO: # - make range selector work with new class # We write a separate tool for that. # This tool does not show up in the Tools menu. if self.parent.RangeSelector is None: self.parent.RangeSelector = tools.RangeSelector(self) self.parent.RangeSelector.Bind(wx.EVT_CLOSE, self.parent.RangeSelector.OnClose) else: try: self.parent.RangeSelector.OnClose() except: pass self.parent.RangeSelector = None def OnSize(self, event): """ Resize the fitting Panel, when Window is resized. """ size = self.parent.notebook.GetSize() tabsize = 33 size[1] = size[1] - tabsize self.sp.SetSize(size) def PlotAll(self, event=None, trigger=None): """ This function plots the whole correlation and residuals canvas. We do: - Channel selection - Background correction - Apply Parameters (separate function) - Drawing of plots The `event` is usually just an event from buttons or similar wx objects. It can be "init", then some initial plotting is done before the data is handled. The `trigger` is passed to `self.parent.OnFNBPageChanged` so that tools can update their content accordingly. For more information on triggers, have a look at the doctring of the `tools` submodule. """ if event == "init": # We use this to have the page plotted at least once before # readout of parameters (e.g. startcrop, endcrop) # This is a performence tweak. if self.InitialPlot: return else: self.InitialPlot = True # Enable/Disable, set values frontend normalization self.OnAmplitudeCheck() # Apply parameters self.apply_parameters() # Calculate correlation function from parameters # Drawing of correlation plot # Plots corr.correlation_fit and the calcualted correlation function # self.datacorr into the upper canvas. # Create a line @ y=zero: zerostart = self.corr.lag_time_fit[0] zeroend = self.corr.lag_time_fit[-1] datazero = [[zerostart, 0], [zeroend, 0]] # Set plot colors width = 1 colexp = "grey" colfit = "blue" colweight = "cyan" lines = list() linezero = plot.PolyLine(datazero, colour='orange', width=width) lines.append(linezero) if self.corr.correlation is not None: if self.corr.is_weighted_fit and \ self.parent.MenuShowWeights.IsChecked(): try: weights = self.corr.fit_results["fit weights"] except: weights = self.corr.fit_weight_data if isinstance(weights, np.ndarray): # user might have selected a new weight type and # presses apply, do not try to display weights # if weights are from average or other, make sure that the # dimensions are correct if weights.shape[0] == self.corr.correlation.shape[0]: weights = weights[self.corr.fit_ival[0]:self.corr. fit_ival[1]] # perform some checks if np.allclose(weights, np.ones_like(weights)): weights = 0 elif weights.shape[0] != self.corr.modeled_fit.shape[0]: # non-matching weigths warnings.warn( "Unmatching weights found. Probably from previous data set." ) weights = 0 # Add the weights to the graph. # This is done by drawing two lines. w = 1 * self.corr.modeled_fit w1 = 1 * w w2 = 1 * w w1[:, 1] = w[:, 1] + weights w2[:, 1] = w[:, 1] - weights # crop w1 and w2 if corr.correlation_fit does not include all # data points. if np.all(w[:, 0] == self.corr.correlation_fit[:, 0]): pass else: raise ValueError( "This should not have happened: size of weights is wrong." ) # Normalization with self.normfactor w1[:, 1] *= self.corr.normalize_factor w2[:, 1] *= self.corr.normalize_factor self.weights_plot_fill_area = [w1, w2] lineweight1 = plot.PolyLine(w1, legend='', colour=colweight, width=width) lines.append(lineweight1) lineweight2 = plot.PolyLine(w2, legend='', colour=colweight, width=width) lines.append(lineweight2) else: self.weights_plot_fill_area = None # Plot Correlation curves # Plot both, experimental and calculated data # Normalization with self.normfactor, new feature in 0.7.8 datacorr_norm = self.corr.modeled_plot linecorr = plot.PolyLine(datacorr_norm, legend='', colour=colfit, width=width) dataexp_norm = self.corr.correlation_plot lineexp = plot.PolyLine(dataexp_norm, legend='', colour=colexp, width=width) # Draw linezero first, so it is in the background lines.append(lineexp) lines.append(linecorr) PlotCorr = plot.PlotGraphics(lines, xLabel=u'lag time τ [ms]', yLabel=u'G(τ)') self.canvascorr.Draw(PlotCorr) # Calculate residuals resid_norm = self.corr.residuals_plot lineres = plot.PolyLine(resid_norm, legend='', colour=colfit, width=width) # residuals or weighted residuals? if self.corr.is_weighted_fit: yLabelRes = "weighted \nresiduals" else: yLabelRes = "residuals" PlotRes = plot.PlotGraphics([linezero, lineres], xLabel=u'lag time τ [ms]', yLabel=yLabelRes) self.canvaserr.Draw(PlotRes) else: # Amplitude normalization, new feature in 0.7.8 datacorr_norm = self.corr.modeled_plot linecorr = plot.PolyLine(datacorr_norm, legend='', colour='blue', width=1) PlotCorr = plot.PlotGraphics([linezero, linecorr], xLabel=u'Lag time τ [ms]', yLabel=u'G(τ)') self.canvascorr.Draw(PlotCorr) self.Refresh() self.parent.OnFNBPageChanged(trigger=trigger) def settings(self): """ Here we define, what should be displayed at the left side of the fitting page/tab. Parameters: """ modelid = self.corr.fit_model.id horizontalsize = self.sizepanelx - 10 # Title # Create empty tab title mddat = mdls.modeldict[modelid] modelshort = mdls.GetModelType(modelid) titlelabel = u"Data set {} ({} {})".format(self.counter.strip(" :"), modelshort, mddat[1]) boxti = wx.StaticBox(self.panelsettings, label=titlelabel) sizerti = wx.StaticBoxSizer(boxti, wx.VERTICAL) sizerti.SetMinSize((horizontalsize, -1)) self.tabtitle = wx.TextCtrl(self.panelsettings, value="", size=(horizontalsize - 20, -1)) self.Bind(wx.EVT_TEXT, self.OnTitleChanged, self.tabtitle) sizerti.Add(self.tabtitle, 0, wx.EXPAND, 0) # Create StaticBoxSizer box1, check, spin = self.MakeStaticBoxSizer("Model parameters") # Make the check boxes and spin-controls available everywhere self.checkboxes = check self.spincontrol = spin # # As of version 0.7.5: we want the units to be displayed # human readable - the way they are displayed # in the Page info tool. # labels, parameters = mdls.GetHumanReadableParms( modelid, self.active_parms[1]) parameterstofit = self.active_parms[2] # Set initial values given by user/programmer for Diffusion Model for i in np.arange(len(labels)): self.checkboxes[i].SetValue(parameterstofit[i]) self.spincontrol[i].SetValue(parameters[i]) # Put everything together self.panelsettings.sizer = wx.BoxSizer(wx.VERTICAL) self.panelsettings.sizer.Add(sizerti, 0, wx.EXPAND, 0) self.panelsettings.sizer.Add(box1, 0, wx.EXPAND, 0) # checkbox "Protect from batch control" self.wxCBPreventBatchParms = wx.CheckBox(self.panelsettings, -1, "prevent batch modification") box1.Add(self.wxCBPreventBatchParms) # buttons "Apply" and "Set range" horzs = wx.BoxSizer(wx.HORIZONTAL) buttonapply = wx.Button(self.panelsettings, label="Apply") self.Bind(wx.EVT_BUTTON, self.PlotAll, buttonapply) horzs.Add(buttonapply) buttonrange = wx.Button(self.panelsettings, label="Set range") self.Bind(wx.EVT_BUTTON, self.OnSetRange, buttonrange) horzs.Add(buttonrange) box1.Add(horzs) # Set horizontal size box1.SetMinSize((horizontalsize, -1)) # More info normbox = wx.StaticBox(self.panelsettings, label="Amplitude corrections") miscsizer = wx.StaticBoxSizer(normbox, wx.VERTICAL) miscsizer.SetMinSize((horizontalsize, -1)) # Intensities and Background sizeint = wx.FlexGridSizer(rows=3, cols=3, vgap=5, hgap=5) sizeint.Add(wx.StaticText(self.panelsettings, label="[kHz]")) sizeint.Add(wx.StaticText(self.panelsettings, label="Intensity")) sizeint.Add(wx.StaticText(self.panelsettings, label="Background")) sizeint.Add(wx.StaticText(self.panelsettings, label="Ch1")) intlabel1 = wx.TextCtrl(self.panelsettings) bgspin1 = wxutils.PCFFloatTextCtrl(self.panelsettings) bgspin1.Bind(wx.EVT_KILL_FOCUS, self.OnBGSpinChanged) bgspin1.Bind(wx.EVT_TEXT_ENTER, self.OnBGSpinChanged) sizeint.Add(intlabel1) intlabel1.SetEditable(False) sizeint.Add(bgspin1) chtext2 = wx.StaticText(self.panelsettings, label="Ch2") sizeint.Add(chtext2) intlabel2 = wx.TextCtrl(self.panelsettings) intlabel2.SetEditable(False) bgspin2 = wxutils.PCFFloatTextCtrl(self.panelsettings) bgspin2.Bind(wx.EVT_KILL_FOCUS, self.OnBGSpinChanged) sizeint.Add(intlabel2) sizeint.Add(bgspin2) miscsizer.Add(sizeint) # Normalize to n? textnor = wx.StaticText(self.panelsettings, label="Plot normalization") miscsizer.Add(textnor) normtoNDropdown = wx.ComboBox(self.panelsettings) self.Bind(wx.EVT_COMBOBOX, self.PlotAll, normtoNDropdown) miscsizer.Add(normtoNDropdown) self.AmplitudeInfo = [[intlabel1, intlabel2], [bgspin1, bgspin2], normtoNDropdown, textnor] self.WXAmplitudeCCOnlyStuff = [chtext2, intlabel2, bgspin2] self.panelsettings.sizer.Add(miscsizer, 0, wx.EXPAND, 0) # Add fitting Box fitbox = wx.StaticBox(self.panelsettings, label="Fitting options") fitsizer = wx.StaticBoxSizer(fitbox, wx.VERTICAL) fitsizer.SetMinSize((horizontalsize, -1)) # Add a checkbox for weighted fitting weightedfitdrop = wx.ComboBox(self.panelsettings) self.weightlist = ["no weights", "spline (5 knots)", "model function"] weightedfitdrop.SetItems(self.weightlist) weightedfitdrop.SetSelection(0) fitsizer.Add(weightedfitdrop) # WeightedFitCheck() Enables or Disables the variance part weightedfitdrop.Bind(wx.EVT_COMBOBOX, self.Fit_WeightedFitCheck) # Add the variance part. # In order to do a weighted fit, we need to calculate the variance # at each point of the experimental data array. # In order to do that, we need to know how many data points from left # and right of the interesting data point we want to include in that # calculation. fittext = wx.StaticText(self.panelsettings, label="Calculation of the variance") fitsizer.Add(fittext) fittext2 = wx.StaticText(self.panelsettings, label="from 2j+1 data points") fitsizer.Add(fittext2) fitsizerspin = wx.BoxSizer(wx.HORIZONTAL) fittextvar = wx.StaticText(self.panelsettings, label="j = ") fitspin = wx.SpinCtrl(self.panelsettings, -1, initial=3, min=1, max=100) fitsizerspin.Add(fittextvar) fitsizerspin.Add(fitspin) fitsizer.Add(fitsizerspin) # Add algorithm selection textalg = wx.StaticText(self.panelsettings, label="Algorithm") fitsizer.Add(textalg) self.AlgorithmDropdown = wx.ComboBox(self.panelsettings) items = fit.GetAlgorithmStringList()[1] self.AlgorithmDropdown.SetItems(items) self.Bind(wx.EVT_COMBOBOX, self.apply_parameters, self.AlgorithmDropdown) fitsizer.Add(self.AlgorithmDropdown) self.AlgorithmDropdown.SetMaxSize(weightedfitdrop.GetSize()) # Add button "Fit" and chi2 display fitbuttonsizer = wx.BoxSizer(wx.HORIZONTAL) buttonfit = wx.Button(self.panelsettings, label="Fit") self.Bind(wx.EVT_BUTTON, self.Fit_function, buttonfit) fitbuttonsizer.Add(buttonfit) # add shortcut acctbl = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord('F'), buttonfit.GetId())]) self.SetAcceleratorTable(acctbl) ## self.WXTextChi2 = wx.StaticText(self.panelsettings) # this StaticText is updated by `self.updateChi2()` fitbuttonsizer.Add(self.WXTextChi2, flag=wx.ALIGN_CENTER) fitsizer.Add(fitbuttonsizer) self.panelsettings.sizer.Add(fitsizer, 0, wx.EXPAND, 0) # Squeeze everything into the sizer self.panelsettings.SetSizer(self.panelsettings.sizer) # This is also necessary in Windows self.panelsettings.Layout() self.panelsettings.Show() # Make all the stuff available for everyone self.Fitbox = [ fitbox, weightedfitdrop, fittext, fittext2, fittextvar, fitspin, buttonfit, textalg, self.AlgorithmDropdown ] # Disable Fitting since no data has been loaded yet for element in self.Fitbox: element.Disable() self.panelsettings.sizer.Fit(self.panelsettings) self.parent.Layout() def updateChi2(self): """ updates the self.WXTextChi2 text control """ label = u"" if hasattr(self.corr, "fit_results"): if "chi2" in self.corr.fit_results: chi2 = self.corr.fit_results["chi2"] chi2str = wxutils.float2string_nsf(chi2, n=3) chi2str = wxutils.nice_string(chi2str) label = u" χ²={}".format(chi2str) # This does not work with wxPython 2.8.12: # self.WXTextChi2.SetLabelMarkup(u"<b>{}</b>".format(label)) self.WXTextChi2.SetLabel(u"{}".format(label))
class FittingPanel(wx.Panel): """ Those are the Panels that show the fitting dialogs with the Plots. """ def __init__(self, parent, counter, modelid, active_parms, tau=None): """ Initialize with given parameters. """ wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY) self.parent = parent self.corr = Correlation(fit_model=modelid) if tau is not None: self.corr.lag_time = tau # active_parameters: # [0] labels # [1] values # [2] bool values to fit self.corr.fit_parameters = active_parms[1] self.corr.fit_parameters_variable = active_parms[2] self._bgselected = None self._bg2selected = None self.FitKnots = 5 # number of knots for spline fit or similiars self.weighted_fittype_id = 0 # integer (drop down item) self.weighted_nuvar = 3 # bins for std-dev. (left and rigth) # The weights that are plotted in the page # This is set by the PlotAll function self.weights_plot_fill_area = None # A list containing page numbers that share parameters with this page. # This parameter is defined by the global fitting tool and is saved in # sessions. self.GlobalParameterShare = [] # Counts number of Pages already created: self.counter = counter # Has inital plot been performed? # Call PlotAll("init") to set this to true. If it is true, then # nothing will be plotted if called with "init" self.InitialPlot = False # Model we are using # Tool statistics uses this list: self.StatisticsCheckboxes = None ### Splitter window # Sizes size = parent.notebook.GetSize() tabsize = 33 size[1] = size[1] - tabsize self.sizepanelx = 270 canvasx = size[0]-self.sizepanelx+5 sizepanel = (self.sizepanelx, size[1]) sizecanvas = (canvasx, size[1]) self.sp = wx.SplitterWindow(self, size=size, style=wx.SP_3DSASH) # This is necessary to prevent "Unsplit" of the SplitterWindow: self.sp.SetMinimumPaneSize(1) ## Settings Section (left side) #self.panelsettings = wx.Panel(self.sp, size=sizepanel) self.panelsettings = scrolled.ScrolledPanel(self.sp, size=sizepanel) self.panelsettings.SetupScrolling(scroll_x=False) ## Setting up Plot (correlation + chi**2) self.spcanvas = wx.SplitterWindow(self.sp, size=sizecanvas, style=wx.SP_3DSASH) # This is necessary to prevent "Unsplit" of the SplitterWindow: self.spcanvas.SetMinimumPaneSize(1) # y difference in pixels between Auocorrelation and Residuals cupsizey = size[1]*4/5 # Calculate initial data self.calculate_corr() # Draw the settings section self.settings() # Load default values self.apply_parameters_reverse() # Upper Plot for plotting of Correlation Function self.canvascorr = plot.PlotCanvas(self.spcanvas) self.canvascorr.setLogScale((True, False)) self.canvascorr.SetEnableZoom(True) self.PlotAll(event="init", trigger="tab_init") self.canvascorr.SetSize((canvasx, cupsizey)) # Lower Plot for plotting of the residuals self.canvaserr = plot.PlotCanvas(self.spcanvas) self.canvaserr.setLogScale((True, False)) self.canvaserr.SetEnableZoom(True) self.canvaserr.SetSize((canvasx, size[1]-cupsizey)) self.spcanvas.SplitHorizontally(self.canvascorr, self.canvaserr, cupsizey) self.sp.SplitVertically(self.panelsettings, self.spcanvas, self.sizepanelx) # Bind resizing to resizing function. wx.EVT_SIZE(self, self.OnSize) @property def active_parms(self): names = self.corr.fit_model.parameters[0] parms = self.corr.fit_parameters bools = self.corr.fit_parameters_variable return [names, parms, bools] @property def IsCrossCorrelation(self): return self.corr.is_cc @property def modelid(self): return self.corr.fit_model.id @property def prevent_batch_modification(self): return self.wxCBPreventBatchParms.GetValue() @prevent_batch_modification.setter def prevent_batch_modification(self, value): self.wxCBPreventBatchParms.SetValue(value) @property def title(self): return self.tabtitle.GetValue() @title.setter def title(self, title): self.tabtitle.SetValue(title.strip()) self.corr.title = title.strip() @property def traceavg(self): warnings.warn("Trace average always set to none!") return None @property def tracecc(self): if self.corr.is_cc and len(self.corr.traces) != 0: return self.corr.traces else: return None @property def bgselected(self): return self._bgselected @bgselected.setter def bgselected(self, value): if value is None: self.corr.backgrounds=[] return # check paren.Background and get id background = self.parent.Background[value] self.corr.background_replace(0, background) self._bgselected = value @property def bg2selected(self): return self._bg2selected @bg2selected.setter def bg2selected(self, value): if value is None: if self.corr.is_cc: self.corr.backgrounds=[] return # check paren.Background and get id background = self.parent.Background[value] self.corr.background_replace(1, background) self._bg2selected = value def apply_parameters(self, event=None): """ Read the values from the GUI form and write it to the pages parameters / correlation class. This function is called when the "Apply" button is hit. """ modelid = self.corr.fit_model.id parameters = list() parameters_variable = list() # Read parameters from form and update self.active_parms[1] for i in np.arange(len(self.spincontrol)): parameters.append(1*self.spincontrol[i].GetValue()) parameters_variable.append(self.checkboxes[i].GetValue()) self.corr.fit_parameters_variable = np.array(parameters_variable, dtype=bool) # As of version 0.7.5: we want the units to be displayed # human readable - the way they are displayed # in the Page info tool. # Here: Convert human readable units to program internal # units parmsconv = mdls.GetInternalFromHumanReadableParm( modelid, np.array(parameters))[1] self.corr.fit_parameters = parmsconv # Fitting parameters self.weighted_nuvar = self.Fitbox[5].GetValue() self.weighted_fittype_id = self.Fitbox[1].GetSelection() fitbox_value = self.Fitbox[1].GetValue() if self.weighted_fittype_id == -1: # User edited knot number Knots = fitbox_value Knots = filter(lambda x: x.isdigit(), Knots) if Knots == "": Knots = "5" self.weighted_fittype_id = 1 self.FitKnots = str(Knots) fit_weight_type = "spline{}".format(self.FitKnots) fit_weight_data = self.weighted_nuvar elif self.weighted_fittype_id == 1: Knots = fitbox_value Knots = filter(lambda x: x.isdigit(), Knots) self.FitKnots = int(Knots) fit_weight_type = "spline{}".format(self.FitKnots) fit_weight_data = self.weighted_nuvar elif self.weighted_fittype_id == 0: fit_weight_type = "none" fit_weight_data = None elif self.weighted_fittype_id == 2: fit_weight_type = "model function" fit_weight_data = self.weighted_nuvar else: # fitbox_selection > 2: fit_weight_type = fitbox_value self.corr.fit_weight_type = fitbox_value fit_weight_data = self.corr.fit_weight_data # Fitting algorithm keys = fit.GetAlgorithmStringList()[0] idalg = self.AlgorithmDropdown.GetSelection() self.corr.fit_algorithm = keys[idalg] self.corr.fit_weight_type = fit_weight_type self.corr.fit_weight_data = fit_weight_data # If parameters have been changed because of the check_parms # function, write them back. self.apply_parameters_reverse() def apply_parameters_reverse(self, event=None): """ Read the values from the pages parameters and write it to the GUI form. """ modelid = self.corr.fit_model.id # # As of version 0.7.5: we want the units to be displayed # human readable - the way they are displayed # in the Page info tool. # # Here: Convert program internal units to # human readable units parameters = mdls.GetHumanReadableParms(modelid, self.corr.fit_parameters)[1] parameters_variable = self.corr.fit_parameters_variable # Write parameters to the form on the Page for i in np.arange(len(self.active_parms[1])): self.spincontrol[i].SetValue(parameters[i]) self.checkboxes[i].SetValue(parameters_variable[i]) # Fitting parameters self.Fitbox[5].SetValue(self.weighted_nuvar) idf = self.weighted_fittype_id List = self.Fitbox[1].GetItems() List[1] = "spline ("+str(self.FitKnots)+" knots)" self.Fitbox[1].SetItems(List) self.Fitbox[1].SetSelection(idf) # Normalization if self.corr.normparm is None: normsel = 0 else: normsel = self.corr.normparm + 1 self.AmplitudeInfo[2].SetSelection(normsel) # Fitting algorithm keys = fit.GetAlgorithmStringList()[0] idalg = keys.index(self.corr.fit_algorithm) self.AlgorithmDropdown.SetSelection(idalg) self.updateChi2() def calculate_corr(self): """ Calculate model correlation function """ return self.corr.modeled def Fit_enable_fitting(self): """ Enable the fitting button and the weighted fit control""" #self.Fitbox = [ fitbox, weightedfitdrop, fittext, fittext2, # fittextvar, fitspin, buttonfit, textalg, # self.AlgorithmDropdown] self.Fitbox[0].Enable() self.Fitbox[1].Enable() self.Fitbox[6].Enable() self.Fitbox[7].Enable() self.Fitbox[8].Enable() def Fit_function(self, event=None, noplots=False, trigger=None): """ Calls the fit function. `noplots=True` prevents plotting of spline fits `trigger` is passed to page.PlotAll. If trigger is "fit_batch", then `noplots` is set to `True`. """ tools.batchcontrol.FitProgressDlg(self, self) def Fit_finalize(self, trigger): """ Things that need be done after fitting """ # Reset list of globally shared parameters, because we are only # fitting this single page now. # TODO: # - also remove this page from the GlobalParameterShare list of # the other pages self.GlobalParameterShare = [] # Update spin-control values self.apply_parameters_reverse() # Plot everything try: self.PlotAll(trigger=trigger) except OverflowError: # Sometimes parameters are just bad and # we still want the user to use the # program. warnings.warn("Could not plot canvas.") # update displayed chi2 self.updateChi2() def Fit_WeightedFitCheck(self, event=None): """ Enable Or disable variance calculation, dependent on "Weighted Fit" checkbox """ #self.Fitbox=[ fitbox, weightedfitdrop, fittext, fittext2, fittextvar, # fitspin, buttonfit ] weighted = (self.Fitbox[1].GetSelection() != 0) # In the case of "Average" we do not enable the # "Calculation of variance" part. if weighted is True and self.Fitbox[1].GetValue() != "Average": self.Fitbox[2].Enable() self.Fitbox[3].Enable() self.Fitbox[4].Enable() self.Fitbox[5].Enable() else: self.Fitbox[2].Disable() self.Fitbox[3].Disable() self.Fitbox[4].Disable() self.Fitbox[5].Disable() def MakeStaticBoxSizer(self, boxlabel): """ Create a Box with check boxes (fit yes/no) and possibilities to change initial values for fitting. Parameters: *boxlabel*: The name of the box (is being displayed) *self.active_parms[0]*: A list of things to put into the box Returns: *sizer*: The static Box *check*: The (un)set checkboxes *spin*: The spin text fields """ modelid = self.corr.fit_model.id box = wx.StaticBox(self.panelsettings, label=boxlabel) sizer = wx.StaticBoxSizer(box, wx.VERTICAL) check = list() spin = list() # # As of version 0.7.5: we want the units to be displayed # human readable - the way they are displayed # in the Page info tool. # labels = mdls.GetHumanReadableParms(modelid, self.corr.fit_parameters)[0] for label in labels: sizerh = wx.BoxSizer(wx.HORIZONTAL) checkbox = wx.CheckBox(self.panelsettings, label=label) # We needed to "from wx.lib.agw import floatspin" to get this: spinctrl = wxutils.PCFFloatTextCtrl(self.panelsettings) sizerh.Add(spinctrl) sizerh.Add(checkbox) sizer.Add(sizerh) # Put everything into lists to be able to refer to it later check.append(checkbox) spin.append(spinctrl) return sizer, check, spin def OnAmplitudeCheck(self, event=None): """ Enable/Disable BG rate text line. New feature introduced in 0.7.8 """ modelid = self.corr.fit_model.id ## Normalization to a certain parameter in plots # Find all parameters that start with an "N" # ? and "C" ? # Create List normlist = list() normlist.append("None") ## Add parameters parameterlist = list() for i in np.arange(len(self.active_parms[0])): label = self.active_parms[0][i] if label[0].lower() == "n": normlist.append("*"+label) parameterlist.append(i) ## Add supplementary parameters # Get them from models supplement = mdls.GetMoreInfo(modelid, self) if supplement is not None: for i in np.arange(len(supplement)): label = supplement[i][0] if label[0].lower() == "n": normlist.append("*"+label) # Add the id of the supplement starting at the # number of fitting parameters of current page. parameterlist.append(i+len(self.active_parms[0])) normsel = self.AmplitudeInfo[2].GetSelection() if normsel in [0, -1]: # init or no normalization selected self.corr.normparm = None normsel = 0 else: self.corr.normparm = parameterlist[normsel-1] if len(parameterlist) > 0: self.AmplitudeInfo[2].Enable() self.AmplitudeInfo[3].Enable() else: self.AmplitudeInfo[2].Disable() self.AmplitudeInfo[3].Disable() # Set dropdown values self.AmplitudeInfo[2].SetItems(normlist) self.AmplitudeInfo[2].SetSelection(normsel) ## Plot intensities # Quick reminder: #self.AmplitudeInfo = [ [intlabel1, intlabel2], # [bgspin1, bgspin2], # normtoNDropdown, textnor] # Signal self.AmplitudeInfo[0][0].SetValue("{:.4f}".format(0)) self.AmplitudeInfo[0][1].SetValue("{:.4f}".format(0)) for i in range(len(self.corr.traces)): S = self.corr.traces[i].countrate self.AmplitudeInfo[0][i].SetValue("{:.4f}".format(S)) if self.corr.is_cc: self.AmplitudeInfo[0][1].Enable() else: self.AmplitudeInfo[0][1].Disable() # Background ## self.parent.Background[self.bgselected][i] ## [0] average signal [kHz] ## [1] signal name (edited by user) ## [2] signal trace (tuple) ([ms], [kHz]) if len(self.corr.backgrounds) >= 1: self.AmplitudeInfo[1][0].SetValue( self.corr.backgrounds[0].countrate) else: self.AmplitudeInfo[1][0].SetValue(0) self.AmplitudeInfo[1][1].SetValue(0) if len(self.corr.backgrounds) == 2: self.AmplitudeInfo[1][1].SetValue( self.corr.backgrounds[1].countrate) else: self.AmplitudeInfo[1][1].SetValue(0) # Disable the second line in amplitude correction, if we have # autocorrelation only. boolval = self.corr.is_cc for item in self.WXAmplitudeCCOnlyStuff: item.Enable(boolval) def OnBGSpinChanged(self, e): """ Calls tools.background.ApplyAutomaticBackground to update background information """ # Quick reminder: #self.AmplitudeInfo = [ [intlabel1, intlabel2], # [bgspin1, bgspin2], # normtoNDropdown, textnor] if self.corr.is_cc: # update both self.bgselected and self.bg2selected bg = [float(self.AmplitudeInfo[1][0].GetValue()), float(self.AmplitudeInfo[1][1].GetValue())] sig = [float(self.AmplitudeInfo[0][0].GetValue()), float(self.AmplitudeInfo[0][1].GetValue())] # Make sure bg < sig for ii in range(len(bg)): if sig[ii] != 0: if bg[ii] > .99*sig[ii]: bg[ii] = .99*sig[ii] self.AmplitudeInfo[1][ii].SetValue(bg[ii]) else: # Only update self.bgselected bg = float(self.AmplitudeInfo[1][0].GetValue()) sig = float(self.AmplitudeInfo[0][0].GetValue()) # Make sure bg < sig if sig != 0: if bg > .99*sig: bg = .99*sig self.AmplitudeInfo[1][0].SetValue(bg) tools.background.ApplyAutomaticBackground(self, bg, self.parent) e.Skip() def OnTitleChanged(self, e): modelid = self.corr.fit_model.id pid = self.parent.notebook.GetPageIndex(self) if self.tabtitle.GetValue() == "": text = self.counter + mdls.modeldict[modelid][1] else: # How many characters of the the page title should be displayed # in the tab? We choose 9: AC1-012 plus 2 whitespaces text = self.counter + self.tabtitle.GetValue()[-9:] self.parent.notebook.SetPageText(pid,text) def OnSetRange(self, e): """ Open a new Frame where the parameter range can be set. Rewrites self.parameter_range Parameter ranges are treated like parameters: They are saved in sessions and applied in batch mode. """ # TODO: # - make range selector work with new class # We write a separate tool for that. # This tool does not show up in the Tools menu. if self.parent.RangeSelector is None: self.parent.RangeSelector = tools.RangeSelector(self) self.parent.RangeSelector.Bind(wx.EVT_CLOSE, self.parent.RangeSelector.OnClose) else: try: self.parent.RangeSelector.OnClose() except: pass self.parent.RangeSelector = None def OnSize(self, event): """ Resize the fitting Panel, when Window is resized. """ size = self.parent.notebook.GetSize() tabsize = 33 size[1] = size[1] - tabsize self.sp.SetSize(size) def PlotAll(self, event=None, trigger=None): """ This function plots the whole correlation and residuals canvas. We do: - Channel selection - Background correction - Apply Parameters (separate function) - Drawing of plots The `event` is usually just an event from buttons or similar wx objects. It can be "init", then some initial plotting is done before the data is handled. The `trigger` is passed to `self.parent.OnFNBPageChanged` so that tools can update their content accordingly. For more information on triggers, have a look at the doctring of the `tools` submodule. """ if event == "init": # We use this to have the page plotted at least once before # readout of parameters (e.g. startcrop, endcrop) # This is a performence tweak. if self.InitialPlot: return else: self.InitialPlot = True ## Enable/Disable, set values frontend normalization self.OnAmplitudeCheck() ## Apply parameters self.apply_parameters() # Calculate correlation function from parameters ## Drawing of correlation plot # Plots corr.correlation_fit and the calcualted correlation function # self.datacorr into the upper canvas. # Create a line @ y=zero: zerostart = self.corr.lag_time_fit[0] zeroend = self.corr.lag_time_fit[-1] datazero = [[zerostart, 0], [zeroend,0]] # Set plot colors width = 1 colexp = "grey" colfit = "blue" colweight = "cyan" lines = list() linezero = plot.PolyLine(datazero, colour='orange', width=width) lines.append(linezero) if self.corr.correlation is not None: if self.corr.is_weighted_fit and \ self.parent.MenuShowWeights.IsChecked(): try: weights = self.corr.fit_results["fit weights"] except: weights = self.corr.fit_weight_data if isinstance(weights, np.ndarray): # user might have selected a new weight type and # presses apply, do not try to display weights # if weights are from average or other, make sure that the # dimensions are correct if weights.shape[0] == self.corr.correlation.shape[0]: weights = weights[self.corr.fit_ival[0]:self.corr.fit_ival[1]] # perform some checks if np.allclose(weights, np.ones_like(weights)): weights = 0 elif weights.shape[0] != self.corr.modeled_fit.shape[0]: # non-matching weigths warnings.warn("Unmatching weights found. Probably from previous data set.") weights = 0 # Add the weights to the graph. # This is done by drawing two lines. w = 1*self.corr.modeled_fit w1 = 1*w w2 = 1*w w1[:, 1] = w[:, 1] + weights w2[:, 1] = w[:, 1] - weights # crop w1 and w2 if corr.correlation_fit does not include all # data points. if np.all(w[:,0] == self.corr.correlation_fit[:,0]): pass else: raise ValueError("This should not have happened: size of weights is wrong.") ## Normalization with self.normfactor w1[:,1] *= self.corr.normalize_factor w2[:,1] *= self.corr.normalize_factor self.weights_plot_fill_area = [w1,w2] lineweight1 = plot.PolyLine(w1, legend='', colour=colweight, width=width) lines.append(lineweight1) lineweight2 = plot.PolyLine(w2, legend='', colour=colweight, width=width) lines.append(lineweight2) else: self.weights_plot_fill_area = None ## Plot Correlation curves # Plot both, experimental and calculated data # Normalization with self.normfactor, new feature in 0.7.8 datacorr_norm = self.corr.modeled_plot linecorr = plot.PolyLine(datacorr_norm, legend='', colour=colfit, width=width) dataexp_norm = self.corr.correlation_plot lineexp = plot.PolyLine(dataexp_norm, legend='', colour=colexp, width=width) # Draw linezero first, so it is in the background lines.append(lineexp) lines.append(linecorr) PlotCorr = plot.PlotGraphics(lines, xLabel=u'lag time τ [ms]', yLabel=u'G(τ)') self.canvascorr.Draw(PlotCorr) ## Calculate residuals resid_norm = self.corr.residuals_plot lineres = plot.PolyLine(resid_norm, legend='', colour=colfit, width=width) # residuals or weighted residuals? if self.corr.is_weighted_fit: yLabelRes = "weighted \nresiduals" else: yLabelRes = "residuals" PlotRes = plot.PlotGraphics([linezero, lineres], xLabel=u'lag time τ [ms]', yLabel=yLabelRes) self.canvaserr.Draw(PlotRes) else: # Amplitude normalization, new feature in 0.7.8 datacorr_norm = self.corr.modeled_plot linecorr = plot.PolyLine(datacorr_norm, legend='', colour='blue', width=1) PlotCorr = plot.PlotGraphics([linezero, linecorr], xLabel=u'Lag time τ [ms]', yLabel=u'G(τ)') self.canvascorr.Draw(PlotCorr) self.parent.OnFNBPageChanged(trigger=trigger) def settings(self): """ Here we define, what should be displayed at the left side of the fitting page/tab. Parameters: """ modelid = self.corr.fit_model.id horizontalsize = self.sizepanelx-10 # Title # Create empty tab title mddat = mdls.modeldict[modelid] modelshort = mdls.GetModelType(modelid) titlelabel = u"Data set {} ({} {})".format( self.counter.strip(" :"), modelshort, mddat[1]) boxti = wx.StaticBox(self.panelsettings, label=titlelabel) sizerti = wx.StaticBoxSizer(boxti, wx.VERTICAL) sizerti.SetMinSize((horizontalsize, -1)) self.tabtitle = wx.TextCtrl(self.panelsettings, value="", size=(horizontalsize-20, -1)) self.Bind(wx.EVT_TEXT, self.OnTitleChanged, self.tabtitle) sizerti.Add(self.tabtitle) # Create StaticBoxSizer box1, check, spin = self.MakeStaticBoxSizer("Model parameters") # Make the check boxes and spin-controls available everywhere self.checkboxes = check self.spincontrol = spin # # As of version 0.7.5: we want the units to be displayed # human readable - the way they are displayed # in the Page info tool. # labels, parameters = mdls.GetHumanReadableParms(modelid, self.active_parms[1]) parameterstofit = self.active_parms[2] # Set initial values given by user/programmer for Diffusion Model for i in np.arange(len(labels)): self.checkboxes[i].SetValue(parameterstofit[i]) self.spincontrol[i].SetValue(parameters[i]) # Put everything together self.panelsettings.sizer = wx.BoxSizer(wx.VERTICAL) self.panelsettings.sizer.Add(sizerti) self.panelsettings.sizer.Add(box1) # checkbox "Protect from batch control" self.wxCBPreventBatchParms = wx.CheckBox(self.panelsettings, -1, "prevent batch modification") box1.Add(self.wxCBPreventBatchParms) # buttons "Apply" and "Set range" horzs = wx.BoxSizer(wx.HORIZONTAL) buttonapply = wx.Button(self.panelsettings, label="Apply") self.Bind(wx.EVT_BUTTON, self.PlotAll, buttonapply) horzs.Add(buttonapply) buttonrange = wx.Button(self.panelsettings, label="Set range") self.Bind(wx.EVT_BUTTON, self.OnSetRange, buttonrange) horzs.Add(buttonrange) box1.Add(horzs) # Set horizontal size box1.SetMinSize((horizontalsize, -1)) ## More info normbox = wx.StaticBox(self.panelsettings, label="Amplitude corrections") miscsizer = wx.StaticBoxSizer(normbox, wx.VERTICAL) miscsizer.SetMinSize((horizontalsize, -1)) # Intensities and Background sizeint = wx.FlexGridSizer(rows=3, cols=3, vgap=5, hgap=5) sizeint.Add(wx.StaticText(self.panelsettings, label="[kHz]")) sizeint.Add(wx.StaticText(self.panelsettings, label="Intensity")) sizeint.Add(wx.StaticText(self.panelsettings, label="Background")) sizeint.Add(wx.StaticText(self.panelsettings, label="Ch1")) intlabel1 = wx.TextCtrl(self.panelsettings) bgspin1 = wxutils.PCFFloatTextCtrl(self.panelsettings) bgspin1.Bind(wx.EVT_KILL_FOCUS, self.OnBGSpinChanged) bgspin1.Bind(wx.EVT_TEXT_ENTER, self.OnBGSpinChanged) sizeint.Add(intlabel1) intlabel1.SetEditable(False) sizeint.Add(bgspin1) chtext2 = wx.StaticText(self.panelsettings, label="Ch2") sizeint.Add(chtext2) intlabel2 = wx.TextCtrl(self.panelsettings) intlabel2.SetEditable(False) bgspin2 = wxutils.PCFFloatTextCtrl(self.panelsettings) bgspin2.Bind(wx.EVT_KILL_FOCUS, self.OnBGSpinChanged) sizeint.Add(intlabel2) sizeint.Add(bgspin2) miscsizer.Add(sizeint) ## Normalize to n? textnor = wx.StaticText(self.panelsettings, label="Plot normalization") miscsizer.Add(textnor) normtoNDropdown = wx.ComboBox(self.panelsettings) self.Bind(wx.EVT_COMBOBOX, self.PlotAll, normtoNDropdown) miscsizer.Add(normtoNDropdown) self.AmplitudeInfo = [ [intlabel1, intlabel2], [bgspin1, bgspin2], normtoNDropdown, textnor] self.WXAmplitudeCCOnlyStuff = [chtext2, intlabel2, bgspin2] self.panelsettings.sizer.Add(miscsizer) ## Add fitting Box fitbox = wx.StaticBox(self.panelsettings, label="Fitting options") fitsizer = wx.StaticBoxSizer(fitbox, wx.VERTICAL) fitsizer.SetMinSize((horizontalsize, -1)) # Add a checkbox for weighted fitting weightedfitdrop = wx.ComboBox(self.panelsettings) self.weightlist = ["no weights", "spline (5 knots)", "model function"] weightedfitdrop.SetItems(self.weightlist) weightedfitdrop.SetSelection(0) fitsizer.Add(weightedfitdrop) # WeightedFitCheck() Enables or Disables the variance part weightedfitdrop.Bind(wx.EVT_COMBOBOX, self.Fit_WeightedFitCheck) # Add the variance part. # In order to do a weighted fit, we need to calculate the variance # at each point of the experimental data array. # In order to do that, we need to know how many data points from left # and right of the interesting data point we want to include in that # calculation. fittext = wx.StaticText(self.panelsettings, label="Calculation of the variance") fitsizer.Add(fittext) fittext2 = wx.StaticText(self.panelsettings, label="from 2j+1 data points") fitsizer.Add(fittext2) fitsizerspin = wx.BoxSizer(wx.HORIZONTAL) fittextvar = wx.StaticText(self.panelsettings, label="j = ") fitspin = wx.SpinCtrl(self.panelsettings, -1, initial=3, min=1, max=100) fitsizerspin.Add(fittextvar) fitsizerspin.Add(fitspin) fitsizer.Add(fitsizerspin) # Add algorithm selection textalg = wx.StaticText(self.panelsettings, label="Algorithm") fitsizer.Add(textalg) self.AlgorithmDropdown = wx.ComboBox(self.panelsettings) items = fit.GetAlgorithmStringList()[1] self.AlgorithmDropdown.SetItems(items) self.Bind(wx.EVT_COMBOBOX, self.apply_parameters, self.AlgorithmDropdown) fitsizer.Add(self.AlgorithmDropdown) self.AlgorithmDropdown.SetMaxSize(weightedfitdrop.GetSize()) # Add button "Fit" and chi2 display fitbuttonsizer = wx.BoxSizer(wx.HORIZONTAL) buttonfit = wx.Button(self.panelsettings, label="Fit") self.Bind(wx.EVT_BUTTON, self.Fit_function, buttonfit) fitbuttonsizer.Add(buttonfit) self.WXTextChi2 = wx.StaticText(self.panelsettings) # this StaticText is updated by `self.updateChi2()` fitbuttonsizer.Add(self.WXTextChi2, flag=wx.ALIGN_CENTER) fitsizer.Add(fitbuttonsizer) self.panelsettings.sizer.Add(fitsizer) # Squeeze everything into the sizer self.panelsettings.SetSizer(self.panelsettings.sizer) # This is also necessary in Windows self.panelsettings.Layout() self.panelsettings.Show() # Make all the stuff available for everyone self.Fitbox = [ fitbox, weightedfitdrop, fittext, fittext2, fittextvar, fitspin, buttonfit, textalg, self.AlgorithmDropdown] # Disable Fitting since no data has been loaded yet for element in self.Fitbox: element.Disable() self.panelsettings.sizer.Fit(self.panelsettings) self.parent.Layout() def updateChi2(self): """ updates the self.WXTextChi2 text control """ label = u"" if hasattr(self.corr, "fit_results"): if "chi2" in self.corr.fit_results: chi2 = self.corr.fit_results["chi2"] chi2str = wxutils.float2string_nsf(chi2, n=3) chi2str = wxutils.nice_string(chi2str) label = u" χ²={}".format(chi2str) # This does not work with wxPython 2.8.12: #self.WXTextChi2.SetLabelMarkup(u"<b>{}</b>".format(label)) self.WXTextChi2.SetLabel(u"{}".format(label))