def __init__(self, parent, id): from sas.sascalc.pr.invertor import help wx.Dialog.__init__(self, parent, id, size=(250, 120)) self.SetTitle("P(r) distribution") vbox = wx.BoxSizer(wx.VERTICAL) label_npts = wx.StaticText(self, -1, "Number of points") self.npts_ctl = PrTextCtrl(self, -1, size=(100, 20)) pars_sizer = wx.GridBagSizer(5, 5) iy = 0 pars_sizer.Add(label_npts, (iy, 0), (1, 1), wx.LEFT, 15) pars_sizer.Add(self.npts_ctl, (iy, 1), (1, 1), wx.RIGHT, 0) vbox.Add(pars_sizer, 0, wx.ALL | wx.EXPAND, 15) static_line = wx.StaticLine(self, -1) vbox.Add(static_line, 0, wx.EXPAND, 0) button_ok = wx.Button(self, wx.ID_OK, "OK") self.Bind(wx.EVT_BUTTON, self._checkValues, button_ok) button_cancel = wx.Button(self, wx.ID_CANCEL, "Cancel") sizer_button = wx.BoxSizer(wx.HORIZONTAL) sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0) sizer_button.Add(button_ok, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10) sizer_button.Add(button_cancel, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10) vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10) self.SetSizer(vbox) self.SetAutoLayout(True) self.Layout() self.Centre()
def __init__(self, pr_state, nfunc, *args, **kwds): """ Initialization. The parameters added to Dialog are: :param pr_state: sas.pr.invertor.Invertor object :param nfunc: Number of terms in the expansion """ kwds["style"] = wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE wx.Dialog.__init__(self, *args, **kwds) # Initialize Results object self.results = Results() self.pr_state = pr_state self._default_min = 0.9 * self.pr_state.d_max self._default_max = 1.1 * self.pr_state.d_max self.nfunc = nfunc # Control for number of points self.npts_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) # Control for the minimum value of D_max self.dmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) # Control for the maximum value of D_max self.dmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) # Output selection box for the y axis self.output_box = None # Create the plot object self.plotpanel = OutputPlot(self._default_min, self._default_max, self, -1, style=wx.RAISED_BORDER) # Create the layout of the dialog self.__do_layout() self.Fit() # Calculate exploration results self._recalc() # Graph the default output curve self._plot_output()
class ExploreDialog(wx.Dialog): """ The explorer dialog box. This dialog is meant to be invoked by the InversionControl class. """ def __init__(self, pr_state, nfunc, *args, **kwds): """ Initialization. The parameters added to Dialog are: :param pr_state: sas.pr.invertor.Invertor object :param nfunc: Number of terms in the expansion """ kwds["style"] = wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE wx.Dialog.__init__(self, *args, **kwds) # Initialize Results object self.results = Results() self.pr_state = pr_state self._default_min = 0.9 * self.pr_state.d_max self._default_max = 1.1 * self.pr_state.d_max self.nfunc = nfunc # Control for number of points self.npts_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) # Control for the minimum value of D_max self.dmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) # Control for the maximum value of D_max self.dmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) # Output selection box for the y axis self.output_box = None # Create the plot object self.plotpanel = OutputPlot(self._default_min, self._default_max, self, -1, style=wx.RAISED_BORDER) # Create the layout of the dialog self.__do_layout() self.Fit() # Calculate exploration results self._recalc() # Graph the default output curve self._plot_output() class Event(object): """ Class that holds the content of the form """ ## Number of points to be plotted npts = 0 ## Minimum value of D_max dmin = 0 ## Maximum value of D_max dmax = 0 def _get_values(self, event=None): """ Invoked when the user changes a value of the form. Check that the values are of the right type. :return: ExploreDialog.Event object if the content is good, None otherwise """ # Flag to make sure that all values are good flag = True # Empty ExploreDialog.Event content content_event = self.Event() # Read each text control and make sure the type is valid # Let the user know if a type is invalid by changing the # background color of the control. try: content_event.npts = int(self.npts_ctl.GetValue()) self.npts_ctl.SetBackgroundColour(wx.WHITE) self.npts_ctl.Refresh() except: flag = False self.npts_ctl.SetBackgroundColour("pink") self.npts_ctl.Refresh() try: content_event.dmin = float(self.dmin_ctl.GetValue()) self.dmin_ctl.SetBackgroundColour(wx.WHITE) self.dmin_ctl.Refresh() except: flag = False self.dmin_ctl.SetBackgroundColour("pink") self.dmin_ctl.Refresh() try: content_event.dmax = float(self.dmax_ctl.GetValue()) self.dmax_ctl.SetBackgroundColour(wx.WHITE) self.dmax_ctl.Refresh() except: flag = False self.dmax_ctl.SetBackgroundColour("pink") self.dmax_ctl.Refresh() # If the content of the form is valid, return the content, # otherwise return None if flag: if event is not None: event.Skip(True) return content_event else: return None def _plot_output(self, event=None): """ Invoked when a new output type is selected for plotting, or when a new computation is finished. """ # Get the output type selection output_type = self.output_box.GetString(self.output_box.GetSelection()) # If the selected output type is part of the results ojbect, # display the results. # Note: by design, the output type should always be part of the # results object. if self.results.outputs.has_key(output_type): self.plotpanel.plot.x = self.results.d_max self.plotpanel.plot.y = self.results.outputs[output_type][2] self.plotpanel.plot.name = '_nolegend_' y_label = "\\rm{%s}" % self.results.outputs[output_type][0] self.plotpanel.graph.yaxis(y_label, self.results.outputs[output_type][1]) # Redraw self.plotpanel.graph.render(self.plotpanel) self.plotpanel.subplot.figure.canvas.draw_idle() else: msg = "ExploreDialog: the Results object's dictionary " msg += "does not contain " msg += "the [%s] output type. This must be indicative of " msg += "a change in the " % str(output_type) msg += "ExploreDialog code." logging.error(msg) def __do_layout(self): """ Do the layout of the dialog """ # Dialog box properties self.SetTitle("D_max Explorer") self.SetSize((600, 595)) sizer_main = wx.BoxSizer(wx.VERTICAL) sizer_button = wx.BoxSizer(wx.HORIZONTAL) sizer_params = wx.GridBagSizer(5, 5) iy = 0 ix = 0 label_npts = wx.StaticText(self, -1, "Npts") sizer_params.Add(label_npts, (iy, ix), (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) ix += 1 sizer_params.Add(self.npts_ctl, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0) self.npts_ctl.SetValue("%g" % DEFAULT_NPTS) ix += 1 label_dmin = wx.StaticText(self, -1, "Min Distance [A]") sizer_params.Add(label_dmin, (iy, ix), (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) ix += 1 sizer_params.Add(self.dmin_ctl, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0) self.dmin_ctl.SetValue(str(self._default_min)) ix += 1 label_dmax = wx.StaticText(self, -1, "Max Distance [A]") sizer_params.Add(label_dmax, (iy, ix), (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) ix += 1 sizer_params.Add(self.dmax_ctl, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0) self.dmax_ctl.SetValue(str(self._default_max)) # Ouput selection box selection_msg = wx.StaticText(self, -1, "Select a dependent variable:") self.output_box = wx.ComboBox(self, -1, style=wx.CB_READONLY) for item in self.results.outputs.keys(): self.output_box.Append(item, "") self.output_box.SetStringSelection(DEFAULT_OUTPUT) output_sizer = wx.GridBagSizer(5, 5) output_sizer.Add(selection_msg, (0, 0), (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10) output_sizer.Add(self.output_box, (0, 1), (1, 2), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10) wx.EVT_COMBOBOX(self.output_box, -1, self._plot_output) sizer_main.Add(output_sizer, 0, wx.EXPAND | wx.ALL, 10) sizer_main.Add(self.plotpanel, 0, wx.EXPAND | wx.ALL, 10) sizer_main.SetItemMinSize(self.plotpanel, 400, 400) sizer_main.Add(sizer_params, 0, wx.EXPAND | wx.ALL, 10) static_line_3 = wx.StaticLine(self, -1) sizer_main.Add(static_line_3, 0, wx.EXPAND, 0) # Bottom area with the close button sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0) button_OK = wx.Button(self, wx.ID_OK, "Close") sizer_button.Add(button_OK, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10) sizer_main.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10) self.SetAutoLayout(True) self.SetSizer(sizer_main) self.Layout() self.Centre() # Bind the Enter key to recalculation self.Bind(wx.EVT_TEXT_ENTER, self._recalc) def set_plot_unfocus(self): """ Not implemented """ pass def send_focus_to_datapanel(self, name): """ The GUI manager sometimes calls this method TODO: refactor this """ pass def _recalc(self, event=None): """ Invoked when the user changed a value on the form. Process the form and compute the output to be plottted. """ # Get the content of the form content = self._get_values() # If the content of the form is invalid, return and do nothing if content is None: return # Results object to store the computation outputs. results = Results() # Loop over d_max values for i in range(content.npts): temp = (content.dmax - content.dmin) / (content.npts - 1.0) d = content.dmin + i * temp self.pr_state.d_max = d try: out, cov = self.pr_state.invert(self.nfunc) # Store results iq0 = self.pr_state.iq0(out) rg = self.pr_state.rg(out) pos = self.pr_state.get_positive(out) pos_err = self.pr_state.get_pos_err(out, cov) osc = self.pr_state.oscillations(out) results.d_max.append(self.pr_state.d_max) results.bck.append(self.pr_state.background) results.chi2.append(self.pr_state.chi2) results.iq0.append(iq0) results.rg.append(rg) results.pos.append(pos) results.pos_err.append(pos_err) results.osc.append(osc) except: # This inversion failed, skip this D_max value msg = "ExploreDialog: inversion failed " msg += "for D_max=%s\n%s" % (str(d), sys.exc_value) logging.error(msg) self.results = results # Plot the selected output self._plot_output()
def _do_layout(self): vbox = wx.GridBagSizer(0, 0) iy_vb = 0 # ----- I(q) data ----- databox = wx.StaticBox(self, -1, "I(q) data source") boxsizer1 = wx.StaticBoxSizer(databox, wx.VERTICAL) boxsizer1.SetMinSize((self._default_width, 50)) pars_sizer = wx.GridBagSizer(5, 5) iy = 0 self.file_radio = wx.StaticText(self, -1, "Name:") pars_sizer.Add(self.file_radio, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) self.plot_data = DataFileTextCtrl(self, -1, size=(260, 20)) pars_sizer.Add(self.plot_data, (iy, 1), (1, 1), wx.EXPAND | wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 15) self.bck_chk = wx.CheckBox(self, -1, "Estimate background level") hint_msg = "Check box to let the fit estimate " hint_msg += "the constant background level." self.bck_chk.SetToolTipString(hint_msg) self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed) iy += 1 pars_sizer.Add(self.bck_chk, (iy, 0), (1, 2), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) boxsizer1.Add(pars_sizer, 0, wx.EXPAND) vbox.Add(boxsizer1, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE | wx.TOP, 5) # ----- Add slit parameters ----- if True: sbox = wx.StaticBox(self, -1, "Slit parameters") sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL) sboxsizer.SetMinSize((self._default_width, 20)) sizer_slit = wx.GridBagSizer(5, 5) label_sheight = wx.StaticText(self, -1, "Height", size=(40, 20)) label_swidth = wx.StaticText(self, -1, "Width", size=(40, 20)) label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20)) self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) hint_msg = "Enter slit height in units of Q or leave blank." self.sheight_ctl.SetToolTipString(hint_msg) hint_msg = "Enter slit width in units of Q or leave blank." self.swidth_ctl.SetToolTipString(hint_msg) iy = 0 sizer_slit.Add(label_sheight, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_slit.Add(self.sheight_ctl, (iy, 1), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_slit.Add(label_swidth, (iy, 2), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_slit.Add(self.swidth_ctl, (iy, 3), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_slit.Add(label_sunits2, (iy, 4), (1, 1), wx.LEFT | wx.EXPAND, 5) sboxsizer.Add(sizer_slit, wx.TOP, 15) iy_vb += 1 vbox.Add(sboxsizer, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5) # ----- Q range ----- qbox = wx.StaticBox(self, -1, "Q range") qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL) qboxsizer.SetMinSize((self._default_width, 20)) sizer_q = wx.GridBagSizer(5, 5) label_qmin = wx.StaticText(self, -1, "Q min", size=(40, 20)) label_qmax = wx.StaticText(self, -1, "Q max", size=(40, 20)) label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20)) self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) hint_msg = "Select a lower bound for Q or leave blank." self.qmin_ctl.SetToolTipString(hint_msg) hint_msg = "Select an upper bound for Q or leave blank." self.qmax_ctl.SetToolTipString(hint_msg) self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed) self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed) iy = 0 sizer_q.Add(label_qmin, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_q.Add(self.qmin_ctl, (iy, 1), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_q.Add(label_qmax, (iy, 2), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_q.Add(self.qmax_ctl, (iy, 3), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_q.Add(label_qunits2, (iy, 4), (1, 1), wx.LEFT | wx.EXPAND, 5) qboxsizer.Add(sizer_q, wx.TOP, 15) iy_vb += 1 vbox.Add(qboxsizer, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5) # ----- Parameters ----- parsbox = wx.StaticBox(self, -1, "Parameters") boxsizer2 = wx.StaticBoxSizer(parsbox, wx.VERTICAL) boxsizer2.SetMinSize((self._default_width, 50)) explanation = "P(r) is found by fitting a set of base functions" explanation += " to I(Q). The minimization involves" explanation += " a regularization term to ensure a smooth P(r)." explanation += " The regularization constant gives the size of that " explanation += "term. The suggested value is the value above which the" explanation += " output P(r) will have only one peak." label_explain = wx.StaticText(self, -1, explanation, size=(280, 90)) boxsizer2.Add(label_explain, wx.LEFT | wx.BOTTOM, 5) label_nfunc = wx.StaticText(self, -1, "Number of terms") label_nfunc.SetMinSize((120, 20)) label_alpha = wx.StaticText(self, -1, "Regularization constant") label_dmax = wx.StaticText(self, -1, "Max distance [A]") self.label_sugg = wx.StaticText(self, -1, "Suggested value") self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.") self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) hint_msg = "Control parameter for the size of the regularization term." self.alpha_ctl.SetToolTipString(hint_msg) self.dmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) hint_msg = "Maximum distance between any two points in the system." self.dmax_ctl.SetToolTipString(hint_msg) wx_id = wx.NewId() self.alpha_estimate_ctl = wx.Button(self, wx_id, "") self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id=wx_id) self.alpha_estimate_ctl.Enable(False) self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...") wx_id = wx.NewId() self.nterms_estimate_ctl = wx.Button(self, wx_id, "") #self.nterms_estimate_ctl.Hide() self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id=wx_id) self.nterms_estimate_ctl.Enable(False) self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...") self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars) self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars) self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed) # Distance explorator wx_id = wx.NewId() self.distance_explorator_ctl = wx.Button(self, wx_id, "Explore") self.Bind(wx.EVT_BUTTON, self._on_explore, id=wx_id) sizer_params = wx.GridBagSizer(5, 5) iy = 0 sizer_params.Add(self.label_sugg, (iy, 2), (1, 1), wx.LEFT, 15) iy += 1 sizer_params.Add(label_nfunc, (iy, 0), (1, 1), wx.LEFT, 15) sizer_params.Add(self.nfunc_ctl, (iy, 1), (1, 1), wx.RIGHT, 0) sizer_params.Add(self.nterms_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15) iy += 1 sizer_params.Add(label_alpha, (iy, 0), (1, 1), wx.LEFT, 15) sizer_params.Add(self.alpha_ctl, (iy, 1), (1, 1), wx.RIGHT, 0) sizer_params.Add(self.alpha_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15) iy += 1 sizer_params.Add(label_dmax, (iy, 0), (1, 1), wx.LEFT, 15) sizer_params.Add(self.dmax_ctl, (iy, 1), (1, 1), wx.RIGHT, 0) sizer_params.Add(self.distance_explorator_ctl, (iy, 2), (1, 1), wx.LEFT, 15) boxsizer2.Add(sizer_params, 0) iy_vb += 1 vbox.Add(boxsizer2, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5) # ----- Results ----- resbox = wx.StaticBox(self, -1, "Outputs") ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL) ressizer.SetMinSize((self._default_width, 50)) label_rg = wx.StaticText(self, -1, "Rg") label_rg_unit = wx.StaticText(self, -1, "[A]") label_iq0 = wx.StaticText(self, -1, "I(Q=0)") label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]") label_bck = wx.StaticText(self, -1, "Background") label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]") self.rg_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Radius of gyration for the computed P(r)." self.rg_ctl.SetToolTipString(hint_msg) self.iq0_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Scattering intensity at Q=0 for the computed P(r)." self.iq0_ctl.SetToolTipString(hint_msg) self.bck_ctl = OutputTextCtrl(self, -1, size=(60, 20)) self.bck_ctl.SetToolTipString( "Value of estimated constant background.") label_time = wx.StaticText(self, -1, "Computation time") label_time_unit = wx.StaticText(self, -1, "secs") label_time.SetMinSize((120, 20)) label_chi2 = wx.StaticText(self, -1, "Chi2/dof") label_osc = wx.StaticText(self, -1, "Oscillations") label_pos = wx.StaticText(self, -1, "Positive fraction") label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction") self.time_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Computation time for the last inversion, in seconds." self.time_ctl.SetToolTipString(hint_msg) self.chi2_ctl = OutputTextCtrl(self, -1, size=(60, 20)) self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.") # Oscillation parameter self.osc_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Oscillation parameter. P(r) for a sphere has an " hint_msg += " oscillation parameter of 1.1." self.osc_ctl.SetToolTipString(hint_msg) # Positive fraction figure of merit self.pos_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Fraction of P(r) that is positive. " hint_msg += "Theoretically, P(r) is defined positive." self.pos_ctl.SetToolTipString(hint_msg) # 1-simga positive fraction figure of merit self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60, 20)) message = "Fraction of P(r) that is at least 1 standard deviation" message += " greater than zero.\n" message += "This figure of merit tells you about the size of the " message += "P(r) errors.\n" message += "If it is close to 1 and the other figures of merit are bad," message += " consider changing the maximum distance." self.pos_err_ctl.SetToolTipString(message) sizer_res = wx.GridBagSizer(5, 5) iy = 0 sizer_res.Add(label_rg, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.rg_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) sizer_res.Add(label_rg_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_iq0, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.iq0_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) sizer_res.Add(label_iq0_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_bck, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.bck_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) sizer_res.Add(label_bck_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_time, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.time_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) sizer_res.Add(label_time_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_chi2, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.chi2_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_osc, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.osc_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_pos, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.pos_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_pos_err, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.pos_err_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) ressizer.Add(sizer_res, 0) iy_vb += 1 vbox.Add(ressizer, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5) # ----- Buttons ----- wx_id = wx.NewId() button_ok = wx.Button(self, wx_id, "Compute") button_ok.SetToolTipString("Perform P(r) inversion.") self.Bind(wx.EVT_BUTTON, self._on_invert, id=wx_id) self.button_help = wx.Button(self, -1, "HELP") self.button_help.SetToolTipString("Get help on P(r) inversion.") self.button_help.Bind(wx.EVT_BUTTON, self.on_help) self._set_reset_flag(True) self._set_save_flag(True) sizer_button = wx.BoxSizer(wx.HORIZONTAL) sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0) sizer_button.Add(button_ok, 0, wx.LEFT | wx.ADJUST_MINSIZE, 10) sizer_button.Add(self.button_help, 0, wx.LEFT | wx.ADJUST_MINSIZE, 10) iy_vb += 1 vbox.Add(sizer_button, (iy_vb, 0), (1, 1), wx.EXPAND | wx.BOTTOM | wx.TOP | wx.RIGHT, 10) self.Bind(wx.EVT_TEXT_ENTER, self._on_invert) self.SetSizer(vbox)
class InversionControl(ScrolledPanel, PanelBase): """ """ window_name = 'pr_control' window_caption = "P(r) control panel" CENTER_PANE = True # Figure of merit parameters [default] ## Oscillation parameters (sin function = 1.1) oscillation_max = 1.5 def __init__(self, parent, id=-1, plots=None, **kwargs): """ """ ScrolledPanel.__init__(self, parent, id=id, **kwargs) PanelBase.__init__(self, parent) self.SetupScrolling() #Set window's font size self.SetWindowVariant(variant=FONT_VARIANT) self._set_analysis(False) self.plots = plots self.radio_buttons = {} self.parent = parent.parent ## Data file TextCtrl self.data_file = None self.plot_data = None self.nfunc_ctl = None self.alpha_ctl = None self.dmax_ctl = None self.time_ctl = None self.chi2_ctl = None self.osc_ctl = None self.file_radio = None self.plot_radio = None self.label_sugg = None self.qmin_ctl = None self.qmax_ctl = None self.swidth_ctl = None self.sheight_ctl = None self.rg_ctl = None self.iq0_ctl = None self.bck_chk = None self.bck_ctl = None # TextCtrl for fraction of positive P(r) self.pos_ctl = None # TextCtrl for fraction of 1 sigma positive P(r) self.pos_err_ctl = None ## Estimates self.alpha_estimate_ctl = None self.nterms_estimate_ctl = None ## D_max distance explorator self.distance_explorator_ctl = None ## Data manager self._manager = None ## Default file location for save self._default_save_location = os.getcwd() if self.parent is not None: self._default_save_location = \ self.parent._default_save_location # Default width self._default_width = 350 self._do_layout() def __setattr__(self, name, value): """ Allow direct hooks to text boxes """ if name == 'nfunc': self.nfunc_ctl.SetValue(str(int(value))) elif name == 'd_max': self.dmax_ctl.SetValue(str(value)) elif name == 'alpha': self.alpha_ctl.SetValue(str(value)) elif name == 'chi2': self.chi2_ctl.SetValue("%-5.2g" % value) elif name == 'bck': self.bck_ctl.SetValue("%-5.2g" % value) elif name == 'q_min': self.qmin_ctl.SetValue("%-5.2g" % value) elif name == 'q_max': self.qmax_ctl.SetValue("%-5.2g" % value) elif name == 'elapsed': self.time_ctl.SetValue("%-5.2g" % value) elif name == 'rg': self.rg_ctl.SetValue("%-5.2g" % value) elif name == 'iq0': self.iq0_ctl.SetValue("%-5.2g" % value) elif name == 'oscillation': self.osc_ctl.SetValue("%-5.2g" % value) elif name == 'slit_width': self.swidth_ctl.SetValue("%-5.2g" % value) elif name == 'slit_height': self.sheight_ctl.SetValue("%-5.2g" % value) elif name == 'positive': self.pos_ctl.SetValue("%-5.2g" % value) elif name == 'pos_err': self.pos_err_ctl.SetValue("%-5.2g" % value) elif name == 'alpha_estimate': self.alpha_estimate_ctl.SetToolTipString("Click to accept value.") self.alpha_estimate_ctl.Enable(True) self.alpha_estimate_ctl.SetLabel("%-3.1g" % value) #self.alpha_estimate_ctl.Show() #self.label_sugg.Show() elif name == 'nterms_estimate': self.nterms_estimate_ctl.SetToolTipString("Click to accept value.") self.nterms_estimate_ctl.Enable(True) self.nterms_estimate_ctl.SetLabel("%-g" % value) elif name == 'plotname': self.plot_data.SetValue(str(value)) self._on_pars_changed(None) elif name == 'datafile': self.plot_data.SetValue(str(value)) self._on_pars_changed(None) else: wx.Panel.__setattr__(self, name, value) def __getattr__(self, name): """ Allow direct hooks to text boxes """ if name == 'nfunc': try: return int(self.nfunc_ctl.GetValue()) except: return -1 elif name == 'd_max': try: return self.dmax_ctl.GetValue() except: return -1.0 elif name == 'alpha': try: return self.alpha_ctl.GetValue() except: return -1.0 elif name == 'chi2': try: return float(self.chi2_ctl.GetValue()) except: return None elif name == 'bck': try: return float(self.bck_ctl.GetValue()) except: return None elif name == 'q_min': try: return float(self.qmin_ctl.GetValue()) except: return 0.0 elif name == 'q_max': try: return float(self.qmax_ctl.GetValue()) except: return 0.0 elif name == 'elapsed': try: return float(self.time_ctl.GetValue()) except: return None elif name == 'rg': try: return float(self.rg_ctl.GetValue()) except: return None elif name == 'iq0': try: return float(self.iq0_ctl.GetValue()) except: return None elif name == 'oscillation': try: return float(self.osc_ctl.GetValue()) except: return None elif name == 'slit_width': try: return float(self.swidth_ctl.GetValue()) except: return None elif name == 'slit_height': try: return float(self.sheight_ctl.GetValue()) except: return None elif name == 'pos': try: return float(self.pos_ctl.GetValue()) except: return None elif name == 'pos_err': try: return float(self.pos_err_ctl.GetValue()) except: return None elif name == 'alpha_estimate': try: return float(self.alpha_estimate_ctl.GetLabel()) except: return None elif name == 'nterms_estimate': try: return int(self.nterms_estimate_ctl.GetLabel()) except: return None elif name == 'plotname': return self.plot_data.GetValue() elif name == 'datafile': return self.plot_data.GetValue() else: return wx.Panel.__getattribute__(self, name) def save_project(self, doc=None): """ return an xml node containing state of the panel that guiframe can write to file """ data = self.get_data() state = self.get_state() if data is not None: new_doc = self._manager.state_reader.write_toXML(data, state) if new_doc is not None: if doc is not None and hasattr(doc, "firstChild"): child = new_doc.getElementsByTagName("SASentry") for item in child: doc.firstChild.appendChild(item) else: doc = new_doc return doc def on_save(self, evt=None): """ Method used to create a memento of the current state :return: state object """ # Ask the user the location of the file to write to. path = None if self.parent is not None: self._default_save_location = self.parent._default_save_location dlg = wx.FileDialog(self, "Choose a file", self._default_save_location, self.window_caption, "*.prv", wx.SAVE) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() self._default_save_location = os.path.dirname(path) if self.parent is not None: self.parent._default_save_location = self._default_save_location else: return None dlg.Destroy() state = self.get_state() # MAC always needs the extension for saving extens = ".prv" # Make sure the ext included in the file name fName = os.path.splitext(path)[0] + extens self._manager.save_data(filepath=fName, prstate=state) return state def get_data(self): """ """ return self._manager.get_data() def get_state(self): """ Get the current state : return: state object """ # Construct the state object state = InversionState() # Read the panel's parameters flag, alpha, dmax, nfunc, qmin, \ qmax, height, width = self._read_pars() state.nfunc = nfunc state.d_max = dmax state.alpha = alpha state.qmin = qmin state.qmax = qmax state.width = width state.height = height # Data file state.file = self.plot_data.GetValue() # Background evaluation checkbox state.estimate_bck = self.bck_chk.IsChecked() # Estimates state.nterms_estimate = self.nterms_estimate state.alpha_estimate = self.alpha_estimate # Read the output values state.chi2 = self.chi2 state.elapsed = self.elapsed state.osc = self.oscillation state.pos = self.pos state.pos_err = self.pos_err state.rg = self.rg state.iq0 = self.iq0 state.bck = self.bck return state def set_state(self, state): """ Set the state of the panel and inversion problem to the state passed as a parameter. Execute the inversion immediately after filling the controls. :param state: InversionState object """ if state.nfunc is not None: self.nfunc = state.nfunc if state.d_max is not None: self.d_max = state.d_max if state.alpha is not None: self.alpha = state.alpha if state.qmin is not None: self.q_min = state.qmin if state.qmax is not None: self.q_max = state.qmax if state.width is not None: self.slit_width = state.width if state.height is not None: self.slit_height = state.height # Data file self.plot_data.SetValue(str(state.file)) # Background evaluation checkbox self.bck_chk.SetValue(state.estimate_bck) # Estimates if state.nterms_estimate is not None: self.nterms_estimate = state.nterms_estimate if state.alpha_estimate is not None: self.alpha_estimate = state.alpha_estimate # Read the output values if state.chi2 is not None: self.chi2 = state.chi2 if state.elapsed is not None: self.elapsed = state.elapsed if state.osc is not None: self.oscillation = state.osc if state.pos is not None: self.positive = state.pos if state.pos_err is not None: self.pos_err = state.pos_err if state.rg is not None: self.rg = state.rg if state.iq0 is not None: self.iq0 = state.iq0 if state.bck is not None: self.bck = state.bck # We have the data available for serialization self._set_analysis(True) # Perform inversion self._on_invert(None) def set_manager(self, manager): self._manager = manager if manager is not None: self._set_analysis(False) def _do_layout(self): vbox = wx.GridBagSizer(0, 0) iy_vb = 0 # ----- I(q) data ----- databox = wx.StaticBox(self, -1, "I(q) data source") boxsizer1 = wx.StaticBoxSizer(databox, wx.VERTICAL) boxsizer1.SetMinSize((self._default_width, 50)) pars_sizer = wx.GridBagSizer(5, 5) iy = 0 self.file_radio = wx.StaticText(self, -1, "Name:") pars_sizer.Add(self.file_radio, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) self.plot_data = DataFileTextCtrl(self, -1, size=(260, 20)) pars_sizer.Add(self.plot_data, (iy, 1), (1, 1), wx.EXPAND | wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 15) self.bck_chk = wx.CheckBox(self, -1, "Estimate background level") hint_msg = "Check box to let the fit estimate " hint_msg += "the constant background level." self.bck_chk.SetToolTipString(hint_msg) self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed) iy += 1 pars_sizer.Add(self.bck_chk, (iy, 0), (1, 2), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) boxsizer1.Add(pars_sizer, 0, wx.EXPAND) vbox.Add(boxsizer1, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE | wx.TOP, 5) # ----- Add slit parameters ----- if True: sbox = wx.StaticBox(self, -1, "Slit parameters") sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL) sboxsizer.SetMinSize((self._default_width, 20)) sizer_slit = wx.GridBagSizer(5, 5) label_sheight = wx.StaticText(self, -1, "Height", size=(40, 20)) label_swidth = wx.StaticText(self, -1, "Width", size=(40, 20)) label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20)) self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) hint_msg = "Enter slit height in units of Q or leave blank." self.sheight_ctl.SetToolTipString(hint_msg) hint_msg = "Enter slit width in units of Q or leave blank." self.swidth_ctl.SetToolTipString(hint_msg) iy = 0 sizer_slit.Add(label_sheight, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_slit.Add(self.sheight_ctl, (iy, 1), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_slit.Add(label_swidth, (iy, 2), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_slit.Add(self.swidth_ctl, (iy, 3), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_slit.Add(label_sunits2, (iy, 4), (1, 1), wx.LEFT | wx.EXPAND, 5) sboxsizer.Add(sizer_slit, wx.TOP, 15) iy_vb += 1 vbox.Add(sboxsizer, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5) # ----- Q range ----- qbox = wx.StaticBox(self, -1, "Q range") qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL) qboxsizer.SetMinSize((self._default_width, 20)) sizer_q = wx.GridBagSizer(5, 5) label_qmin = wx.StaticText(self, -1, "Q min", size=(40, 20)) label_qmax = wx.StaticText(self, -1, "Q max", size=(40, 20)) label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20)) self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) hint_msg = "Select a lower bound for Q or leave blank." self.qmin_ctl.SetToolTipString(hint_msg) hint_msg = "Select an upper bound for Q or leave blank." self.qmax_ctl.SetToolTipString(hint_msg) self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed) self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed) iy = 0 sizer_q.Add(label_qmin, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_q.Add(self.qmin_ctl, (iy, 1), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_q.Add(label_qmax, (iy, 2), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_q.Add(self.qmax_ctl, (iy, 3), (1, 1), wx.LEFT | wx.EXPAND, 5) sizer_q.Add(label_qunits2, (iy, 4), (1, 1), wx.LEFT | wx.EXPAND, 5) qboxsizer.Add(sizer_q, wx.TOP, 15) iy_vb += 1 vbox.Add(qboxsizer, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5) # ----- Parameters ----- parsbox = wx.StaticBox(self, -1, "Parameters") boxsizer2 = wx.StaticBoxSizer(parsbox, wx.VERTICAL) boxsizer2.SetMinSize((self._default_width, 50)) explanation = "P(r) is found by fitting a set of base functions" explanation += " to I(Q). The minimization involves" explanation += " a regularization term to ensure a smooth P(r)." explanation += " The regularization constant gives the size of that " explanation += "term. The suggested value is the value above which the" explanation += " output P(r) will have only one peak." label_explain = wx.StaticText(self, -1, explanation, size=(280, 90)) boxsizer2.Add(label_explain, wx.LEFT | wx.BOTTOM, 5) label_nfunc = wx.StaticText(self, -1, "Number of terms") label_nfunc.SetMinSize((120, 20)) label_alpha = wx.StaticText(self, -1, "Regularization constant") label_dmax = wx.StaticText(self, -1, "Max distance [A]") self.label_sugg = wx.StaticText(self, -1, "Suggested value") self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.") self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) hint_msg = "Control parameter for the size of the regularization term." self.alpha_ctl.SetToolTipString(hint_msg) self.dmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20)) hint_msg = "Maximum distance between any two points in the system." self.dmax_ctl.SetToolTipString(hint_msg) wx_id = wx.NewId() self.alpha_estimate_ctl = wx.Button(self, wx_id, "") self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id=wx_id) self.alpha_estimate_ctl.Enable(False) self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...") wx_id = wx.NewId() self.nterms_estimate_ctl = wx.Button(self, wx_id, "") #self.nterms_estimate_ctl.Hide() self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id=wx_id) self.nterms_estimate_ctl.Enable(False) self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...") self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars) self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars) self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed) # Distance explorator wx_id = wx.NewId() self.distance_explorator_ctl = wx.Button(self, wx_id, "Explore") self.Bind(wx.EVT_BUTTON, self._on_explore, id=wx_id) sizer_params = wx.GridBagSizer(5, 5) iy = 0 sizer_params.Add(self.label_sugg, (iy, 2), (1, 1), wx.LEFT, 15) iy += 1 sizer_params.Add(label_nfunc, (iy, 0), (1, 1), wx.LEFT, 15) sizer_params.Add(self.nfunc_ctl, (iy, 1), (1, 1), wx.RIGHT, 0) sizer_params.Add(self.nterms_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15) iy += 1 sizer_params.Add(label_alpha, (iy, 0), (1, 1), wx.LEFT, 15) sizer_params.Add(self.alpha_ctl, (iy, 1), (1, 1), wx.RIGHT, 0) sizer_params.Add(self.alpha_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15) iy += 1 sizer_params.Add(label_dmax, (iy, 0), (1, 1), wx.LEFT, 15) sizer_params.Add(self.dmax_ctl, (iy, 1), (1, 1), wx.RIGHT, 0) sizer_params.Add(self.distance_explorator_ctl, (iy, 2), (1, 1), wx.LEFT, 15) boxsizer2.Add(sizer_params, 0) iy_vb += 1 vbox.Add(boxsizer2, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5) # ----- Results ----- resbox = wx.StaticBox(self, -1, "Outputs") ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL) ressizer.SetMinSize((self._default_width, 50)) label_rg = wx.StaticText(self, -1, "Rg") label_rg_unit = wx.StaticText(self, -1, "[A]") label_iq0 = wx.StaticText(self, -1, "I(Q=0)") label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]") label_bck = wx.StaticText(self, -1, "Background") label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]") self.rg_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Radius of gyration for the computed P(r)." self.rg_ctl.SetToolTipString(hint_msg) self.iq0_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Scattering intensity at Q=0 for the computed P(r)." self.iq0_ctl.SetToolTipString(hint_msg) self.bck_ctl = OutputTextCtrl(self, -1, size=(60, 20)) self.bck_ctl.SetToolTipString( "Value of estimated constant background.") label_time = wx.StaticText(self, -1, "Computation time") label_time_unit = wx.StaticText(self, -1, "secs") label_time.SetMinSize((120, 20)) label_chi2 = wx.StaticText(self, -1, "Chi2/dof") label_osc = wx.StaticText(self, -1, "Oscillations") label_pos = wx.StaticText(self, -1, "Positive fraction") label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction") self.time_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Computation time for the last inversion, in seconds." self.time_ctl.SetToolTipString(hint_msg) self.chi2_ctl = OutputTextCtrl(self, -1, size=(60, 20)) self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.") # Oscillation parameter self.osc_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Oscillation parameter. P(r) for a sphere has an " hint_msg += " oscillation parameter of 1.1." self.osc_ctl.SetToolTipString(hint_msg) # Positive fraction figure of merit self.pos_ctl = OutputTextCtrl(self, -1, size=(60, 20)) hint_msg = "Fraction of P(r) that is positive. " hint_msg += "Theoretically, P(r) is defined positive." self.pos_ctl.SetToolTipString(hint_msg) # 1-simga positive fraction figure of merit self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60, 20)) message = "Fraction of P(r) that is at least 1 standard deviation" message += " greater than zero.\n" message += "This figure of merit tells you about the size of the " message += "P(r) errors.\n" message += "If it is close to 1 and the other figures of merit are bad," message += " consider changing the maximum distance." self.pos_err_ctl.SetToolTipString(message) sizer_res = wx.GridBagSizer(5, 5) iy = 0 sizer_res.Add(label_rg, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.rg_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) sizer_res.Add(label_rg_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_iq0, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.iq0_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) sizer_res.Add(label_iq0_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_bck, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.bck_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) sizer_res.Add(label_bck_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_time, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.time_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) sizer_res.Add(label_time_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_chi2, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.chi2_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_osc, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.osc_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_pos, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.pos_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) iy += 1 sizer_res.Add(label_pos_err, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15) sizer_res.Add(self.pos_err_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15) ressizer.Add(sizer_res, 0) iy_vb += 1 vbox.Add(ressizer, (iy_vb, 0), (1, 1), wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5) # ----- Buttons ----- wx_id = wx.NewId() button_ok = wx.Button(self, wx_id, "Compute") button_ok.SetToolTipString("Perform P(r) inversion.") self.Bind(wx.EVT_BUTTON, self._on_invert, id=wx_id) self.button_help = wx.Button(self, -1, "HELP") self.button_help.SetToolTipString("Get help on P(r) inversion.") self.button_help.Bind(wx.EVT_BUTTON, self.on_help) self._set_reset_flag(True) self._set_save_flag(True) sizer_button = wx.BoxSizer(wx.HORIZONTAL) sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0) sizer_button.Add(button_ok, 0, wx.LEFT | wx.ADJUST_MINSIZE, 10) sizer_button.Add(self.button_help, 0, wx.LEFT | wx.ADJUST_MINSIZE, 10) iy_vb += 1 vbox.Add(sizer_button, (iy_vb, 0), (1, 1), wx.EXPAND | wx.BOTTOM | wx.TOP | wx.RIGHT, 10) self.Bind(wx.EVT_TEXT_ENTER, self._on_invert) self.SetSizer(vbox) def _on_accept_alpha(self, evt): """ User has accepted the estimated alpha, set it as part of the input parameters """ try: alpha = self.alpha_estimate_ctl.GetLabel() # Check that we have a number float(alpha) self.alpha_ctl.SetValue(alpha) except ValueError: logger.error( "InversionControl._on_accept_alpha got a value that was not a number: %s" % alpha) except: # No estimate or bad estimate, either do nothing logger.error("InversionControl._on_accept_alpha: %s" % sys.exc_value) def _on_accept_nterms(self, evt): """ User has accepted the estimated number of terms, set it as part of the input parameters """ try: nterms = self.nterms_estimate_ctl.GetLabel() # Check that we have a number float(nterms) self.nfunc_ctl.SetValue(nterms) except ValueError: logger.error( "InversionControl._on_accept_nterms got a value that was not a number: %s" % nterms) except: # No estimate or bad estimate, either do nothing logger.error("InversionControl._on_accept_nterms: %s" % sys.exc_value) def clear_panel(self): """ """ self.plot_data.SetValue("") self.on_reset(event=None) def on_reset(self, event=None): """ Resets inversion parameters """ self.nfunc = self._manager.DEFAULT_NFUNC self.d_max = self._manager.DEFAULT_DMAX self.alpha = self._manager.DEFAULT_ALPHA self.qmin_ctl.SetValue("") self.qmax_ctl.SetValue("") self.time_ctl.SetValue("") self.rg_ctl.SetValue("") self.iq0_ctl.SetValue("") self.bck_ctl.SetValue("") self.chi2_ctl.SetValue("") self.osc_ctl.SetValue("") self.pos_ctl.SetValue("") self.pos_err_ctl.SetValue("") self.alpha_estimate_ctl.Enable(False) self.alpha_estimate_ctl.SetLabel("") self.nterms_estimate_ctl.Enable(False) self.nterms_estimate_ctl.SetLabel("") self._set_analysis(False) self._on_pars_changed() def _on_pars_changed(self, evt=None): """ Called when an input parameter has changed We will estimate the alpha parameter behind the scenes. """ flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars() has_bck = self.bck_chk.IsChecked() # If the pars are valid, estimate alpha if flag: self.nterms_estimate_ctl.Enable(False) self.alpha_estimate_ctl.Enable(False) dataset = self.plot_data.GetValue() if dataset is not None and dataset.strip() != "": self._manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc, d_max=dmax, q_min=qmin, q_max=qmax, bck=has_bck, height=height, width=width) def _read_pars(self, evt=None): """ """ alpha = 0 nfunc = 5 dmax = 120 qmin = 0 qmax = 0 height = 0 width = 0 flag = True # Read slit height try: height_str = self.sheight_ctl.GetValue() if len(height_str.lstrip().rstrip()) == 0: height = 0 else: height = float(height_str) self.sheight_ctl.SetBackgroundColour(wx.WHITE) self.sheight_ctl.Refresh() except: flag = False self.sheight_ctl.SetBackgroundColour("pink") self.sheight_ctl.Refresh() # Read slit width try: width_str = self.swidth_ctl.GetValue() if len(width_str.lstrip().rstrip()) == 0: width = 0 else: width = float(width_str) self.swidth_ctl.SetBackgroundColour(wx.WHITE) self.swidth_ctl.Refresh() except: flag = False self.swidth_ctl.SetBackgroundColour("pink") self.swidth_ctl.Refresh() # Read alpha try: alpha = float(self.alpha_ctl.GetValue()) self.alpha_ctl.SetBackgroundColour(wx.WHITE) self.alpha_ctl.Refresh() except: flag = False self.alpha_ctl.SetBackgroundColour("pink") self.alpha_ctl.Refresh() # Read d_max try: dmax = float(self.dmax_ctl.GetValue()) self.dmax_ctl.SetBackgroundColour(wx.WHITE) self.dmax_ctl.Refresh() except: flag = False self.dmax_ctl.SetBackgroundColour("pink") self.dmax_ctl.Refresh() # Read nfunc try: nfunc = int(self.nfunc_ctl.GetValue()) npts = self._manager.get_npts() if npts > 0 and nfunc > npts: message = "Number of function terms should be smaller " message += "than the number of points" wx.PostEvent(self._manager.parent, StatusEvent(status=message)) raise ValueError, message self.nfunc_ctl.SetBackgroundColour(wx.WHITE) self.nfunc_ctl.Refresh() except: flag = False self.nfunc_ctl.SetBackgroundColour("pink") self.nfunc_ctl.Refresh() # Read qmin try: qmin_str = self.qmin_ctl.GetValue() if len(qmin_str.lstrip().rstrip()) == 0: qmin = None else: qmin = float(qmin_str) self.qmin_ctl.SetBackgroundColour(wx.WHITE) self.qmin_ctl.Refresh() except: flag = False self.qmin_ctl.SetBackgroundColour("pink") self.qmin_ctl.Refresh() # Read qmax try: qmax_str = self.qmax_ctl.GetValue() if len(qmax_str.lstrip().rstrip()) == 0: qmax = None else: qmax = float(qmax_str) self.qmax_ctl.SetBackgroundColour(wx.WHITE) self.qmax_ctl.Refresh() except: flag = False self.qmax_ctl.SetBackgroundColour("pink") self.qmax_ctl.Refresh() return flag, alpha, dmax, nfunc, qmin, qmax, height, width def _on_explore(self, evt): """ Invoke the d_max exploration dialog """ from explore_dialog import ExploreDialog if self._manager._last_pr is not None: pr = self._manager._create_plot_pr() dialog = ExploreDialog(pr, 10, None, -1, "") dialog.Show() else: message = "No data to analyze. Please load a data set to proceed." wx.PostEvent(self._manager.parent, StatusEvent(status=message)) def _on_invert(self, evt): """ Perform inversion :param silent: when True, there will be no output for the user """ # Get the data from the form # Push it to the manager flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars() has_bck = self.bck_chk.IsChecked() if flag: dataset = self.plot_data.GetValue() if dataset is None or len(dataset.strip()) == 0: message = "No data to invert. Select a data set before" message += " proceeding with P(r) inversion." wx.PostEvent(self._manager.parent, StatusEvent(status=message)) else: self._manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc, d_max=dmax, q_min=qmin, q_max=qmax, bck=has_bck, height=height, width=width) else: message = "The P(r) form contains invalid values: " message += "please submit it again." wx.PostEvent(self.parent, StatusEvent(status=message)) def _change_file(self, evt=None, filepath=None, data=None): """ Choose a new input file for I(q) """ if self._manager is not None: self.plot_data.SetValue(str(data.name)) try: self._manager.show_data(data=data, reset=True) self._on_pars_changed(None) self._on_invert(None) self._set_analysis(True) except: msg = "InversionControl._change_file: %s" % sys.exc_value logger.error(msg) def on_help(self, event): """ Bring up the P(r) Documentation whenever the HELP button is clicked. Calls DocumentationWindow with the path of the location within the documentation tree (after /doc/ ....". Note that when using old versions of Wx (before 2.9) and thus not the release version of installers, the help comes up at the top level of the file as webbrowser does not pass anything past the # to the browser when it is running "file:///...." :param evt: Triggers on clicking the help button """ _TreeLocation = "user/sasgui/perspectives/pr/pr_help.html" _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "", "P(r) Help")
class PrDistDialog(wx.Dialog): """ Property dialog to let the user change the number of points on the P(r) plot. """ def __init__(self, parent, id): from sas.sascalc.pr.invertor import help wx.Dialog.__init__(self, parent, id, size=(250, 120)) self.SetTitle("P(r) distribution") vbox = wx.BoxSizer(wx.VERTICAL) label_npts = wx.StaticText(self, -1, "Number of points") self.npts_ctl = PrTextCtrl(self, -1, size=(100, 20)) pars_sizer = wx.GridBagSizer(5, 5) iy = 0 pars_sizer.Add(label_npts, (iy, 0), (1, 1), wx.LEFT, 15) pars_sizer.Add(self.npts_ctl, (iy, 1), (1, 1), wx.RIGHT, 0) vbox.Add(pars_sizer, 0, wx.ALL | wx.EXPAND, 15) static_line = wx.StaticLine(self, -1) vbox.Add(static_line, 0, wx.EXPAND, 0) button_ok = wx.Button(self, wx.ID_OK, "OK") self.Bind(wx.EVT_BUTTON, self._checkValues, button_ok) button_cancel = wx.Button(self, wx.ID_CANCEL, "Cancel") sizer_button = wx.BoxSizer(wx.HORIZONTAL) sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0) sizer_button.Add(button_ok, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10) sizer_button.Add(button_cancel, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10) vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10) self.SetSizer(vbox) self.SetAutoLayout(True) self.Layout() self.Centre() def _checkValues(self, event): """ Check the dialog content. """ flag = True try: int(self.npts_ctl.GetValue()) self.npts_ctl.SetBackgroundColour(wx.WHITE) self.npts_ctl.Refresh() except: flag = False self.npts_ctl.SetBackgroundColour("pink") self.npts_ctl.Refresh() if flag: event.Skip(True) def get_content(self): """ Return the content of the dialog. At this point the values have already been checked. """ value = int(self.npts_ctl.GetValue()) return value def set_content(self, npts): """ Initialize the content of the dialog. """ self.npts_ctl.SetValue("%i" % npts)