source: sasview/src/sas/sasgui/perspectives/fitting/fitpage.py @ 61a2a0c

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 61a2a0c was c8e1996, checked in by krzywon, 8 years ago

Fixes #738: No errors are thrown on loading projects with fits, plus linting.

  • Property mode set to 100644
File size: 128.0 KB
RevLine 
[682c432]1"""
2    FitPanel class contains fields allowing to display results when
3    fitting  a model and one data
4"""
5import sys
6import wx
7import wx.lib.newevent
8import numpy
9import copy
10import math
11import time
[6c382da]12import traceback
[cb4ef58]13
14from sasmodels.weights import MODELS as POLYDISPERSITY_MODELS
15
[c8e1996]16from sas.sasgui.guiframe.events import StatusEvent, NewPlotEvent, \
17    PlotQrangeEvent
[d85c194]18from sas.sasgui.guiframe.dataFitting import check_data_validity
[c8e1996]19from sas.sasgui.guiframe.utils import format_number, check_float
[d85c194]20from sas.sasgui.guiframe.documentation_window import DocumentationWindow
[682c432]21
[c8e1996]22from sas.sasgui.perspectives.fitting.basepage import BasicPage as BasicPage
23from sas.sasgui.perspectives.fitting.basepage import PageInfoEvent as \
24    PageInfoEvent
25from sas.sascalc.data_util.qsmearing import smear_selection
26from .basepage import ModelTextCtrl
27
[682c432]28(Chi2UpdateEvent, EVT_CHI2_UPDATE) = wx.lib.newevent.NewEvent()
29_BOX_WIDTH = 76
30_DATA_BOX_WIDTH = 300
31SMEAR_SIZE_L = 0.00
32SMEAR_SIZE_H = 0.00
33
34
35class FitPage(BasicPage):
36    """
37    FitPanel class contains fields allowing to display results when
38    fitting  a model and one data
39
40    :note: For Fit to be performed the user should check at least one parameter
41        on fit Panel window.
42    """
43
44    def __init__(self, parent, color=None):
45        """
46        Initialization of the Panel
47        """
48        BasicPage.__init__(self, parent, color=color)
49
[c8e1996]50        # draw sizer
[682c432]51        self._fill_data_sizer()
52        self.is_2D = None
53        self.fit_started = False
54        self.weightbt_string = None
55        self.m_name = None
56        # get smear info from data
57        self._get_smear_info()
58        self._fill_model_sizer(self.sizer1)
59        self._get_defult_custom_smear()
60        self._fill_range_sizer()
61        self._set_smear(self.data)
62        self.Bind(EVT_CHI2_UPDATE, self.on_complete_chisqr)
63        # bind key event
64        self.Bind(wx.EVT_RIGHT_DOWN, self.on_right_down)
65        self._set_bookmark_flag(False)
66        self._set_save_flag(False)
67        self._set_preview_flag(False)
68        self._set_copy_flag(False)
69        self._set_paste_flag(False)
70        self.btFit.SetFocus()
71        self.enable_fit_button()
72        self.fill_data_combobox(data_list=self.data_list)
[c8e1996]73        # create a default data for an empty panel
[682c432]74        self.create_default_data()
75        self._manager.frame.Bind(wx.EVT_SET_FOCUS, self.on_set_focus)
76
77    def enable_fit_button(self):
78        """
79        Enable fit button if data is valid and model is valid
80        """
81        flag = check_data_validity(self.data) & (self.model is not None)
82        self.btFit.Enable(flag)
83       
84    def on_set_focus(self, event):
85        """
86        Override the basepage focus method to ensure the save flag is set
87        properly when focusing on the fit page.
88        """
89        flag = check_data_validity(self.data) & (self.model is not None)
90        self._set_save_flag(flag)
91        self.parent.on_set_focus(event)
[85ccd3a]92        self.on_tap_focus()
[682c432]93
94    def _fill_data_sizer(self):
95        """
96        fill sizer 0 with data info
97        """
[6f16e25]98        self.data_box_description = wx.StaticBox(self, wx.ID_ANY,
99                                                 'I(q) Data Source')
[682c432]100        if check_data_validity(self.data):
101            dname_color = wx.BLUE
102        else:
103            dname_color = wx.RED
104        self.data_box_description.SetForegroundColour(dname_color)
105        boxsizer1 = wx.StaticBoxSizer(self.data_box_description, wx.VERTICAL)
[c8e1996]106        # ----------------------------------------------------------
[682c432]107        sizer_data = wx.BoxSizer(wx.HORIZONTAL)
[6f16e25]108        self.dataSource = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
109        wx.EVT_COMBOBOX(self.dataSource, wx.ID_ANY, self.on_select_data)
[682c432]110        self.dataSource.SetMinSize((_DATA_BOX_WIDTH, -1))
[6f16e25]111        sizer_data.Add(wx.StaticText(self, wx.ID_ANY, 'Name : '))
[682c432]112        sizer_data.Add(self.dataSource)
113        sizer_data.Add((0, 5))
114        boxsizer1.Add(sizer_data, 0, wx.ALL, 10)
115        self.sizer0.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
116        self.sizer0.Layout()
117
118    def enable_datasource(self):
119        """
120        Enable or disable data source control depending on existing data
121        """
122        if not self.data_list:
123            self.dataSource.Disable()
124        else:
125            self.dataSource.Enable()
126
127    def fill_data_combobox(self, data_list):
128        """
129        Get a list of data and fill the corresponding combobox
130        """
131        self.dataSource.Clear()
132        self.data_list = data_list
133        self.enable_datasource()
134        if len(data_list) > 0:
[c8e1996]135            # find the maximum range covering all data
[682c432]136            qmin, qmax, npts = self.compute_data_set_range(data_list)
137            self.qmin_data_set = qmin
138            self.qmax_data_set = qmax
139            self.npts_data_set = npts
140
141            self.qmin.SetValue(str(self.qmin_data_set))
142            self.qmax.SetValue(str(self.qmax_data_set))
143            self.qmin.SetBackgroundColour("white")
144            self.qmax.SetBackgroundColour("white")
145            self.qmin_x = self.qmin_data_set
146            self.qmax_x = self.qmax_data_set
147            self.state.qmin = self.qmin_x
148            self.state.qmax = self.qmax_x
149        is_data = False
150        for data in self.data_list:
151            if data is not None:
152                self.dataSource.Append(str(data.name), clientData=data)
153                if not is_data:
154                    is_data = check_data_validity(data)
155        if is_data:
156            self.dataSource.SetSelection(0)
157            self.on_select_data(event=None)
158
159        if len(data_list) == 1:
160            self.dataSource.Disable()
161
162    def on_select_data(self, event=None):
163        """
164        On_select_data
165        """
166        if event is None and self.dataSource.GetCount() > 0:
167            data = self.dataSource.GetClientData(0)
168            self.set_data(data)
169        elif self.dataSource.GetCount() > 0:
170            pos = self.dataSource.GetSelection()
171            data = self.dataSource.GetClientData(pos)
172            self.set_data(data)
173
174    def _on_fit_complete(self):
175        """
176        When fit is complete ,reset the fit button label.
177        """
178        self.fit_started = False
179        self.set_fitbutton()
180
181    def _is_2D(self):
182        """
183        Check if data_name is Data2D
184
185        :return: True or False
186        """
187        if self.data.__class__.__name__ == "Data2D" or \
188                        self.enable2D:
189            return True
190        return False
191
192    def _fill_range_sizer(self):
193        """
[7801df8]194        Fill the Fitting sizer on the fit panel which contains: the smearing
195        information (dq), the weighting information (dI or other), the plotting
196        range, access to the 2D mask editor, the compute, fit, and help
197        buttons, xi^2, number of points etc.
[682c432]198        """
[c8e1996]199        is_2d_data = False
[682c432]200
201        # Check if data is 2D
202        if self.data.__class__.__name__ == "Data2D" or \
203                        self.enable2D:
[c8e1996]204            is_2d_data = True
[682c432]205
206        title = "Fitting"
[c8e1996]207        # smear messages & titles
[682c432]208        smear_message_none = "No smearing is selected..."
209        smear_message_dqdata = "The dQ data is being used for smearing..."
210        smear_message_2d = \
211              "Higher accuracy is very time-expensive. Use it with care..."
212        smear_message_new_ssmear = \
213              "Please enter only the value of interest to customize smearing..."
214        smear_message_new_psmear = \
215              "Please enter both; the dQ will be generated by interpolation..."
216        smear_message_2d_x_title = "<dQp>[1/A]:"
217        smear_message_2d_y_title = "<dQs>[1/A]:"
218        smear_message_pinhole_min_title = "dQ_low[1/A]:"
219        smear_message_pinhole_max_title = "dQ_high[1/A]:"
220        smear_message_slit_height_title = "Slit height[1/A]:"
221        smear_message_slit_width_title = "Slit width[1/A]:"
222
223        self._get_smear_info()
224
[c8e1996]225        # Sizers
[6f16e25]226        box_description_range = wx.StaticBox(self, wx.ID_ANY, str(title))
[682c432]227        box_description_range.SetForegroundColour(wx.BLUE)
228        boxsizer_range = wx.StaticBoxSizer(box_description_range, wx.VERTICAL)
229        self.sizer_set_smearer = wx.BoxSizer(wx.VERTICAL)
230        sizer_smearer = wx.BoxSizer(wx.HORIZONTAL)
231        self.sizer_new_smear = wx.BoxSizer(wx.HORIZONTAL)
232        self.sizer_set_masking = wx.BoxSizer(wx.HORIZONTAL)
233        sizer_chi2 = wx.BoxSizer(wx.VERTICAL)
[6f16e25]234        smear_set_box = wx.StaticBox(self, wx.ID_ANY,
235                                     'Set Instrumental Smearing')
[682c432]236        sizer_smearer_box = wx.StaticBoxSizer(smear_set_box, wx.HORIZONTAL)
237        sizer_smearer_box.SetMinSize((_DATA_BOX_WIDTH, 60))
238
[6f16e25]239        weighting_set_box = wx.StaticBox(self, wx.ID_ANY,
[682c432]240                                'Set Weighting by Selecting dI Source')
241        weighting_box = wx.StaticBoxSizer(weighting_set_box, wx.HORIZONTAL)
242        sizer_weighting = wx.BoxSizer(wx.HORIZONTAL)
243        weighting_box.SetMinSize((_DATA_BOX_WIDTH, 40))
[c8e1996]244        # Filling the sizer containing weighting info.
[6f16e25]245        self.dI_noweight = wx.RadioButton(self, wx.ID_ANY,
246                                          'No Weighting', style=wx.RB_GROUP)
247        self.dI_didata = wx.RadioButton(self, wx.ID_ANY, 'Use dI Data')
248        self.dI_sqrdata = wx.RadioButton(self, wx.ID_ANY, 'Use |sqrt(I Data)|')
249        self.dI_idata = wx.RadioButton(self, wx.ID_ANY, 'Use |I Data|')
[682c432]250        self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
251                  id=self.dI_noweight.GetId())
252        self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
253                  id=self.dI_didata.GetId())
254        self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
255                  id=self.dI_sqrdata.GetId())
256        self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
257                  id=self.dI_idata.GetId())
258        self.dI_didata.SetValue(True)
259        # add 4 types of weighting to the sizer
260        sizer_weighting.Add(self.dI_noweight, 0, wx.LEFT, 10)
261        sizer_weighting.Add((14, 10))
262        sizer_weighting.Add(self.dI_didata)
263        sizer_weighting.Add((14, 10))
264        sizer_weighting.Add(self.dI_sqrdata)
265        sizer_weighting.Add((14, 10))
266        sizer_weighting.Add(self.dI_idata)
267        sizer_weighting.Add((10, 10))
268        self.dI_noweight.Enable(False)
269        self.dI_didata.Enable(False)
270        self.dI_sqrdata.Enable(False)
271        self.dI_idata.Enable(False)
272        weighting_box.Add(sizer_weighting)
273
274        # combobox for smear2d accuracy selection
[6f16e25]275        self.smear_accuracy = wx.ComboBox(self, wx.ID_ANY,
276                                          size=(50, -1), style=wx.CB_READONLY)
[682c432]277        self._set_accuracy_list()
278        self.smear_accuracy.SetValue(self.smear2d_accuracy)
279        self.smear_accuracy.SetSelection(0)
[6f16e25]280        self.smear_accuracy.SetToolTipString(
[682c432]281            "'Higher' uses more Gaussian points for smearing computation.")
282
[6f16e25]283        wx.EVT_COMBOBOX(self.smear_accuracy, wx.ID_ANY,
284                        self._on_select_accuracy)
[682c432]285
[c8e1996]286        # Fit button
[6f16e25]287        self.btFit = wx.Button(self, self._ids.next(), 'Fit')
[682c432]288        self.default_bt_colour = self.btFit.GetDefaultAttributes()
289        self.btFit.Bind(wx.EVT_BUTTON, self._onFit, id=self.btFit.GetId())
290        self.btFit.SetToolTipString("Start fitting.")
291
[c8e1996]292        # General Help button
[6f16e25]293        self.btFitHelp = wx.Button(self, wx.ID_ANY, 'Help')
[d06c34c]294        self.btFitHelp.SetToolTipString("General fitting help.")
[7801df8]295        self.btFitHelp.Bind(wx.EVT_BUTTON, self._onFitHelp)
296       
[c8e1996]297        # Resolution Smearing Help button (for now use same technique as
298        # used for dI help to get tiniest possible button that works
299        # both on MAC and PC.  Should completely rewrite the fitting sizer
300        # in future.  This is minimum to get out release 3.1
[f3dc56c]301        #        comment June 14, 2015     --- PDB
302        if sys.platform.count("win32") > 0:
[c8e1996]303            size_q = (20, 15)  # on PC
[f3dc56c]304        else:
[c8e1996]305            size_q = (30, 20)  # on MAC
[6f16e25]306        self.btSmearHelp = wx.Button(self, wx.ID_ANY, '?',
307                                     style=wx.BU_EXACTFIT, size=size_q)
[d06c34c]308        self.btSmearHelp.SetToolTipString("Resolution smearing help.")
[7801df8]309        self.btSmearHelp.Bind(wx.EVT_BUTTON, self._onSmearHelp)
310       
[c8e1996]311        # textcntrl for custom resolution
[6f16e25]312        self.smear_pinhole_max = ModelTextCtrl(self, wx.ID_ANY,
[682c432]313                            size=(_BOX_WIDTH - 25, 20),
314                            style=wx.TE_PROCESS_ENTER,
315                            text_enter_callback=self.onPinholeSmear)
[6f16e25]316        self.smear_pinhole_min = ModelTextCtrl(self, wx.ID_ANY,
[682c432]317                            size=(_BOX_WIDTH - 25, 20),
318                            style=wx.TE_PROCESS_ENTER,
319                            text_enter_callback=self.onPinholeSmear)
[6f16e25]320        self.smear_slit_height = ModelTextCtrl(self, wx.ID_ANY,
[682c432]321                            size=(_BOX_WIDTH - 25, 20),
322                            style=wx.TE_PROCESS_ENTER,
323                            text_enter_callback=self.onSlitSmear)
[6f16e25]324        self.smear_slit_width = ModelTextCtrl(self, wx.ID_ANY,
[682c432]325                            size=(_BOX_WIDTH - 25, 20),
326                            style=wx.TE_PROCESS_ENTER,
327                            text_enter_callback=self.onSlitSmear)
328
[c8e1996]329        # smear
[6f16e25]330        self.smear_data_left = BGTextCtrl(self, wx.ID_ANY,
[373d4ee]331                                          size=(_BOX_WIDTH - 25, 20), style=0)
[682c432]332        self.smear_data_left.SetValue(str(self.dq_l))
[6f16e25]333        self.smear_data_right = BGTextCtrl(self, wx.ID_ANY,
[373d4ee]334                                           size=(_BOX_WIDTH - 25, 20), style=0)
[682c432]335        self.smear_data_right.SetValue(str(self.dq_r))
336
[c8e1996]337        # set default values for smear
[682c432]338        self.smear_pinhole_max.SetValue(str(self.dx_max))
339        self.smear_pinhole_min.SetValue(str(self.dx_min))
340        self.smear_slit_height.SetValue(str(self.dxl))
341        self.smear_slit_width.SetValue(str(self.dxw))
342
[c8e1996]343        # Filling the sizer containing instruments smearing info.
[6f16e25]344        self.disable_smearer = wx.RadioButton(self, wx.ID_ANY,
[682c432]345                                              'None', style=wx.RB_GROUP)
[6f16e25]346        self.enable_smearer = wx.RadioButton(self, wx.ID_ANY, 'Use dQ Data')
[c8e1996]347        # self.enable_smearer.SetToolTipString(
348        # "Click to use the loaded dQ data for smearing.")
[6f16e25]349        self.pinhole_smearer = wx.RadioButton(self, wx.ID_ANY,
[682c432]350                                              'Custom Pinhole Smear')
[c8e1996]351        # self.pinhole_smearer.SetToolTipString
352        # ("Click to input custom resolution for pinhole smearing.")
[6f16e25]353        self.slit_smearer = wx.RadioButton(self, wx.ID_ANY, 'Custom Slit Smear')
[c8e1996]354        # self.slit_smearer.SetToolTipString
355        # ("Click to input custom resolution for slit smearing.")
[682c432]356        self.Bind(wx.EVT_RADIOBUTTON, self.onSmear,
357                  id=self.disable_smearer.GetId())
358        self.Bind(wx.EVT_RADIOBUTTON, self.onSmear,
359                  id=self.enable_smearer.GetId())
360        self.Bind(wx.EVT_RADIOBUTTON, self.onPinholeSmear,
361                  id=self.pinhole_smearer.GetId())
362        self.Bind(wx.EVT_RADIOBUTTON, self.onSlitSmear,
363                  id=self.slit_smearer.GetId())
[b4818d9]364        self.enable_smearer.SetValue(True)
[682c432]365
366        sizer_smearer.Add(self.disable_smearer, 0, wx.LEFT, 10)
367        sizer_smearer.Add(self.enable_smearer)
368        sizer_smearer.Add(self.pinhole_smearer)
369        sizer_smearer.Add(self.slit_smearer)
[f3dc56c]370        sizer_smearer.Add(self.btSmearHelp)
[682c432]371        sizer_smearer.Add((10, 10))
372
373        # StaticText for chi2, N(for fitting), Npts + Log/linear spacing
[6f16e25]374        self.tcChi = BGTextCtrl(self, wx.ID_ANY, "-", size=(75, 20), style=0)
[682c432]375        self.tcChi.SetToolTipString("Chi2/Npts(Fit)")
[6f16e25]376        self.Npts_fit = BGTextCtrl(self, wx.ID_ANY, "-", size=(75, 20), style=0)
377        self.Npts_fit.SetToolTipString(
[c8e1996]378            " Npts : number of points selected for fitting")
[6f16e25]379        self.Npts_total = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
[c8e1996]380                                        style=wx.TE_PROCESS_ENTER,
381                                        text_enter_callback=self._onQrangeEnter)
[682c432]382        self.Npts_total.SetValue(format_number(self.npts_x))
[c8e1996]383        self.Npts_total.SetToolTipString(
384            " Total Npts : total number of data points")
[682c432]385
386        # Update and Draw button
[6f16e25]387        self.draw_button = wx.Button(self, self._ids.next(), 'Compute')
388        self.draw_button.Bind(wx.EVT_BUTTON,
[682c432]389                              self._onDraw, id=self.draw_button.GetId())
390        self.draw_button.SetToolTipString("Compute and Draw.")
391
392        self.points_sizer = wx.BoxSizer(wx.HORIZONTAL)
[6f16e25]393        self.pointsbox = wx.CheckBox(self, wx.ID_ANY, 'Log?', (10, 10))
[682c432]394        self.pointsbox.SetValue(False)
395        self.pointsbox.SetToolTipString("Check mark to use log spaced points")
396        wx.EVT_CHECKBOX(self, self.pointsbox.GetId(), self.select_log)
397
[6f16e25]398        self.points_sizer.Add(wx.StaticText(self, wx.ID_ANY, 'Npts    '))
[682c432]399        self.points_sizer.Add(self.pointsbox)
400
[6f16e25]401        box_description_1 = wx.StaticText(self, wx.ID_ANY, '   Chi2/Npts')
402        box_description_2 = wx.StaticText(self, wx.ID_ANY, 'Npts(Fit)')
[682c432]403
404        # StaticText for smear
[6f16e25]405        self.smear_description_none = wx.StaticText(self, wx.ID_ANY,
[682c432]406                                    smear_message_none, style=wx.ALIGN_LEFT)
[6f16e25]407        self.smear_description_dqdata = wx.StaticText(self, wx.ID_ANY,
408                                 smear_message_dqdata, style=wx.ALIGN_LEFT)
409        self.smear_description_type = wx.StaticText(self, wx.ID_ANY,
410                                    "Type:", style=wx.ALIGN_LEFT)
411        self.smear_description_accuracy_type = wx.StaticText(self, wx.ID_ANY,
412                                    "Accuracy:", style=wx.ALIGN_LEFT)
413        self.smear_description_smear_type = BGTextCtrl(self, wx.ID_ANY,
[682c432]414                                                       size=(57, 20), style=0)
415        self.smear_description_smear_type.SetValue(str(self.dq_l))
416        self.SetBackgroundColour(self.GetParent().GetBackgroundColour())
[6f16e25]417        self.smear_description_2d = wx.StaticText(self, wx.ID_ANY,
[682c432]418                                    smear_message_2d, style=wx.ALIGN_LEFT)
[6f16e25]419        self.smear_message_new_s = wx.StaticText(self, wx.ID_ANY,
[682c432]420                         smear_message_new_ssmear, style=wx.ALIGN_LEFT)
[6f16e25]421        self.smear_message_new_p = wx.StaticText(self, wx.ID_ANY,
[682c432]422                            smear_message_new_psmear, style=wx.ALIGN_LEFT)
[6f16e25]423        self.smear_description_2d_x = wx.StaticText(self, wx.ID_ANY,
[682c432]424                            smear_message_2d_x_title, style=wx.ALIGN_LEFT)
[6f16e25]425        self.smear_description_2d_x.SetToolTipString(
[682c432]426                                        "  dQp(parallel) in q_r direction.")
[6f16e25]427        self.smear_description_2d_y = wx.StaticText(self, wx.ID_ANY,
[682c432]428                            smear_message_2d_y_title, style=wx.ALIGN_LEFT)
[c8e1996]429        self.smear_description_2d_y.SetToolTipString(
[682c432]430                                    " dQs(perpendicular) in q_phi direction.")
[6f16e25]431        self.smear_description_pin_min = wx.StaticText(self, wx.ID_ANY,
[682c432]432                        smear_message_pinhole_min_title, style=wx.ALIGN_LEFT)
[6f16e25]433        self.smear_description_pin_max = wx.StaticText(self, wx.ID_ANY,
[682c432]434                        smear_message_pinhole_max_title, style=wx.ALIGN_LEFT)
[6f16e25]435        self.smear_description_slit_height = wx.StaticText(self, wx.ID_ANY,
[682c432]436                        smear_message_slit_height_title, style=wx.ALIGN_LEFT)
[6f16e25]437        self.smear_description_slit_width = wx.StaticText(self, wx.ID_ANY,
[682c432]438                        smear_message_slit_width_title, style=wx.ALIGN_LEFT)
439
[c8e1996]440        # arrange sizers
[682c432]441        self.sizer_set_smearer.Add(sizer_smearer)
442        self.sizer_set_smearer.Add((10, 10))
443        self.sizer_set_smearer.Add(self.smear_description_none,
[c8e1996]444                                   0, wx.CENTER, 10)
[682c432]445        self.sizer_set_smearer.Add(self.smear_description_dqdata,
[c8e1996]446                                   0, wx.CENTER, 10)
[682c432]447        self.sizer_set_smearer.Add(self.smear_description_2d,
[c8e1996]448                                   0, wx.CENTER, 10)
[682c432]449        self.sizer_new_smear.Add(self.smear_description_type,
[c8e1996]450                                 0, wx.CENTER, 10)
[682c432]451        self.sizer_new_smear.Add(self.smear_description_accuracy_type,
[c8e1996]452                                 0, wx.CENTER, 10)
[682c432]453        self.sizer_new_smear.Add(self.smear_accuracy)
454        self.sizer_new_smear.Add(self.smear_description_smear_type,
[c8e1996]455                                 0, wx.CENTER, 10)
[682c432]456        self.sizer_new_smear.Add((15, -1))
[c8e1996]457        self.sizer_new_smear.Add(self.smear_description_2d_x, 0, wx.CENTER, 10)
[682c432]458        self.sizer_new_smear.Add(self.smear_description_pin_min,
[c8e1996]459                                 0, wx.CENTER, 10)
[682c432]460        self.sizer_new_smear.Add(self.smear_description_slit_height,
[c8e1996]461                                 0, wx.CENTER, 10)
462
463        self.sizer_new_smear.Add(self.smear_pinhole_min, 0, wx.CENTER, 10)
464        self.sizer_new_smear.Add(self.smear_slit_height, 0, wx.CENTER, 10)
465        self.sizer_new_smear.Add(self.smear_data_left, 0, wx.CENTER, 10)
[682c432]466        self.sizer_new_smear.Add((20, -1))
467        self.sizer_new_smear.Add(self.smear_description_2d_y,
[c8e1996]468                                 0, wx.CENTER, 10)
[682c432]469        self.sizer_new_smear.Add(self.smear_description_pin_max,
[c8e1996]470                                 0, wx.CENTER, 10)
[682c432]471        self.sizer_new_smear.Add(self.smear_description_slit_width,
[c8e1996]472                                 0, wx.CENTER, 10)
[682c432]473
474        self.sizer_new_smear.Add(self.smear_pinhole_max, 0, wx.CENTER, 10)
475        self.sizer_new_smear.Add(self.smear_slit_width, 0, wx.CENTER, 10)
476        self.sizer_new_smear.Add(self.smear_data_right, 0, wx.CENTER, 10)
477
478        self.sizer_set_smearer.Add(self.smear_message_new_s, 0, wx.CENTER, 10)
479        self.sizer_set_smearer.Add(self.smear_message_new_p, 0, wx.CENTER, 10)
480        self.sizer_set_smearer.Add((5, 2))
481        self.sizer_set_smearer.Add(self.sizer_new_smear, 0, wx.CENTER, 10)
482
483        # add all to chi2 sizer
484        sizer_smearer_box.Add(self.sizer_set_smearer)
485        sizer_chi2.Add(sizer_smearer_box)
486        sizer_chi2.Add((-1, 5))
487        sizer_chi2.Add(weighting_box)
488        sizer_chi2.Add((-1, 5))
489
490        # hide all smear messages and textctrl
491        self._hide_all_smear_info()
492
493        # get smear_selection
494        self.current_smearer = smear_selection(self.data, self.model)
495
496        # Show only the relevant smear messages, etc
[c8e1996]497        if self.current_smearer is None:
498            if not is_2d_data:
[682c432]499                self.smear_description_none.Show(True)
500                self.enable_smearer.Disable()
501            else:
502                self.smear_description_none.Show(True)
503                self.slit_smearer.Disable()
[c8e1996]504            if self.data is None:
[682c432]505                self.slit_smearer.Disable()
506                self.pinhole_smearer.Disable()
507                self.enable_smearer.Disable()
508        else:
509            self._show_smear_sizer()
510        boxsizer_range.Add(self.sizer_set_masking)
[c8e1996]511        # 2D data? default
512        is_2d_data = False
[682c432]513
[c8e1996]514        # check if it is 2D data
[682c432]515        if self.data.__class__.__name__ == "Data2D" or self.enable2D:
[c8e1996]516            is_2d_data = True
[682c432]517
518        self.sizer5.Clear(True)
519
[6f16e25]520        self.qmin = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
[373d4ee]521                                  style=wx.TE_PROCESS_ENTER,
522                                  set_focus_callback=self.qrang_set_focus,
523                                  text_enter_callback=self._onQrangeEnter,
524                                  name='qmin')
[682c432]525        self.qmin.SetValue(str(self.qmin_x))
526        q_tip = "Click outside of the axes\n to remove the lines."
527        qmin_tip = "Minimun value of Q.\n"
528        qmin_tip += q_tip
529        self.qmin.SetToolTipString(qmin_tip)
530
[6f16e25]531        self.qmax = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
[373d4ee]532                                  style=wx.TE_PROCESS_ENTER,
533                                  set_focus_callback=self.qrang_set_focus,
534                                  text_enter_callback=self._onQrangeEnter,
535                                  name='qmax')
[682c432]536        self.qmax.SetValue(str(self.qmax_x))
537        qmax_tip = "Maximum value of Q.\n"
538        qmax_tip += q_tip
539        self.qmax.SetToolTipString(qmax_tip)
540        self.qmin.Bind(wx.EVT_MOUSE_EVENTS, self.qrange_click)
541        self.qmax.Bind(wx.EVT_MOUSE_EVENTS, self.qrange_click)
542        self.qmin.Bind(wx.EVT_KEY_DOWN, self.on_key)
543        self.qmax.Bind(wx.EVT_KEY_DOWN, self.on_key)
544        self.qmin.Bind(wx.EVT_TEXT, self.on_qrange_text)
545        self.qmax.Bind(wx.EVT_TEXT, self.on_qrange_text)
[6f16e25]546        wx_id = self._ids.next()
[d06c34c]547        self.reset_qrange = wx.Button(self, wx_id, 'Reset')
[682c432]548
[d06c34c]549        self.reset_qrange.Bind(wx.EVT_BUTTON, self.on_reset_clicked, id=wx_id)
[682c432]550        self.reset_qrange.SetToolTipString("Reset Q range to the default")
551
[d06c34c]552        sizer = wx.GridSizer(5, 5, 2, 6)
[682c432]553
[6f16e25]554        self.btEditMask = wx.Button(self, self._ids.next(), 'Editor')
[682c432]555        self.btEditMask.Bind(wx.EVT_BUTTON, self._onMask,
556                             id=self.btEditMask.GetId())
557        self.btEditMask.SetToolTipString("Edit Mask.")
[6f16e25]558        self.EditMask_title = wx.StaticText(self, wx.ID_ANY, ' Masking(2D)')
[682c432]559
[6f16e25]560        sizer.Add(wx.StaticText(self, wx.ID_ANY, '   Q range'))
561        sizer.Add(wx.StaticText(self, wx.ID_ANY, ' Min[1/A]'))
562        sizer.Add(wx.StaticText(self, wx.ID_ANY, ' Max[1/A]'))
[682c432]563        sizer.Add(self.EditMask_title)
[c8e1996]564        sizer.Add((-1, 5))
[d06c34c]565
[682c432]566        sizer.Add(self.reset_qrange)
567        sizer.Add(self.qmin)
568        sizer.Add(self.qmax)
569        sizer.Add(self.btEditMask)
[c8e1996]570        sizer.Add((-1, 5))
[d06c34c]571
[c8e1996]572        sizer.AddMany(5*[(-1, 5)])
[d06c34c]573
574        sizer.Add(box_description_1, 0, 0)
575        sizer.Add(box_description_2, 0, 0)
576        sizer.Add(self.points_sizer, 0, 0)
577        sizer.Add(self.draw_button, 0, 0)
[c8e1996]578        sizer.Add((-1, 5))
[d06c34c]579       
580        sizer.Add(self.tcChi, 0, 0)
581        sizer.Add(self.Npts_fit, 0, 0)
582        sizer.Add(self.Npts_total, 0, 0)
583        sizer.Add(self.btFit, 0, 0)
584        sizer.Add(self.btFitHelp, 0, 0)
585       
[682c432]586        boxsizer_range.Add(sizer_chi2)
587        boxsizer_range.Add(sizer)
[c8e1996]588        if is_2d_data:
[682c432]589            self.btEditMask.Enable()
590            self.EditMask_title.Enable()
591        else:
592            self.btEditMask.Disable()
593            self.EditMask_title.Disable()
[c8e1996]594        # save state
[682c432]595        self.save_current_state()
596        self.sizer5.Add(boxsizer_range, 0, wx.EXPAND | wx.ALL, 10)
597        self.sizer5.Layout()
598
599    def _set_sizer_dispersion(self):
600        """
601        draw sizer with gaussian dispersity parameters
602        """
603        self.fittable_param = []
604        self.fixed_param = []
605        self.orientation_params_disp = []
606
607        self.sizer4_4.Clear(True)
[c8e1996]608        if self.model is None:
609            # no model is selected
[682c432]610            return
611        if not self.enable_disp.GetValue():
[c8e1996]612            # the user didn't select dispersity display
[682c432]613            return
614
615        self._reset_dispersity()
616
[c8e1996]617        # fill a sizer with the combobox to select dispersion type
[6f16e25]618        model_disp = wx.StaticText(self, wx.ID_ANY, 'Function')
[4c3be25]619        CHECK_STATE = False
[682c432]620
621        ix = 0
622        iy = 0
[6f16e25]623        disp = wx.StaticText(self, wx.ID_ANY, ' ')
[682c432]624        self.sizer4_4.Add(disp, (iy, ix), (1, 1),
625                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
626        ix += 1
[6f16e25]627        values = wx.StaticText(self, wx.ID_ANY, 'PD[ratio]')
[682c432]628        polytext = "Polydispersity (= STD/mean); "
629        polytext += "the standard deviation over the mean value."
630        values.SetToolTipString(polytext)
631
632        self.sizer4_4.Add(values, (iy, ix), (1, 1),
633                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
634        ix += 2
635        if self.is_mac:
636            err_text = 'Error'
637        else:
638            err_text = ''
[6f16e25]639        self.text_disp_1 = wx.StaticText(self, wx.ID_ANY, err_text)
[c8e1996]640        self.sizer4_4.Add(self.text_disp_1, (iy, ix), (1, 1),
[682c432]641                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
642
643        ix += 1
[6f16e25]644        self.text_disp_min = wx.StaticText(self, wx.ID_ANY, 'Min')
[c8e1996]645        self.sizer4_4.Add(self.text_disp_min, (iy, ix), (1, 1),
[682c432]646                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
647
648        ix += 1
[6f16e25]649        self.text_disp_max = wx.StaticText(self, wx.ID_ANY, 'Max')
[682c432]650        self.sizer4_4.Add(self.text_disp_max, (iy, ix), (1, 1),
651                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
652
653        ix += 1
[6f16e25]654        npts = wx.StaticText(self, wx.ID_ANY, 'Npts')
[682c432]655        npts.SetToolTipString("Number of sampling points for the numerical\n\
656        integration over the distribution function.")
657        self.sizer4_4.Add(npts, (iy, ix), (1, 1),
658                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
659        ix += 1
[6f16e25]660        nsigmas = wx.StaticText(self, wx.ID_ANY, 'Nsigs')
[682c432]661        nsigmas.SetToolTipString("Number of sigmas between which the range\n\
662         of the distribution function will be used for weighting. \n\
663        The value '3' covers 99.5% for Gaussian distribution \n\
664        function. Note: Not recommended to change this value.")
665        self.sizer4_4.Add(nsigmas, (iy, ix), (1, 1),
666                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
667        ix += 1
668        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
669                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
670
671        self.text_disp_max.Show(True)
672        self.text_disp_min.Show(True)
673
674        for item in self.model.dispersion.keys():
675            if not self.magnetic_on:
676                if item in self.model.magnetic_params:
677                    continue
[c8e1996]678            if item not in self.model.orientation_params:
679                if item not in self.disp_cb_dict:
[682c432]680                    self.disp_cb_dict[item] = None
681                name0 = "Distribution of " + item
682                name1 = item + ".width"
683                name2 = item + ".npts"
684                name3 = item + ".nsigmas"
[c8e1996]685                if name1 not in self.model.details:
[682c432]686                    self.model.details[name1] = ["", None, None]
687
688                iy += 1
689                for p in self.model.dispersion[item].keys():
690
691                    if p == "width":
692                        ix = 0
[6f16e25]693                        cb = wx.CheckBox(self, wx.ID_ANY, name0, (10, 10))
[682c432]694                        cb.SetValue(CHECK_STATE)
695                        cb.SetToolTipString("Check mark to fit")
696                        wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
697                        self.sizer4_4.Add(cb, (iy, ix), (1, 1),
698                                wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
699                        ix = 1
700                        value = self.model.getParam(name1)
[6f16e25]701                        ctl1 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]702                                             size=(_BOX_WIDTH / 1.3, 20),
703                                             style=wx.TE_PROCESS_ENTER)
[682c432]704                        ctl1.SetLabel('PD[ratio]')
705                        poly_text = "Polydispersity (STD/mean) of %s\n" % item
706                        poly_text += "STD: the standard deviation"
707                        poly_text += " from the mean value."
708                        ctl1.SetToolTipString(poly_text)
709                        ctl1.SetValue(str(format_number(value, True)))
710                        self.sizer4_4.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
[c8e1996]711                        # text to show error sign
[682c432]712                        ix = 2
[6f16e25]713                        text2 = wx.StaticText(self, wx.ID_ANY, '+/-')
[682c432]714                        self.sizer4_4.Add(text2, (iy, ix), (1, 1),
715                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
716                        if not self.is_mac:
717                            text2.Hide()
718
719                        ix = 3
[6f16e25]720                        ctl2 = wx.TextCtrl(self, wx.ID_ANY,
[682c432]721                                           size=(_BOX_WIDTH / 1.3, 20),
722                                           style=0)
723
724                        self.sizer4_4.Add(ctl2, (iy, ix), (1, 1),
725                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
726                        if not self.is_mac:
727                            ctl2.Hide()
728
729                        ix = 4
[6f16e25]730                        ctl3 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]731                                             size=(_BOX_WIDTH / 2, 20),
732                                             style=wx.TE_PROCESS_ENTER,
733                            text_enter_callback=self._onparamRangeEnter)
[682c432]734
735                        self.sizer4_4.Add(ctl3, (iy, ix), (1, 1),
736                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
737
738                        ix = 5
[6f16e25]739                        ctl4 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]740                                             size=(_BOX_WIDTH / 2, 20),
741                                             style=wx.TE_PROCESS_ENTER,
[682c432]742                            text_enter_callback=self._onparamRangeEnter)
743
744                        self.sizer4_4.Add(ctl4, (iy, ix), (1, 1),
745                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
746
747                        ctl3.Show(True)
748                        ctl4.Show(True)
749
750                    elif p == "npts":
751                        ix = 6
752                        value = self.model.getParam(name2)
[6f16e25]753                        Tctl = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]754                                             size=(_BOX_WIDTH / 2.2, 20),
755                                             style=wx.TE_PROCESS_ENTER)
[682c432]756
757                        Tctl.SetValue(str(format_number(value)))
758                        self.sizer4_4.Add(Tctl, (iy, ix), (1, 1),
759                                           wx.EXPAND | wx.ADJUST_MINSIZE, 0)
760                        self.fixed_param.append([None, name2, Tctl, None, None,
761                                                 None, None, None])
762                    elif p == "nsigmas":
763                        ix = 7
764                        value = self.model.getParam(name3)
[6f16e25]765                        Tct2 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]766                                             size=(_BOX_WIDTH / 2.2, 20),
767                                             style=wx.TE_PROCESS_ENTER)
[682c432]768
769                        Tct2.SetValue(str(format_number(value)))
770                        self.sizer4_4.Add(Tct2, (iy, ix), (1, 1),
[c8e1996]771                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[682c432]772                        self.fixed_param.append([None, name3, Tct2,
773                                                 None, None, None,
774                                                 None, None])
775
776                ix = 8
[6f16e25]777                disp_box = wx.ComboBox(self, wx.ID_ANY, size=(65, -1),
[682c432]778                                       style=wx.CB_READONLY, name='%s' % name1)
[cb4ef58]779                for key, value in POLYDISPERSITY_MODELS.iteritems():
[682c432]780                    name_disp = str(key)
781                    disp_box.Append(name_disp, value)
782                    disp_box.SetStringSelection("gaussian")
[6f16e25]783                wx.EVT_COMBOBOX(disp_box, wx.ID_ANY, self._on_disp_func)
[682c432]784                self.sizer4_4.Add(disp_box, (iy, ix), (1, 1), wx.EXPAND)
785                self.fittable_param.append([cb, name1, ctl1, text2,
786                                            ctl2, ctl3, ctl4, disp_box])
787
788        ix = 0
789        iy += 1
790        self.sizer4_4.Add((20, 20), (iy, ix), (1, 1),
791                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
792        first_orient = True
793        for item in self.model.dispersion.keys():
794            if not self.magnetic_on:
795                if item in self.model.magnetic_params:
796                    continue
[c8e1996]797            if item in self.model.orientation_params:
798                if item not in self.disp_cb_dict:
[682c432]799                    self.disp_cb_dict[item] = None
800                name0 = "Distribution of " + item
801                name1 = item + ".width"
802                name2 = item + ".npts"
803                name3 = item + ".nsigmas"
804
[c8e1996]805                if name1 not in self.model.details:
[682c432]806                    self.model.details[name1] = ["", None, None]
807
808                iy += 1
809                for p in self.model.dispersion[item].keys():
810
811                    if p == "width":
812                        ix = 0
[6f16e25]813                        cb = wx.CheckBox(self, wx.ID_ANY, name0, (10, 10))
[682c432]814                        cb.SetValue(CHECK_STATE)
815                        cb.SetToolTipString("Check mark to fit")
816                        wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
817                        self.sizer4_4.Add(cb, (iy, ix), (1, 1),
818                                wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
819                        if self.data.__class__.__name__ == "Data2D" or \
820                                    self.enable2D:
821                            cb.Show(True)
822                        elif cb.IsShown():
823                            cb.Hide()
824                        ix = 1
825                        value = self.model.getParam(name1)
[6f16e25]826                        ctl1 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]827                                             size=(_BOX_WIDTH / 1.3, 20),
828                                             style=wx.TE_PROCESS_ENTER)
[682c432]829                        poly_tip = "Absolute Sigma for %s." % item
830                        ctl1.SetToolTipString(poly_tip)
831                        ctl1.SetValue(str(format_number(value, True)))
832                        if self.data.__class__.__name__ == "Data2D" or \
833                                    self.enable2D:
834                            if first_orient:
835                                values.SetLabel('PD[ratio], Sig[deg]')
836                                poly_text = "PD(polydispersity for lengths):\n"
837                                poly_text += "It should be a value between"
838                                poly_text += "0 and 1\n"
839                                poly_text += "Sigma for angles: \n"
840                                poly_text += "It is the STD (ratio*mean)"
841                                poly_text += " of the distribution.\n "
842
843                                values.SetToolTipString(poly_text)
844                                first_orient = False
845                            ctl1.Show(True)
846                        elif ctl1.IsShown():
847                            ctl1.Hide()
848
849                        self.sizer4_4.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
[c8e1996]850                        # text to show error sign
[682c432]851                        ix = 2
[6f16e25]852                        text2 = wx.StaticText(self, wx.ID_ANY, '+/-')
[682c432]853                        self.sizer4_4.Add(text2, (iy, ix), (1, 1),
854                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
855
856                        text2.Hide()
857
858                        ix = 3
[6f16e25]859                        ctl2 = wx.TextCtrl(self, wx.ID_ANY,
[682c432]860                                           size=(_BOX_WIDTH / 1.3, 20),
861                                           style=0)
862
863                        self.sizer4_4.Add(ctl2, (iy, ix), (1, 1),
864                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
865
866                        ctl2.Hide()
867                        if self.data.__class__.__name__ == "Data2D" or \
868                                self.enable2D:
869                            if self.is_mac:
870                                text2.Show(True)
871                                ctl2.Show(True)
872
873                        ix = 4
[6f16e25]874                        ctl3 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]875                                             size=(_BOX_WIDTH / 2, 20),
876                                             style=wx.TE_PROCESS_ENTER,
[682c432]877                                text_enter_callback=self._onparamRangeEnter)
878
879                        self.sizer4_4.Add(ctl3, (iy, ix), (1, 1),
880                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
881
882                        ctl3.Hide()
883
884                        ix = 5
[6f16e25]885                        ctl4 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]886                                             size=(_BOX_WIDTH / 2, 20),
887                                             style=wx.TE_PROCESS_ENTER,
[682c432]888                            text_enter_callback=self._onparamRangeEnter)
889                        self.sizer4_4.Add(ctl4, (iy, ix), (1, 1),
890                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
891                        ctl4.Hide()
892
893                        if self.data.__class__.__name__ == "Data2D" or \
894                                self.enable2D:
895                            ctl3.Show(True)
896                            ctl4.Show(True)
897
898                    elif p == "npts":
899                        ix = 6
900                        value = self.model.getParam(name2)
[6f16e25]901                        Tctl = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]902                                             size=(_BOX_WIDTH / 2.2, 20),
903                                             style=wx.TE_PROCESS_ENTER)
[682c432]904
905                        Tctl.SetValue(str(format_number(value)))
906                        if self.data.__class__.__name__ == "Data2D" or \
907                                self.enable2D:
908                            Tctl.Show(True)
909                        else:
910                            Tctl.Hide()
911                        self.sizer4_4.Add(Tctl, (iy, ix), (1, 1),
912                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
913                        self.fixed_param.append([None, name2, Tctl, None, None,
914                                                 None, None, None])
915                        self.orientation_params_disp.append([None, name2,
916                                                             Tctl, None, None,
917                                                             None, None, None])
918                    elif p == "nsigmas":
919                        ix = 7
920                        value = self.model.getParam(name3)
[6f16e25]921                        Tct2 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]922                                             size=(_BOX_WIDTH / 2.2, 20),
923                                             style=wx.TE_PROCESS_ENTER)
[682c432]924
925                        Tct2.SetValue(str(format_number(value)))
926                        if self.data.__class__.__name__ == "Data2D" or \
927                                self.enable2D:
928                            Tct2.Show(True)
929                        else:
930                            Tct2.Hide()
931                        self.sizer4_4.Add(Tct2, (iy, ix), (1, 1),
932                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
933
934                        self.fixed_param.append([None, name3, Tct2,
935                                                 None, None, None, None, None])
936
937                        self.orientation_params_disp.append([None, name3,
938                                        Tct2, None, None, None, None, None])
939
940                ix = 8
[6f16e25]941                disp_box = wx.ComboBox(self, wx.ID_ANY, size=(65, -1),
[682c432]942                                style=wx.CB_READONLY, name='%s' % name1)
[cb4ef58]943                for key, value in POLYDISPERSITY_MODELS.iteritems():
[682c432]944                    name_disp = str(key)
945                    disp_box.Append(name_disp, value)
946                    disp_box.SetStringSelection("gaussian")
[6f16e25]947                wx.EVT_COMBOBOX(disp_box, wx.ID_ANY, self._on_disp_func)
[682c432]948                self.sizer4_4.Add(disp_box, (iy, ix), (1, 1), wx.EXPAND)
949                self.fittable_param.append([cb, name1, ctl1, text2,
950                                            ctl2, ctl3, ctl4, disp_box])
951                self.orientation_params_disp.append([cb, name1, ctl1,
952                                            text2, ctl2, ctl3, ctl4, disp_box])
953
954                if self.data.__class__.__name__ == "Data2D" or \
955                                self.enable2D:
956                    disp_box.Show(True)
957                else:
958                    disp_box.Hide()
959
960        self.state.disp_cb_dict = copy.deepcopy(self.disp_cb_dict)
961
962        self.state.model = self.model.clone()
[c8e1996]963        # save state into
[682c432]964        self._copy_parameters_state(self.parameters, self.state.parameters)
965        self._copy_parameters_state(self.orientation_params_disp,
966                                     self.state.orientation_params_disp)
967        self._copy_parameters_state(self.fittable_param,
968                                    self.state.fittable_param)
969        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
970
971        wx.PostEvent(self.parent,
972                     StatusEvent(status=" Selected Distribution: Gaussian"))
[c8e1996]973        # Fill the list of fittable parameters
[682c432]974        self.get_all_checked_params()
975        self.Layout()
976
977    def _onDraw(self, event):
978        """
979        Update and Draw the model
980        """
[c8e1996]981        if self.model is None:
[682c432]982            msg = "Please select a Model first..."
983            wx.MessageBox(msg, 'Info')
984            return
985        """
986        if not self.data.is_data:
987            self.npts_x = self.Npts_total.GetValue()
988            self.Npts_fit.SetValue(self.npts_x)
989            self.create_default_data()
990        """
[c8e1996]991        flag, is_modified = self._update_paramv_on_fit()
[682c432]992
[c8e1996]993        wx.CallAfter(self._onparamEnter_helper, is_modified)
[682c432]994        if not flag:
995            msg = "The parameters are invalid"
996            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
997            return
998
999    def _onFit(self, event):
1000        """
1001        Allow to fit
1002        """
[c8e1996]1003        if event is not None:
[682c432]1004            event.Skip()
1005        if self.fit_started:
1006            self._StopFit()
1007            self.fit_started = False
1008            wx.CallAfter(self.set_fitbutton)
1009            return
1010
1011        if self.data is None:
1012            msg = "Please get Data first..."
1013            wx.MessageBox(msg, 'Info')
1014            wx.PostEvent(self._manager.parent,
1015                         StatusEvent(status="Fit: %s" % msg))
1016            return
1017        if self.model is None:
1018            msg = "Please select a Model first..."
1019            wx.MessageBox(msg, 'Info')
1020            wx.PostEvent(self._manager.parent,
1021                         StatusEvent(status="Fit: %s" % msg, type="stop"))
1022            return
1023
1024        if len(self.param_toFit) <= 0:
1025            msg = "Select at least one parameter to fit"
1026            wx.MessageBox(msg, 'Info')
1027            wx.PostEvent(self._manager.parent,
1028                         StatusEvent(status=msg, type="stop"))
1029            return
1030
1031        flag = self._update_paramv_on_fit()
1032
1033        if self.batch_on and not self._is_2D():
1034            if not self._validate_Npts_1D():
1035                return
1036
1037        if not flag:
1038            msg = "Fitting range or parameters are invalid"
1039            wx.PostEvent(self._manager.parent,
1040                         StatusEvent(status=msg, type="stop"))
1041            return
1042
[c8e1996]1043        self.select_param()
[682c432]1044
1045        # Remove or do not allow fitting on the Q=0 point, especially
1046        # when y(q=0)=None at x[0].
1047        self.qmin_x = float(self.qmin.GetValue())
1048        self.qmax_x = float(self.qmax.GetValue())
1049        self._manager._reset_schedule_problem(value=0, uid=self.uid)
1050        self._manager.schedule_for_fit(uid=self.uid, value=1)
1051        self._manager.set_fit_range(uid=self.uid, qmin=self.qmin_x,
1052                                    qmax=self.qmax_x)
1053
[c8e1996]1054        # single fit
1055        # self._manager.onFit(uid=self.uid)
[682c432]1056        self.fit_started = self._manager.onFit(uid=self.uid)
1057        wx.CallAfter(self.set_fitbutton)
1058
[7801df8]1059    def _onFitHelp(self, event):
1060        """
1061        Bring up the Full Fitting Documentation whenever the HELP button is
1062        clicked.
1063
1064        Calls DocumentationWindow with the path of the location within the
1065        documentation tree (after /doc/ ....".  Note that when using old
1066        versions of Wx (before 2.9) and thus not the release version of
1067        installers, the help comes up at the top level of the file as
[c8e1996]1068        web browser does not pass anything past the # to the browser when it is
[7801df8]1069        running "file:///...."
1070
[c8e1996]1071        :param evt: Triggers on clicking the help button
1072        """
[7801df8]1073
[f27dd1e]1074        _TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html"
[6f16e25]1075        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
[7801df8]1076                                          "General Fitting Help")
1077
1078    def _onSmearHelp(self, event):
1079        """
1080        Bring up the instrumental resolution smearing Documentation whenever
1081        the ? button in the smearing box is clicked.
1082
1083        Calls DocumentationWindow with the path of the location within the
1084        documentation tree (after /doc/ ....".  Note that when using old
1085        versions of Wx (before 2.9) and thus not the release version of
1086        installers, the help comes up at the top level of the file as
[c8e1996]1087        web browser does not pass anything past the # to the browser when it is
[7801df8]1088        running "file:///...."
1089
[c8e1996]1090        :param evt: Triggers on clicking the help button
1091        """
[7801df8]1092
[f27dd1e]1093        _TreeLocation = "user/sasgui/perspectives/fitting/sm_help.html"
[6f16e25]1094        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
[7801df8]1095                                          "Instrumental Resolution Smearing \
1096                                          Help")
1097
[682c432]1098    def set_fitbutton(self):
1099        """
1100        Set fit button label depending on the fit_started[bool]
1101        """
1102        # Skip this feature if we are not on Windows
[c8e1996]1103        # NOTE: the is_mac data member actually means "is no Windows".
[682c432]1104        if self.is_mac:
1105            return
1106
1107        if self.fit_started:
1108            label = "Stop"
1109            color = "red"
1110        else:
1111            label = "Fit"
1112            color = "black"
[c8e1996]1113        # self.btFit.Enable(False)
[682c432]1114        self.btFit.SetLabel(label)
1115        self.btFit.SetForegroundColour(color)
1116        self.btFit.Enable(True)
1117
1118    def get_weight_flag(self):
1119        """
1120        Get flag corresponding to a given weighting dI data.
1121        """
1122        button_list = [self.dI_noweight,
1123                       self.dI_didata,
1124                       self.dI_sqrdata,
1125                       self.dI_idata]
1126        flag = 1
1127        for item in button_list:
1128            if item.GetValue():
1129                if button_list.index(item) == 0:
1130                    flag = 0  # dy = numpy.ones_like(dy_data)
1131                elif button_list.index(item) == 1:
1132                    flag = 1  # dy = dy_data
1133                elif button_list.index(item) == 2:
1134                    flag = 2  # dy = numpy.sqrt(numpy.abs(data))
1135                elif button_list.index(item) == 3:
1136                    flag = 3  # dy = numpy.abs(data)
1137                break
1138        return flag
1139
1140    def _StopFit(self, event=None):
1141        """
1142        Stop fit
1143        """
[c8e1996]1144        if event is not None:
[682c432]1145            event.Skip()
1146        self._manager.stop_fit(self.uid)
1147        self._manager._reset_schedule_problem(value=0)
1148        self._on_fit_complete()
1149
1150    def rename_model(self):
1151        """
1152        find a short name for model
1153        """
1154        if self.model is not None:
1155            self.model.name = "M" + str(self.index_model)
1156
1157    def _on_select_model(self, event=None):
1158        """
1159        call back for model selection
1160        """
1161        self.Show(False)
1162        copy_flag = False
1163        is_poly_enabled = None
[c8e1996]1164        if event is not None:
1165            if (event.GetEventObject() == self.formfactorbox
[682c432]1166                        and self.structurebox.GetLabel() != 'None')\
1167                        or event.GetEventObject() == self.structurebox\
1168                        or event.GetEventObject() == self.multifactorbox:
1169                copy_flag = self.get_copy_params()
1170                is_poly_enabled = self.enable_disp.GetValue()
1171
1172        self._on_select_model_helper()
1173        self.set_model_param_sizer(self.model)
1174        if self.model is None:
1175            self._set_bookmark_flag(False)
1176            self._keep.Enable(False)
1177            self._set_save_flag(False)
1178        self.enable_disp.SetValue(False)
1179        self.disable_disp.SetValue(True)
[6f16e25]1180        # TODO: should not have an untrapped exception when displaying disperser
1181        # TODO: do we need to create the disperser panel on every model change?
1182        # Note: if we fix this, then remove ID_DISPERSER_HELP from basepage
[682c432]1183        try:
1184            self.set_dispers_sizer()
1185        except:
1186            pass
1187        self.state.enable_disp = self.enable_disp.GetValue()
1188        self.state.disable_disp = self.disable_disp.GetValue()
1189        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
1190        self.state.slit_smearer = self.slit_smearer.GetValue()
1191
1192        self.state.structurecombobox = self.structurebox.GetLabel()
1193        self.state.formfactorcombobox = self.formfactorbox.GetLabel()
1194        self.enable_fit_button()
[373d4ee]1195        if self.model is not None:
[682c432]1196            self.m_name = self.model.name
1197            self.state.m_name = self.m_name
1198            self.rename_model()
1199            self._set_copy_flag(True)
1200            self._set_paste_flag(True)
1201            if self.data is not None:
1202                is_data = check_data_validity(self.data)
1203                if is_data:
1204                    self._set_bookmark_flag(not self.batch_on)
1205                    self._keep.Enable(not self.batch_on)
1206                    self._set_save_flag(True)
[c8e1996]1207                    self._set_smear(self.data)
[682c432]1208
1209            # more disables for 2D
1210            self._set_smear_buttons()
1211
1212            try:
1213                # update smearer sizer
1214                self.onSmear(None)
1215                temp_smear = None
1216                if not self.disable_smearer.GetValue():
1217                    # Set the smearer environments
1218                    temp_smear = self.current_smearer
1219            except:
1220                raise
[c8e1996]1221                # error occured on chisqr computation
1222                # pass
1223            # event to post model to fit to fitting plugins
[682c432]1224            (ModelEventbox, EVT_MODEL_BOX) = wx.lib.newevent.NewEvent()
1225
[c8e1996]1226            # set smearing value whether or not data contain the smearing info
[682c432]1227            evt = ModelEventbox(model=self.model,
1228                            smearer=temp_smear,
1229                            enable_smearer=not self.disable_smearer.GetValue(),
1230                            qmin=float(self.qmin_x),
1231                            uid=self.uid,
1232                            caption=self.window_caption,
1233                            qmax=float(self.qmax_x))
1234
1235            self._manager._on_model_panel(evt=evt)
[c8e1996]1236            self.mbox_description.SetLabel("Model [ %s ]" %
1237                                           str(self.model.name))
[682c432]1238            self.mbox_description.SetForegroundColour(wx.BLUE)
1239            self.state.model = self.model.clone()
1240            self.state.model.name = self.model.name
1241
[c8e1996]1242        if event is not None:
1243            # post state to fit panel
[682c432]1244            new_event = PageInfoEvent(page=self)
1245            wx.PostEvent(self.parent, new_event)
[c8e1996]1246            # update list of plugins if new plugin is available
[682c432]1247            custom_model = 'Customized Models'
1248            mod_cat = self.categorybox.GetStringSelection()
1249            if mod_cat == custom_model:
1250                temp = self.parent.update_model_list()
1251                if temp:
1252                    self.model_list_box = temp
1253                    current_val = self.formfactorbox.GetLabel()
1254                    pos = self.formfactorbox.GetSelection()
1255                    self._show_combox_helper()
1256                    self.formfactorbox.SetSelection(pos)
1257                    self.formfactorbox.SetValue(current_val)
1258            # when select a model only from guictr/button
[c8e1996]1259            if is_poly_enabled is not None:
[682c432]1260                self.enable_disp.SetValue(is_poly_enabled)
1261                self.disable_disp.SetValue(not is_poly_enabled)
1262                self._set_dipers_Param(event=None)
1263                self.state.enable_disp = self.enable_disp.GetValue()
1264                self.state.disable_disp = self.disable_disp.GetValue()
1265
1266            # Keep the previous param values
1267            if copy_flag:
1268                self.get_paste_params(copy_flag)
1269                wx.CallAfter(self._onDraw, None)
1270
1271        else:
1272            self._draw_model()
1273
1274        if self.batch_on:
1275            self.slit_smearer.Enable(False)
1276            self.pinhole_smearer.Enable(False)
1277            self.btEditMask.Disable()
1278            self.EditMask_title.Disable()
1279
1280        self.Show(True)
1281        self.SetupScrolling()
1282
1283    def _onparamEnter(self, event):
1284        """
1285        when enter value on panel redraw model according to changed
1286        """
[c8e1996]1287        if self.model is None:
[682c432]1288            msg = "Please select a Model first..."
1289            wx.MessageBox(msg, 'Info')
1290            return
1291
[c8e1996]1292        # default flag
[682c432]1293        flag = False
1294        self.fitrange = True
[c8e1996]1295        # get event object
[682c432]1296        tcrtl = event.GetEventObject()
[c8e1996]1297        # Clear msg if previously shown.
[682c432]1298        msg = ""
1299        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1300
1301        if check_float(tcrtl):
1302            flag = self._onparamEnter_helper()
1303            self.show_npts2fit()
1304            if self.fitrange:
1305                temp_smearer = None
1306                if not self.disable_smearer.GetValue():
1307                    temp_smearer = self.current_smearer
[c8e1996]1308                    # set smearing value whether or not data contain the
1309                    # smearing info
[682c432]1310                    if self.slit_smearer.GetValue():
1311                        flag1 = self.update_slit_smear()
1312                        flag = flag or flag1
1313                    elif self.pinhole_smearer.GetValue():
1314                        flag1 = self.update_pinhole_smear()
1315                        flag = flag or flag1
1316                elif self.data.__class__.__name__ != "Data2D" and \
1317                        not self.enable2D:
[373d4ee]1318                    enable_smearer = not self.disable_smearer.GetValue()
[682c432]1319                    self._manager.set_smearer(smearer=temp_smearer,
1320                                              fid=self.data.id,
1321                                              uid=self.uid,
1322                                              qmin=float(self.qmin_x),
1323                                              qmax=float(self.qmax_x),
[934ce649]1324                                              enable_smearer=enable_smearer)
[682c432]1325                if flag:
[c8e1996]1326                    # self.compute_chisqr(smearer= temp_smearer)
[682c432]1327
[c8e1996]1328                    # new state posted
[682c432]1329                    if self.state_change:
[c8e1996]1330                        # self._undo.Enable(True)
[682c432]1331                        event = PageInfoEvent(page=self)
1332                        wx.PostEvent(self.parent, event)
1333                    self.state_change = False
1334            else:
1335                # invalid fit range: do nothing here:
1336                # msg already displayed in validate
1337                return
1338        else:
1339            self.save_current_state()
[c8e1996]1340            msg = "Cannot Plot: Must enter a number!!!  "
[682c432]1341            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1342
1343        self.save_current_state()
1344        return
1345
1346    def _onparamRangeEnter(self, event):
1347        """
1348        Check validity of value enter in the parameters range field
1349        """
1350        tcrtl = event.GetEventObject()
[c8e1996]1351        # Clear msg if previously shown.
[682c432]1352        msg = ""
1353        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1354        # Flag to register when a parameter has changed.
1355        is_modified = False
1356        if tcrtl.GetValue().lstrip().rstrip() != "":
1357            try:
1358                tcrtl.SetBackgroundColour(wx.WHITE)
[ee4b3cb]1359                self._check_value_enter(self.fittable_param)
1360                self._check_value_enter(self.parameters)
[682c432]1361            except:
1362                tcrtl.SetBackgroundColour("pink")
1363                msg = "Model Error:wrong value entered : %s" % sys.exc_value
1364                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1365                return
1366        else:
1367            tcrtl.SetBackgroundColour(wx.WHITE)
1368
[c8e1996]1369        # self._undo.Enable(True)
[682c432]1370        self.save_current_state()
1371        event = PageInfoEvent(page=self)
1372        wx.PostEvent(self.parent, event)
1373        self.state_change = False
1374
1375    def qrang_set_focus(self, event=None):
1376        """
1377        ON Qrange focus
1378        """
[c8e1996]1379        if event is not None:
[682c432]1380            event.Skip()
[c8e1996]1381        # tcrtl = event.GetEventObject()
[682c432]1382        self._validate_qrange(self.qmin, self.qmax)
1383
1384    def qrange_click(self, event):
1385        """
1386        On Qrange textctrl click, make the qrange lines in the plot
1387        """
[c8e1996]1388        if event is not None:
[682c432]1389            event.Skip()
1390        if self.data.__class__.__name__ == "Data2D":
1391            return
1392        is_click = event.LeftDown()
1393        if is_click:
1394            d_id = self.data.id
1395            d_group_id = self.data.group_id
1396            act_ctrl = event.GetEventObject()
1397            wx.PostEvent(self._manager.parent,
1398                         PlotQrangeEvent(ctrl=[self.qmin, self.qmax], id=d_id,
1399                                     group_id=d_group_id, leftdown=is_click,
1400                                     active=act_ctrl))
1401
1402    def on_qrange_text(self, event):
1403        """
1404        #On q range value updated. DO not combine with qrange_click().
1405        """
[c8e1996]1406        if event is not None:
[682c432]1407            event.Skip()
1408        if self.data.__class__.__name__ == "Data2D":
1409            return
1410        act_ctrl = event.GetEventObject()
1411        d_id = self.data.id
1412        d_group_id = self.data.group_id
1413        wx.PostEvent(self._manager.parent,
1414                     PlotQrangeEvent(ctrl=[self.qmin, self.qmax], id=d_id,
1415                                     group_id=d_group_id, leftdown=False,
1416                                     active=act_ctrl))
1417        self._validate_qrange(self.qmin, self.qmax)
1418
1419    def on_key(self, event):
1420        """
1421        On Key down
1422        """
1423        event.Skip()
1424        if self.data.__class__.__name__ == "Data2D":
1425            return
1426        ctrl = event.GetEventObject()
1427        try:
1428            x_data = float(ctrl.GetValue())
1429        except:
1430            return
1431        key = event.GetKeyCode()
1432        length = len(self.data.x)
1433        indx = (numpy.abs(self.data.x - x_data)).argmin()
[c8e1996]1434        # return array.flat[idx]
[682c432]1435        if key == wx.WXK_PAGEUP or key == wx.WXK_NUMPAD_PAGEUP:
1436            indx += 1
1437            if indx >= length:
1438                indx = length - 1
1439        elif key == wx.WXK_PAGEDOWN or key == wx.WXK_NUMPAD_PAGEDOWN:
1440            indx -= 1
1441            if indx < 0:
1442                indx = 0
1443        else:
1444            return
1445        ctrl.SetValue(str(self.data.x[indx]))
1446        self._validate_qrange(self.qmin, self.qmax)
1447
1448    def _onQrangeEnter(self, event):
1449        """
1450        Check validity of value enter in the Q range field
1451        """
1452        tcrtl = event.GetEventObject()
[c8e1996]1453        # Clear msg if previously shown.
[682c432]1454        msg = ""
1455        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1456        # For theory mode
1457        if not self.data.is_data:
1458            self.npts_x = self.Npts_total.GetValue()
1459            self.Npts_fit.SetValue(self.npts_x)
1460            self.create_default_data()
1461        # Flag to register when a parameter has changed.
1462        if tcrtl.GetValue().lstrip().rstrip() != "":
1463            try:
1464                tcrtl.SetBackgroundColour(wx.WHITE)
1465                # If qmin and qmax have been modified, update qmin and qmax
1466                if self._validate_qrange(self.qmin, self.qmax):
1467                    tempmin = float(self.qmin.GetValue())
1468                    if tempmin != self.qmin_x:
1469                        self.qmin_x = tempmin
1470                    tempmax = float(self.qmax.GetValue())
1471                    if tempmax != self.qmax_x:
1472                        self.qmax_x = tempmax
1473                else:
1474                    tcrtl.SetBackgroundColour("pink")
1475                    msg = "Model Error:wrong value entered : %s" % sys.exc_value
1476                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1477                    return
1478            except:
1479                tcrtl.SetBackgroundColour("pink")
1480                msg = "Model Error:wrong value entered : %s" % sys.exc_value
1481                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1482                return
[c8e1996]1483            # Check if # of points for theory model are valid(>0).
[682c432]1484            # check for 2d
1485            if self.data.__class__.__name__ == "Data2D" or \
1486                    self.enable2D:
1487                # set mask
1488                radius = numpy.sqrt(self.data.qx_data * self.data.qx_data +
1489                                    self.data.qy_data * self.data.qy_data)
[c8e1996]1490                index_data = ((self.qmin_x <= radius) & (radius <= self.qmax_x))
[682c432]1491                index_data = (index_data) & (self.data.mask)
1492                index_data = (index_data) & (numpy.isfinite(self.data.data))
1493                if len(index_data[index_data]) < 10:
1494                    msg = "Cannot Plot :No or too little npts in"
1495                    msg += " that data range!!!  "
1496                    wx.PostEvent(self._manager.parent,
1497                                 StatusEvent(status=msg))
1498                    return
1499                else:
[c8e1996]1500                    # self.data.mask = index_data
1501                    # self.Npts_fit.SetValue(str(len(self.data.mask)))
[682c432]1502                    self.show_npts2fit()
1503            else:
[c8e1996]1504                index_data = ((self.qmin_x <= self.data.x) &
[682c432]1505                              (self.data.x <= self.qmax_x))
1506                self.Npts_fit.SetValue(str(len(self.data.x[index_data])))
1507
1508            self.npts_x = self.Npts_total.GetValue()
1509            self.create_default_data()
1510            self._save_plotting_range()
1511        else:
1512            tcrtl.SetBackgroundColour("pink")
1513            msg = "Model Error:wrong value entered!!!"
1514            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1515
1516        self._draw_model()
1517        self.save_current_state()
1518        event = PageInfoEvent(page=self)
1519        wx.PostEvent(self.parent, event)
1520        self.state_change = False
1521        return
1522
1523    def _clear_Err_on_Fit(self):
1524        """
1525        hide the error text control shown
1526        after fitting
1527        """
1528
1529        if self.is_mac:
1530            return
1531        if hasattr(self, "text2_3"):
1532            self.text2_3.Hide()
1533
1534        if len(self.parameters) > 0:
1535            for item in self.parameters:
1536                if item[0].IsShown():
[c8e1996]1537                    # Skip the angle parameters if 1D data
[682c432]1538                    if self.data.__class__.__name__ != "Data2D" and \
1539                            not self.enable2D:
1540                        if item in self.orientation_params:
1541                            continue
1542                    if item in self.param_toFit:
1543                        continue
[c8e1996]1544                    # hide statictext +/-
[682c432]1545                    if len(item) < 4:
1546                        continue
[c8e1996]1547                    if item[3] is not None and item[3].IsShown():
[682c432]1548                        item[3].Hide()
[c8e1996]1549                    # hide textcrtl  for error after fit
1550                    if item[4] is not None and item[4].IsShown():
[682c432]1551                        item[4].Hide()
1552
1553        if len(self.fittable_param) > 0:
1554            for item in self.fittable_param:
1555                if item[0].IsShown():
[c8e1996]1556                    # Skip the angle parameters if 1D data
[682c432]1557                    if self.data.__class__.__name__ != "Data2D" and \
1558                            not self.enable2D:
1559                        if item in self.orientation_params:
1560                            continue
1561                    if item in self.param_toFit:
1562                        continue
1563                    if len(item) < 4:
1564                        continue
[c8e1996]1565                    # hide statictext +/-
1566                    if item[3] is not None and item[3].IsShown():
[682c432]1567                        item[3].Hide()
[c8e1996]1568                    # hide textcrtl  for error after fit
1569                    if item[4] is not None and item[4].IsShown():
[682c432]1570                        item[4].Hide()
1571        return
1572
1573    def _get_defult_custom_smear(self):
1574        """
1575        Get the defult values for custum smearing.
1576        """
1577        # get the default values
[c8e1996]1578        if self.dxl is None:
[682c432]1579            self.dxl = 0.0
[c8e1996]1580        if self.dxw is None:
[682c432]1581            self.dxw = ""
[c8e1996]1582        if self.dx_min is None:
[682c432]1583            self.dx_min = SMEAR_SIZE_L
[c8e1996]1584        if self.dx_max is None:
[682c432]1585            self.dx_max = SMEAR_SIZE_H
1586
1587    def _get_smear_info(self):
1588        """
1589        Get the smear info from data.
1590
1591        :return: self.smear_type, self.dq_l and self.dq_r,
1592            respectively the type of the smear, dq_min and
1593            dq_max for pinhole smear data
1594            while dxl and dxw for slit smear
1595        """
1596        # default
1597        self.smear_type = None
1598        self.dq_l = None
1599        self.dq_r = None
1600        data = self.data
1601        if self.data is None:
1602            return
1603        elif self.data.__class__.__name__ == "Data2D" or \
1604            self.enable2D:
[c8e1996]1605            if data.dqx_data is None or data.dqy_data is None:
[682c432]1606                return
[c8e1996]1607            elif self.current_smearer is not None \
[682c432]1608                and data.dqx_data.any() != 0 \
1609                and data.dqx_data.any() != 0:
1610                self.smear_type = "Pinhole2d"
1611                self.dq_l = format_number(numpy.average(data.dqx_data))
1612                self.dq_r = format_number(numpy.average(data.dqy_data))
1613                return
1614            else:
1615                return
1616        # check if it is pinhole smear and get min max if it is.
[c8e1996]1617        if data.dx is not None and not numpy.any(data.dx):
[682c432]1618            self.smear_type = "Pinhole"
1619            self.dq_l = data.dx[0]
1620            self.dq_r = data.dx[-1]
1621
1622        # check if it is slit smear and get min max if it is.
[c8e1996]1623        elif data.dxl is not None or data.dxw is not None:
[682c432]1624            self.smear_type = "Slit"
[c8e1996]1625            if data.dxl is not None and not numpy.all(data.dxl, 0):
[682c432]1626                self.dq_l = data.dxl[0]
[c8e1996]1627            if data.dxw is not None and not numpy.all(data.dxw, 0):
[682c432]1628                self.dq_r = data.dxw[0]
[c8e1996]1629        # return self.smear_type,self.dq_l,self.dq_r
[682c432]1630
1631    def _show_smear_sizer(self):
1632        """
1633        Show only the sizers depending on smear selection
1634        """
1635        # smear disabled
1636        if self.disable_smearer.GetValue():
1637            self.smear_description_none.Show(True)
1638        # 2Dsmear
1639        elif self._is_2D():
1640            self.smear_description_accuracy_type.Show(True)
1641            self.smear_accuracy.Show(True)
1642            self.smear_description_accuracy_type.Show(True)
1643            self.smear_description_2d.Show(True)
1644            self.smear_description_2d_x.Show(True)
1645            self.smear_description_2d_y.Show(True)
1646            if self.pinhole_smearer.GetValue():
1647                self.smear_pinhole_min.Show(True)
1648                self.smear_pinhole_max.Show(True)
1649        # smear from data
1650        elif self.enable_smearer.GetValue():
1651
1652            self.smear_description_dqdata.Show(True)
[c8e1996]1653            if self.smear_type is not None:
[682c432]1654                self.smear_description_smear_type.Show(True)
1655                if self.smear_type == 'Slit':
1656                    self.smear_description_slit_height.Show(True)
1657                    self.smear_description_slit_width.Show(True)
1658                elif self.smear_type == 'Pinhole':
1659                    self.smear_description_pin_min.Show(True)
1660                    self.smear_description_pin_max.Show(True)
1661                self.smear_description_smear_type.Show(True)
1662                self.smear_description_type.Show(True)
1663                self.smear_data_left.Show(True)
1664                self.smear_data_right.Show(True)
1665        # custom pinhole smear
1666        elif self.pinhole_smearer.GetValue():
1667            if self.smear_type == 'Pinhole':
1668                self.smear_message_new_p.Show(True)
1669                self.smear_description_pin_min.Show(True)
1670                self.smear_description_pin_max.Show(True)
1671
1672            self.smear_pinhole_min.Show(True)
1673            self.smear_pinhole_max.Show(True)
1674        # custom slit smear
1675        elif self.slit_smearer.GetValue():
1676            self.smear_message_new_s.Show(True)
1677            self.smear_description_slit_height.Show(True)
1678            self.smear_slit_height.Show(True)
1679            self.smear_description_slit_width.Show(True)
1680            self.smear_slit_width.Show(True)
1681
1682    def _hide_all_smear_info(self):
1683        """
1684        Hide all smearing messages in the set_smearer sizer
1685        """
1686        self.smear_description_none.Hide()
1687        self.smear_description_dqdata.Hide()
1688        self.smear_description_type.Hide()
1689        self.smear_description_smear_type.Hide()
1690        self.smear_description_accuracy_type.Hide()
1691        self.smear_description_2d_x.Hide()
1692        self.smear_description_2d_y.Hide()
1693        self.smear_description_2d.Hide()
1694
1695        self.smear_accuracy.Hide()
1696        self.smear_data_left.Hide()
1697        self.smear_data_right.Hide()
1698        self.smear_description_pin_min.Hide()
1699        self.smear_pinhole_min.Hide()
1700        self.smear_description_pin_max.Hide()
1701        self.smear_pinhole_max.Hide()
1702        self.smear_description_slit_height.Hide()
1703        self.smear_slit_height.Hide()
1704        self.smear_description_slit_width.Hide()
1705        self.smear_slit_width.Hide()
1706        self.smear_message_new_p.Hide()
1707        self.smear_message_new_s.Hide()
1708
1709    def _set_accuracy_list(self):
1710        """
1711        Set the list of an accuracy in 2D custum smear:
1712                Xhigh, High, Med, or Low
1713        """
1714        # list of accuracy choices
1715        list = ['Low', 'Med', 'High', 'Xhigh']
1716        for idx in range(len(list)):
1717            self.smear_accuracy.Append(list[idx], idx)
1718
1719    def _set_fun_box_list(self, fun_box):
1720        """
1721        Set the list of func for multifunctional models
1722        """
1723        # Check if it is multi_functional model
1724        if self.model.__class__ not in self.model_list_box["Multi-Functions"] \
1725                and not self.temp_multi_functional:
1726            return None
1727        # Get the func name list
1728        list = self.model.fun_list
1729        if len(list) == 0:
1730            return None
1731        # build function (combo)box
1732        ind = 0
1733        while(ind < len(list)):
1734            for key, val in list.iteritems():
1735                if (val == ind):
1736                    fun_box.Append(key, val)
1737                    break
1738            ind += 1
1739
1740    def _on_select_accuracy(self, event):
1741        """
1742        Select an accuracy in 2D custom smear: Xhigh, High, Med, or Low
1743        """
[c8e1996]1744        # event.Skip()
[682c432]1745        # Check if the accuracy is same as before
[c8e1996]1746        # self.smear2d_accuracy = event.GetEventObject().GetValue()
[682c432]1747        self.smear2d_accuracy = self.smear_accuracy.GetValue()
1748        if self.pinhole_smearer.GetValue():
1749            self.onPinholeSmear(event=None)
1750        else:
1751            self.onSmear(event=None)
[c8e1996]1752            if self.current_smearer is not None:
[682c432]1753                self.current_smearer.set_accuracy(accuracy=\
1754                                                  self.smear2d_accuracy)
1755        event.Skip()
1756
1757    def _on_fun_box(self, event):
1758        """
1759        Select an func: Erf,Rparabola,LParabola
1760        """
1761        fun_val = None
1762        fun_box = event.GetEventObject()
1763        name = fun_box.Name
1764        value = fun_box.GetValue()
1765        if value in self.model.fun_list:
1766            fun_val = self.model.fun_list[value]
1767
1768        self.model.setParam(name, fun_val)
1769        # save state
1770        self._copy_parameters_state(self.str_parameters,
1771                                    self.state.str_parameters)
1772        # update params
1773        self._update_paramv_on_fit()
1774        # draw
1775        self._draw_model()
1776        self.Refresh()
1777        # get ready for new event
1778        event.Skip()
1779
1780    def _onMask(self, event):
1781        """
1782        Build a panel to allow to edit Mask
1783        """
[c8e1996]1784        from sas.sasgui.guiframe.local_perspectives.plotting.masking import \
1785            MaskPanel as MaskDialog
[682c432]1786
1787        self.panel = MaskDialog(base=self, data=self.data, id=wx.NewId())
1788        self.panel.ShowModal()
1789
1790    def _draw_masked_model(self, event):
1791        """
1792        Draw model image w/mask
1793        """
1794        is_valid_qrange = self._update_paramv_on_fit()
1795
[c8e1996]1796        if is_valid_qrange and self.model is not None:
[682c432]1797            self.panel.MakeModal(False)
1798            event.Skip()
1799            # try re draw the model plot if it exists
1800            self._draw_model()
1801            self.show_npts2fit()
[c8e1996]1802        elif self.model is None:
[682c432]1803            self.panel.MakeModal(False)
1804            event.Skip()
1805            self.show_npts2fit()
1806            msg = "No model is found on updating MASK in the model plot... "
1807            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1808        else:
1809            event.Skip()
1810            msg = ' Please consider your Q range, too.'
1811            self.panel.ShowMessage(msg)
1812
1813    def _set_smear(self, data):
1814        """
1815        Set_smear
1816        """
1817        if data is None:
1818            return
1819        self.current_smearer = smear_selection(data, self.model)
1820        flag = self.disable_smearer.GetValue()
[373d4ee]1821        if self.current_smearer is None:
[682c432]1822            self.enable_smearer.Disable()
1823        else:
1824            self.enable_smearer.Enable()
1825        if not flag:
1826            self.onSmear(None)
1827
1828    def _mac_sleep(self, sec=0.2):
1829        """
1830        Give sleep to MAC
1831        """
1832        if self.is_mac:
1833            time.sleep(sec)
1834
1835    def get_view_mode(self):
1836        """
1837        return True if the panel allow 2D or False if 1D
1838        """
1839        return self.enable2D
1840
1841    def compute_data_set_range(self, data_list):
1842        """
1843        find the range that include all data  in the set
1844        return the minimum and the maximum values
1845        """
1846        if data_list is not None and data_list != []:
1847            for data in data_list:
1848                qmin, qmax, npts = self.compute_data_range(data)
1849                self.qmin_data_set = min(self.qmin_data_set, qmin)
1850                self.qmax_data_set = max(self.qmax_data_set, qmax)
1851                self.npts_data_set += npts
1852        return self.qmin_data_set, self.qmax_data_set, self.npts_data_set
1853
1854    def compute_data_range(self, data):
1855        """
1856        compute the minimum and the maximum range of the data
1857        return the npts contains in data
1858        :param data:
1859        """
1860        qmin, qmax, npts = None, None, None
1861        if data is not None:
1862            if not hasattr(data, "data"):
1863                try:
1864                    qmin = min(data.x)
1865                    # Maximum value of data
1866                    qmax = max(data.x)
1867                    npts = len(data.x)
1868                except:
[c8e1996]1869                    msg = "Unable to find min/max/length of \n data named %s" %\
[682c432]1870                                data.filename
1871                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
1872                                               info="error"))
1873                    raise ValueError, msg
1874
1875            else:
1876                qmin = 0
1877                try:
1878                    x = max(math.fabs(data.xmin), math.fabs(data.xmax))
1879                    y = max(math.fabs(data.ymin), math.fabs(data.ymax))
1880                except:
1881                    msg = "Unable to find min/max of \n data named %s" % \
1882                                data.filename
1883                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
1884                                               info="error"))
1885                    raise ValueError, msg
[c8e1996]1886                # Maximum value of data
[682c432]1887                qmax = math.sqrt(x * x + y * y)
1888                npts = len(data.data)
1889        return qmin, qmax, npts
1890
1891    def set_data(self, data):
1892        """
1893        reset the current data
1894        """
1895        id = None
1896        flag = False
1897        is_data = False
[c8e1996]1898        npts = 0
[682c432]1899        try:
1900            old_id = self.data.id
1901            old_group_id = self.data.group_id
1902        except:
1903            old_id = id
1904            old_group_id = id
1905        if self.data is not None:
1906            is_data = check_data_validity(self.data)
1907        if not is_data and data is not None:
1908                flag = True
1909        if data is not None:
1910            if is_data:
1911                self.graph_id = self.data.group_id
1912                flag = (data.id != self.data.id)
1913        self.data = data
1914        if check_data_validity(data):
1915            self.graph_id = data.group_id
1916        self.data.group_id = self.graph_id
1917
1918        if self.data is None:
1919            data_name = ""
1920            self._set_bookmark_flag(False)
1921            self._keep.Enable(False)
1922            self._set_save_flag(False)
1923        else:
[c8e1996]1924            if self.model is not None:
[682c432]1925                self._set_bookmark_flag(not self.batch_on)
1926                self._keep.Enable(not self.batch_on)
1927            if self.data.is_data:
1928                self._set_save_flag(True)
1929                self._set_preview_flag(True)
1930
1931            # more disables for 2D
1932            if self.data.__class__.__name__ == "Data2D" or \
1933                        self.enable2D:
1934                self.slit_smearer.Disable()
1935                self.pinhole_smearer.Enable(True)
1936                self.default_mask = copy.deepcopy(self.data.mask)
[c8e1996]1937                if self.data.err_data is None or\
1938                        numpy.all(err == 1 for err in self.data.err_data) or \
1939                        not numpy.any(self.data.err_data):
[682c432]1940                    self.dI_didata.Enable(False)
1941                    self.dI_noweight.SetValue(True)
1942                    self.weightbt_string = self.dI_noweight.GetLabelText()
1943                else:
1944                    self.dI_didata.Enable(True)
1945                    self.dI_didata.SetValue(True)
1946                    self.weightbt_string = self.dI_didata.GetLabelText()
1947            else:
1948                self.slit_smearer.Enable(True)
1949                self.pinhole_smearer.Enable(True)
[c8e1996]1950
1951                if self.data.dy is None or\
1952                     numpy.all(self.data.dy == 1) or\
1953                     not numpy.any(self.data.dy):
[682c432]1954                    self.dI_didata.Enable(False)
1955                    self.dI_noweight.SetValue(True)
1956                    self.weightbt_string = self.dI_noweight.GetLabelText()
1957                else:
1958                    self.dI_didata.Enable(True)
1959                    self.dI_didata.SetValue(True)
1960                    self.weightbt_string = self.dI_didata.GetLabelText()
[c8e1996]1961            # Enable weighting radio buttons
[682c432]1962            self.dI_noweight.Enable(True)
1963            self.dI_sqrdata.Enable(True)
1964            self.dI_idata.Enable(True)
1965
1966            self.formfactorbox.Enable()
1967            self.structurebox.Enable()
1968            data_name = self.data.name
1969            _, _, npts = self.compute_data_range(self.data)
[c8e1996]1970            # set maximum range for x in linear scale
[682c432]1971            if not hasattr(self.data, "data"):  # Display only for 1D data fit
1972                self.btEditMask.Disable()
1973                self.EditMask_title.Disable()
1974            else:
1975                self.btEditMask.Enable()
1976                self.EditMask_title.Enable()
1977
1978        self.Npts_total.SetValue(str(npts))
[c8e1996]1979        # default:number of data points selected to fit
[682c432]1980        self.Npts_fit.SetValue(str(npts))
1981        self.Npts_total.SetEditable(False)
[c8e1996]1982        self.Npts_total.SetBackgroundColour(
[682c432]1983                                    self.GetParent().GetBackgroundColour())
1984
1985        self.Npts_total.Bind(wx.EVT_MOUSE_EVENTS, self._npts_click)
1986        self.pointsbox.Disable()
1987        self.dataSource.SetValue(data_name)
1988        self.state.data = data
1989        self.enable_fit_button()
1990        # send graph_id to page_finder
1991        self._manager.set_graph_id(uid=self.uid, graph_id=self.graph_id)
[c8e1996]1992        # focus the page
[682c432]1993        if check_data_validity(data):
1994            self.data_box_description.SetForegroundColour(wx.BLUE)
1995
1996        if self.batch_on:
1997            self.slit_smearer.Enable(False)
1998            self.pinhole_smearer.Enable(False)
1999            self.btEditMask.Disable()
2000            self.EditMask_title.Disable()
2001
2002        self.on_set_focus(None)
2003        self.Refresh()
[c8e1996]2004        # update model plot with new data information
[682c432]2005        if flag:
2006            if self.data.__class__.__name__ == "Data2D":
2007                self.enable2D = True
2008                self.model_view.SetLabel("2D Mode")
2009            else:
2010                self.enable2D = False
2011                self.model_view.SetLabel("1D Mode")
2012            self.model_view.Disable()
[c8e1996]2013            #  replace data plot on combo box selection
2014            # by removing the previous selected data
[682c432]2015            try:
2016                wx.PostEvent(self._manager.parent,
2017                             NewPlotEvent(action="delete",
2018                                          group_id=old_group_id, id=old_id))
2019            except:
2020                pass
[c8e1996]2021            # plot the current selected data
[682c432]2022            wx.PostEvent(self._manager.parent,
2023                         NewPlotEvent(action="check", plot=self.data,
2024                                      title=str(self.data.title)))
2025            self._draw_model()
2026
2027    def _npts_click(self, event):
2028        """
2029        Prevent further handling of the mouse event on Npts_total
2030        by not calling Skip().
2031        """
2032        pass
2033
2034    def reset_page(self, state, first=False):
2035        """
2036        reset the state
2037        """
2038        try:
2039            self.reset_page_helper(state)
2040
[c8e1996]2041            self.select_param()
2042            # Save state_fit
[682c432]2043            self.save_current_state_fit()
2044        except:
2045            self._show_combox_helper()
2046            msg = "Error: This model state has missing or outdated "
2047            msg += "information.\n"
[6c382da]2048            msg += traceback.format_exc()
[682c432]2049            wx.PostEvent(self._manager.parent,
2050                         StatusEvent(status=msg, info="error"))
2051        self._lay_out()
2052        self.Refresh()
2053
2054    def get_range(self):
2055        """
2056        return the fitting range
2057        """
2058        return float(self.qmin_x), float(self.qmax_x)
2059
2060    def get_npts2fit(self):
2061        """
2062        return numbers of data points within qrange
2063
2064        :Note: This is to normalize chisq by Npts of fit
2065
2066        """
2067        if self.data is None:
2068            return
2069        npts2fit = 0
2070        qmin, qmax = self.get_range()
2071        if self.data.__class__.__name__ == "Data2D" or \
2072                        self.enable2D:
2073            radius = numpy.sqrt(self.data.qx_data * self.data.qx_data +
2074                                self.data.qy_data * self.data.qy_data)
2075            index_data = (self.qmin_x <= radius) & (radius <= self.qmax_x)
2076            index_data = (index_data) & (self.data.mask)
2077            index_data = (index_data) & (numpy.isfinite(self.data.data))
2078            npts2fit = len(self.data.data[index_data])
2079        else:
2080            for qx in self.data.x:
[c8e1996]2081                if qmax >= qx >= qmin:
[682c432]2082                    npts2fit += 1
2083        return npts2fit
2084
2085    def show_npts2fit(self):
2086        """
2087        setValue Npts for fitting
2088        """
2089        self.Npts_fit.SetValue(str(self.get_npts2fit()))
2090
2091    def get_chi2(self):
2092        """
2093        return the current chi2
2094        """
2095        return self.tcChi.GetValue()
2096
2097    def onsetValues(self, chisqr, p_name, out, cov):
2098        """
2099        Build the panel from the fit result
2100
2101        :param chisqr: Value of the goodness of fit metric
2102        :param p_name: the name of parameters
2103        :param out: list of parameter with the best value found during fitting
2104        :param cov: Covariance matrix
2105
2106        """
2107
2108        # make sure stop button to fit button all the time
2109        self._on_fit_complete()
[c8e1996]2110        if out is None or not numpy.isfinite(chisqr):
[682c432]2111            raise ValueError, "Fit error occured..."
2112
2113        is_modified = False
2114        has_error = False
2115        dispersity = ''
2116
[c8e1996]2117        # Hide textctrl boxes of errors.
[682c432]2118        self._clear_Err_on_Fit()
2119
[c8e1996]2120        # Check if chi2 is finite
2121        if chisqr is not None and numpy.isfinite(chisqr):
2122            # format chi2
[682c432]2123            chi2 = format_number(chisqr, True)
2124            self.tcChi.SetValue(chi2)
2125            self.tcChi.Refresh()
2126        else:
2127            self.tcChi.SetValue("-")
2128
[c8e1996]2129        # Hide error title
[682c432]2130        if self.text2_3.IsShown() and not self.is_mac:
2131            self.text2_3.Hide()
2132
2133        try:
2134            if self.enable_disp.GetValue():
2135                if hasattr(self, "text_disp_1"):
[c8e1996]2136                    if self.text_disp_1 is not None and not self.is_mac:
[682c432]2137                        self.text_disp_1.Hide()
2138        except:
2139            dispersity = None
2140            pass
2141
2142        i = 0
[c8e1996]2143        # Set the panel when fit result are list
[682c432]2144
2145        for item in self.param_toFit:
[c8e1996]2146            if len(item) > 5 and item is not None:
[682c432]2147
2148                if item[0].IsShown():
[c8e1996]2149                    # reset error value to initial state
[682c432]2150                    if not self.is_mac:
2151                        item[3].Hide()
2152                        item[4].Hide()
2153                    for ind in range(len(out)):
2154                        if item[1] == p_name[ind]:
2155                            break
[c8e1996]2156                    if len(out) > 0 and out[ind] is not None:
[682c432]2157                        val_out = format_number(out[ind], True)
2158                        item[2].SetValue(val_out)
2159
[c8e1996]2160                    if(cov is not None and len(cov) == len(out)):
[682c432]2161                        try:
[c8e1996]2162                            if dispersity is not None:
[682c432]2163                                if self.enable_disp.GetValue():
2164                                    if hasattr(self, "text_disp_1"):
[c8e1996]2165                                        if self.text_disp_1 is not None:
[682c432]2166                                            if not self.text_disp_1.IsShown()\
[c8e1996]2167                                                  and not self.is_mac:
[682c432]2168                                                self.text_disp_1.Show(True)
2169                        except:
2170                            pass
2171
[c8e1996]2172                        if cov[ind] is not None:
[682c432]2173                            if numpy.isfinite(float(cov[ind])):
2174                                val_err = format_number(cov[ind], True)
[c8e1996]2175                                item[4].SetForegroundColour(wx.BLACK)
[373d4ee]2176                            else:
2177                                val_err = 'NaN'
[60d08fd]2178                                item[4].SetForegroundColour(wx.RED)
[373d4ee]2179                            if not self.is_mac:
2180                                item[3].Show(True)
2181                                item[4].Show(True)
2182                            item[4].SetValue(val_err)
2183                            has_error = True
[682c432]2184                i += 1
2185            else:
2186                raise ValueError, "onsetValues: Invalid parameters..."
[c8e1996]2187        # Show error title when any errors displayed
[682c432]2188        if has_error:
2189            if not self.text2_3.IsShown():
2190                self.text2_3.Show(True)
[c8e1996]2191        # save current state
[682c432]2192        self.save_current_state()
2193
2194        if not self.is_mac:
2195            self.Layout()
2196            self.Refresh()
2197        self._mac_sleep(0.1)
[c8e1996]2198        # plot model ( when drawing, do not update chisqr value again)
[682c432]2199        self._draw_model(update_chisqr=False, source='fit')
2200
2201    def onWeighting(self, event):
2202        """
2203        On Weighting radio button event, sets the weightbt_string
2204        """
2205        self.weightbt_string = event.GetEventObject().GetLabelText()
2206        self._set_weight()
2207
2208    def _set_weight(self, is_2D=None):
2209        """
2210        Set weight in fit problem
2211        """
2212        # compute weight for the current data
2213        flag_weight = self.get_weight_flag()
[c8e1996]2214        if is_2D is None:
[682c432]2215            is_2D = self._is_2D()
2216        self._manager.set_fit_weight(uid=self.uid,
2217                                     flag=flag_weight,
2218                                     is2d=is_2D,
2219                                     fid=None)
2220
2221    def onPinholeSmear(self, event):
2222        """
2223        Create a custom pinhole smear object that will change the way residuals
2224        are compute when fitting
2225
2226        :Note: accuracy is given by strings'High','Med', 'Low' FOR 2d,
2227                     None for 1D
2228
2229        """
2230        # Need update param values
2231        self._update_paramv_on_fit()
2232
[c8e1996]2233        if event is not None:
[682c432]2234            tcrtl = event.GetEventObject()
2235            # event case of radio button
[c8e1996]2236            if tcrtl.GetValue():
[682c432]2237                self.dx_min = 0.0
2238                self.dx_max = 0.0
2239                is_new_pinhole = True
2240            else:
2241                is_new_pinhole = self._is_changed_pinhole()
2242        else:
2243            is_new_pinhole = True
2244        # if any value is changed
2245        if is_new_pinhole:
[098f3d2]2246            self._set_pinhole_smear()
[682c432]2247        # hide all silt sizer
2248        self._hide_all_smear_info()
2249
2250        # show relevant slit sizers
2251        self._show_smear_sizer()
2252
2253        self.sizer_set_smearer.Layout()
[c8e1996]2254        # we need FitInside here not just self.Layout to ensure all the sizers
2255        # end up with the necessasary space to in the scroll panel. In
2256        # particular the compute and fit buttons end up on top of each other
2257        # PDB Nov 28 2015.
[1c2bf90]2258        self.FitInside()
[682c432]2259
[c8e1996]2260        if event is not None:
[682c432]2261            event.Skip()
[c8e1996]2262        # self._undo.Enable(True)
[682c432]2263        self.save_current_state()
2264        event = PageInfoEvent(page=self)
2265        wx.PostEvent(self.parent, event)
2266
2267    def _is_changed_pinhole(self):
2268        """
2269        check if any of pinhole smear is changed
2270
2271        :return: True or False
2272
2273        """
2274        # get the values
2275        pin_min = self.smear_pinhole_min.GetValue()
2276        pin_max = self.smear_pinhole_max.GetValue()
2277
2278        # Check changes in slit width
2279        try:
2280            dx_min = float(pin_min)
2281        except:
2282            return True
2283        if self.dx_min != dx_min:
2284            return True
2285
2286        # Check changes in slit heigth
2287        try:
2288            dx_max = float(pin_max)
2289        except:
2290            return True
2291        if self.dx_max != dx_max:
2292            return True
2293        return False
2294
2295    def _set_pinhole_smear(self):
2296        """
2297        Set custom pinhole smear
2298
2299        :return: msg
2300
2301        """
2302        # copy data
2303        data = copy.deepcopy(self.data)
2304        if self._is_2D():
2305            self.smear_type = 'Pinhole2d'
2306            len_data = len(data.data)
2307            data.dqx_data = numpy.zeros(len_data)
2308            data.dqy_data = numpy.zeros(len_data)
2309        else:
2310            self.smear_type = 'Pinhole'
2311            len_data = len(data.x)
2312            data.dx = numpy.zeros(len_data)
2313            data.dxl = None
2314            data.dxw = None
2315        msg = None
2316
2317        get_pin_min = self.smear_pinhole_min
2318        get_pin_max = self.smear_pinhole_max
2319
2320        if not check_float(get_pin_min):
2321            get_pin_min.SetBackgroundColour("pink")
2322            msg = "Model Error:wrong value entered!!!"
2323        elif not check_float(get_pin_max):
2324            get_pin_max.SetBackgroundColour("pink")
2325            msg = "Model Error:wrong value entered!!!"
2326        else:
2327            if len_data < 2:
2328                len_data = 2
2329            self.dx_min = float(get_pin_min.GetValue())
2330            self.dx_max = float(get_pin_max.GetValue())
2331            if self.dx_min < 0:
2332                get_pin_min.SetBackgroundColour("pink")
2333                msg = "Model Error:This value can not be negative!!!"
2334            elif self.dx_max < 0:
2335                get_pin_max.SetBackgroundColour("pink")
2336                msg = "Model Error:This value can not be negative!!!"
[c8e1996]2337            elif self.dx_min is not None and self.dx_max is not None:
[682c432]2338                if self._is_2D():
2339                    data.dqx_data[data.dqx_data == 0] = self.dx_min
2340                    data.dqy_data[data.dqy_data == 0] = self.dx_max
2341                elif self.dx_min == self.dx_max:
2342                    data.dx[data.dx == 0] = self.dx_min
2343                else:
2344                    step = (self.dx_max - self.dx_min) / (len_data - 1)
2345                    data.dx = numpy.arange(self.dx_min,
2346                                           self.dx_max + step / 1.1,
2347                                           step)
[c8e1996]2348            elif self.dx_min is not None:
[682c432]2349                if self._is_2D():
2350                    data.dqx_data[data.dqx_data == 0] = self.dx_min
2351                else:
2352                    data.dx[data.dx == 0] = self.dx_min
[c8e1996]2353            elif self.dx_max is not None:
[682c432]2354                if self._is_2D():
2355                    data.dqy_data[data.dqy_data == 0] = self.dx_max
2356                else:
2357                    data.dx[data.dx == 0] = self.dx_max
2358            self.current_smearer = smear_selection(data, self.model)
2359            # 2D need to set accuracy
2360            if self._is_2D():
[c8e1996]2361                self.current_smearer.set_accuracy(
2362                    accuracy=self.smear2d_accuracy)
[682c432]2363
[c8e1996]2364        if msg is not None:
[682c432]2365            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2366        else:
2367            get_pin_min.SetBackgroundColour("white")
2368            get_pin_max.SetBackgroundColour("white")
[c8e1996]2369        # set smearing value whether or not the data contain the smearing info
[682c432]2370
[373d4ee]2371        enable_smearer = not self.disable_smearer.GetValue()
[682c432]2372        self._manager.set_smearer(smearer=self.current_smearer,
[373d4ee]2373                                  fid=self.data.id,
2374                                  qmin=float(self.qmin_x),
2375                                  qmax=float(self.qmax_x),
2376                                  enable_smearer=enable_smearer,
2377                                  uid=self.uid)
[682c432]2378        return msg
2379
2380    def update_pinhole_smear(self):
2381        """
2382        called by kill_focus on pinhole TextCntrl
2383        to update the changes
2384
2385        :return: False when wrong value was entered
2386
2387        """
2388        # msg default
2389        msg = None
2390        # check if any value is changed
2391        if self._is_changed_pinhole():
2392            msg = self._set_pinhole_smear()
2393        wx.CallAfter(self.save_current_state)
2394
[c8e1996]2395        if msg is not None:
[682c432]2396            return False
2397        else:
2398            return True
2399
2400    def onSlitSmear(self, event):
2401        """
2402        Create a custom slit smear object that will change the way residuals
2403        are compute when fitting
2404        """
2405        # Need update param values
2406        self._update_paramv_on_fit()
2407
2408        # msg default
2409        msg = None
2410        # for event given
[c8e1996]2411        if event is not None:
[682c432]2412            tcrtl = event.GetEventObject()
2413            # event case of radio button
2414            if tcrtl.GetValue():
2415                self.dxl = 0.0
2416                self.dxw = 0.0
2417                is_new_slit = True
2418            else:
2419                is_new_slit = self._is_changed_slit()
2420        else:
2421            is_new_slit = True
2422
2423        # if any value is changed
2424        if is_new_slit:
2425            msg = self._set_slit_smear()
2426
2427        # hide all silt sizer
2428        self._hide_all_smear_info()
2429        # show relevant slit sizers
2430        self._show_smear_sizer()
2431        self.sizer_set_smearer.Layout()
[c8e1996]2432        # we need FitInside here not just self.Layout to ensure all the sizers
2433        # end up with the necessasary space to in the scroll panel. In
2434        # particular the compute and fit buttons end up on top of each other
2435        # PDB Nov 28 2015.
[1c2bf90]2436        self.FitInside()
[682c432]2437
[c8e1996]2438        if event is not None:
[682c432]2439            event.Skip()
2440        self.save_current_state()
2441        event = PageInfoEvent(page=self)
2442        wx.PostEvent(self.parent, event)
[c8e1996]2443        if msg is not None:
[682c432]2444            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2445
2446    def _is_changed_slit(self):
2447        """
2448        check if any of slit lengths is changed
2449
2450        :return: True or False
2451
2452        """
2453        # get the values
2454        width = self.smear_slit_width.GetValue()
2455        height = self.smear_slit_height.GetValue()
2456
2457        # check and change the box bg color if it was pink
2458        #    but it should be white now
2459        # because this is the case that _set_slit_smear() will not handle
2460        if height.lstrip().rstrip() == "":
2461            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2462        if width.lstrip().rstrip() == "":
2463            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2464
2465        # Check changes in slit width
2466        if width == "":
2467            dxw = 0.0
2468        else:
2469            try:
2470                dxw = float(width)
2471            except:
2472                return True
2473        if self.dxw != dxw:
2474            return True
2475
2476        # Check changes in slit heigth
2477        if height == "":
2478            dxl = 0.0
2479        else:
2480            try:
2481                dxl = float(height)
2482            except:
2483                return True
2484        if self.dxl != dxl:
2485            return True
2486
2487        return False
2488
2489    def _set_slit_smear(self):
2490        """
2491        Set custom slit smear
2492
2493        :return: message to inform the user about the validity
2494            of the values entered for slit smear
2495        """
[373d4ee]2496        if self.data.__class__.__name__ == "Data2D" or self.enable2D:
[682c432]2497            return
2498        # make sure once more if it is smearer
2499        data = copy.deepcopy(self.data)
2500        data_len = len(data.x)
2501        data.dx = None
2502        data.dxl = None
2503        data.dxw = None
2504        msg = None
2505
2506        try:
2507            self.dxl = float(self.smear_slit_height.GetValue())
2508            data.dxl = self.dxl * numpy.ones(data_len)
2509            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2510        except:
2511            self.dxl = None
2512            data.dxl = numpy.zeros(data_len)
2513            if self.smear_slit_height.GetValue().lstrip().rstrip() != "":
2514                self.smear_slit_height.SetBackgroundColour("pink")
2515                msg = "Wrong value entered... "
2516            else:
2517                self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2518        try:
2519            self.dxw = float(self.smear_slit_width.GetValue())
2520            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2521            data.dxw = self.dxw * numpy.ones(data_len)
2522        except:
2523            self.dxw = None
2524            data.dxw = numpy.zeros(data_len)
2525            if self.smear_slit_width.GetValue().lstrip().rstrip() != "":
2526                self.smear_slit_width.SetBackgroundColour("pink")
2527                msg = "Wrong Fit value entered... "
2528            else:
2529                self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2530
2531        self.current_smearer = smear_selection(data, self.model)
[c8e1996]2532        # set smearing value whether or not the data contain the smearing info
[373d4ee]2533        enable_smearer = not self.disable_smearer.GetValue()
[682c432]2534        self._manager.set_smearer(smearer=self.current_smearer,
[373d4ee]2535                                  fid=self.data.id,
2536                                  qmin=float(self.qmin_x),
2537                                  qmax=float(self.qmax_x),
2538                                  enable_smearer=enable_smearer,
2539                                  uid=self.uid)
[682c432]2540        return msg
2541
2542    def update_slit_smear(self):
2543        """
2544        called by kill_focus on pinhole TextCntrl
2545        to update the changes
2546
2547        :return: False when wrong value was entered
2548
2549        """
2550        # msg default
2551        msg = None
2552        # check if any value is changed
2553        if self._is_changed_slit():
2554            msg = self._set_slit_smear()
[c8e1996]2555        # self._undo.Enable(True)
[682c432]2556        self.save_current_state()
2557
[c8e1996]2558        if msg is not None:
[682c432]2559            return False
2560        else:
2561            return True
2562
2563    def onSmear(self, event):
2564        """
2565        Create a smear object that will change the way residuals
[1c2bf90]2566        are computed when fitting
[682c432]2567        """
[c8e1996]2568        if event is not None:
[682c432]2569            event.Skip()
2570        if self.data is None:
2571            return
2572
2573        # Need update param values
2574        self._update_paramv_on_fit()
[373d4ee]2575        if self.model is not None:
[682c432]2576            if self.data.is_data:
2577                self._manager.page_finder[self.uid].add_data(data=self.data)
2578        temp_smearer = self.on_smear_helper()
2579
2580        self.sizer_set_smearer.Layout()
[c8e1996]2581        # we need FitInside here not just self.Layout to ensure all the sizers
2582        # end up with the necessasary space to in the scroll panel. In
2583        # particular the compute and fit buttons end up on top of each other
2584        # PDB Nov 28 2015.
[1c2bf90]2585        self.FitInside()
[682c432]2586        self._set_weight()
2587
[c8e1996]2588        # set smearing value whether or not the data contain the smearing info
[373d4ee]2589        enable_smearer = not self.disable_smearer.GetValue()
[682c432]2590        wx.CallAfter(self._manager.set_smearer, uid=self.uid,
2591                     smearer=temp_smearer,
2592                     fid=self.data.id,
2593                     qmin=float(self.qmin_x),
2594                     qmax=float(self.qmax_x),
[934ce649]2595                     enable_smearer=enable_smearer)
[682c432]2596
2597        self.state.enable_smearer = self.enable_smearer.GetValue()
2598        self.state.disable_smearer = self.disable_smearer.GetValue()
2599        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
2600        self.state.slit_smearer = self.slit_smearer.GetValue()
2601
2602    def on_smear_helper(self, update=False):
2603        """
2604        Help for onSmear
2605
2606        :param update: force or not to update
2607        """
2608        self._get_smear_info()
[c8e1996]2609        # renew smear sizer
[373d4ee]2610        if self.smear_type is not None:
[682c432]2611            self.smear_description_smear_type.SetValue(str(self.smear_type))
2612            self.smear_data_left.SetValue(str(self.dq_l))
2613            self.smear_data_right.SetValue(str(self.dq_r))
2614
2615        self._hide_all_smear_info()
2616        data = copy.deepcopy(self.data)
2617
2618        # make sure once more if it is smearer
2619        temp_smearer = smear_selection(data, self.model)
2620        if self.current_smearer != temp_smearer or update:
2621            self.current_smearer = temp_smearer
2622        if self.enable_smearer.GetValue():
[373d4ee]2623            if self.current_smearer is None:
[682c432]2624                wx.PostEvent(self._manager.parent,
2625                    StatusEvent(status="Data contains no smearing information"))
2626            else:
2627                wx.PostEvent(self._manager.parent,
2628                    StatusEvent(status="Data contains smearing information"))
2629
2630            self.smear_data_left.Show(True)
2631            self.smear_data_right.Show(True)
2632            temp_smearer = self.current_smearer
2633        elif self.disable_smearer.GetValue():
2634            self.smear_description_none.Show(True)
2635        elif self.pinhole_smearer.GetValue():
2636            self.onPinholeSmear(None)
2637        elif self.slit_smearer.GetValue():
2638            self.onSlitSmear(None)
2639        self._show_smear_sizer()
2640
2641        return temp_smearer
2642
2643    def on_complete_chisqr(self, event):
2644        """
2645        Display result chisqr on the panel
2646        :event: activated by fitting/ complete after draw
2647        """
2648        try:
[c8e1996]2649            if event is None:
[682c432]2650                output = "-"
2651            elif not numpy.isfinite(event.output):
2652                output = "-"
2653            else:
2654                output = event.output
2655            self.tcChi.SetValue(str(format_number(output, True)))
2656            self.state.tcChi = self.tcChi.GetValue()
2657        except:
2658            pass
2659
2660    def get_all_checked_params(self):
2661        """
2662        Found all parameters current check and add them to list of parameters
2663        to fit
2664        """
2665        self.param_toFit = []
2666        for item in self.parameters:
2667            if item[0].GetValue() and item not in self.param_toFit:
2668                if item[0].IsShown():
2669                    self.param_toFit.append(item)
2670        for item in self.fittable_param:
2671            if item[0].GetValue() and item not in self.param_toFit:
2672                if item[0].IsShown():
2673                    self.param_toFit.append(item)
2674        self.save_current_state_fit()
2675
2676        event = PageInfoEvent(page=self)
2677        wx.PostEvent(self.parent, event)
2678        param2fit = []
2679        for item in self.param_toFit:
2680            if item[0] and item[0].IsShown():
2681                param2fit.append(item[1])
2682        self._manager.set_param2fit(self.uid, param2fit)
2683
[c8e1996]2684    def select_param(self, event=None):
[682c432]2685        """
2686        Select TextCtrl  checked for fitting purpose and stores them
2687        in  self.param_toFit=[] list
2688        """
2689        self.param_toFit = []
2690        for item in self.parameters:
[c8e1996]2691            # Skip t ifhe angle parameters if 1D data
[682c432]2692            if self.data.__class__.__name__ != "Data2D" and\
2693                        not self.enable2D:
2694                if item in self.orientation_params:
2695                    continue
[c8e1996]2696            # Select parameters to fit for list of primary parameters
[682c432]2697            if item[0].GetValue() and item[0].IsShown():
2698                if not (item in self.param_toFit):
2699                    self.param_toFit.append(item)
2700            else:
2701                #remove parameters from the fitting list
2702                if item in self.param_toFit:
2703                    self.param_toFit.remove(item)
2704
[c8e1996]2705        # Select parameters to fit for list of fittable parameters
[682c432]2706        #        with dispersion
2707        for item in self.fittable_param:
[c8e1996]2708            # Skip t ifhe angle parameters if 1D data
[682c432]2709            if self.data.__class__.__name__ != "Data2D" and\
2710                        not self.enable2D:
2711                if item in self.orientation_params:
2712                    continue
2713            if item[0].GetValue() and item[0].IsShown():
2714                if not (item in self.param_toFit):
2715                    self.param_toFit.append(item)
2716            else:
[c8e1996]2717                # remove parameters from the fitting list
[682c432]2718                if item in self.param_toFit:
2719                    self.param_toFit.remove(item)
2720
[c8e1996]2721        # Calculate num. of angle parameters
[682c432]2722        if self.data.__class__.__name__ == "Data2D" or \
2723                       self.enable2D:
2724            len_orient_para = 0
2725        else:
2726            len_orient_para = len(self.orientation_params)  # assume even len
[c8e1996]2727        # Total num. of angle parameters
[682c432]2728        if len(self.fittable_param) > 0:
2729            len_orient_para *= 2
2730
2731        self.save_current_state_fit()
[c8e1996]2732        if event is not None:
2733            # post state to fit panel
[682c432]2734            event = PageInfoEvent(page=self)
2735            wx.PostEvent(self.parent, event)
2736
2737        param2fit = []
2738        for item in self.param_toFit:
2739            if item[0] and item[0].IsShown():
2740                param2fit.append(item[1])
2741        self._manager.set_param2fit(self.uid, param2fit)
2742
2743    def set_model_param_sizer(self, model):
2744        """
2745        Build the panel from the model content
2746
2747        :param model: the model selected in combo box for fitting purpose
2748
2749        """
2750        self.sizer3.Clear(True)
2751        self.parameters = []
2752        self.str_parameters = []
2753        self.param_toFit = []
2754        self.fittable_param = []
2755        self.fixed_param = []
2756        self.orientation_params = []
2757        self.orientation_params_disp = []
2758
[c8e1996]2759        if model is None:
[682c432]2760            self.sizer3.Layout()
2761            self.SetupScrolling()
2762            return
2763
[6f16e25]2764        box_description = wx.StaticBox(self, wx.ID_ANY, str("Model Parameters"))
[682c432]2765        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
2766        sizer = wx.GridBagSizer(5, 5)
[c8e1996]2767        # save the current model
[682c432]2768        self.model = model
2769
2770        keys = self.model.getParamList()
2771
[c8e1996]2772        # list of dispersion parameters
[682c432]2773        self.disp_list = self.model.getDispParamList()
2774
2775        def custom_compare(a, b):
2776            """
2777            Custom compare to order, first by alphabets then second by number.
2778            """
2779            # number at the last digit
2780            a_last = a[len(a) - 1]
2781            b_last = b[len(b) - 1]
2782            # default
2783            num_a = None
2784            num_b = None
2785            # split the names
2786            a2 = a.lower().split('_')
2787            b2 = b.lower().split('_')
2788            # check length of a2, b2
2789            len_a2 = len(a2)
2790            len_b2 = len(b2)
2791            # check if it contains a int number(<10)
2792            try:
2793                num_a = int(a_last)
2794            except:
2795                pass
2796            try:
2797                num_b = int(b_last)
2798            except:
2799                pass
2800            # Put 'scale' near the top; happens
2801            # when numbered param name exists
2802            if a == 'scale':
2803                return -1
2804            # both have a number
[c8e1996]2805            if num_a is not None and num_b is not None:
[682c432]2806                if num_a > num_b:
2807                    return -1
2808                # same number
2809                elif num_a == num_b:
2810                    # different last names
2811                    if a2[len_a2 - 1] != b2[len_b2 - 1] and num_a != 0:
2812                        return -cmp(a2[len_a2 - 1], b2[len_b2 - 1])
2813                    else:
2814                        return cmp(a, b)
2815                else:
2816                    return 1
2817            # one of them has a number
[c8e1996]2818            elif num_a is not None:
[682c432]2819                return 1
[c8e1996]2820            elif num_b is not None:
[682c432]2821                return -1
2822            # no numbers
2823            else:
2824                return cmp(a.lower(), b.lower())
[7d265a8]2825       
[c8e1996]2826        # keys obtained now from ordered dict, so commenting alphabetical
2827        # ordering keys.sort(custom_compare)
[682c432]2828
2829        iy = 0
2830        ix = 0
[4c3be25]2831        sizer.Add(wx.StaticText(self, wx.ID_ANY, 'Parameter'),
2832                  (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[682c432]2833        ix += 1
[6f16e25]2834        self.text2_2 = wx.StaticText(self, wx.ID_ANY, 'Value')
[c8e1996]2835        sizer.Add(self.text2_2, (iy, ix), (1, 1),
[682c432]2836                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2837        ix += 2
[6f16e25]2838        self.text2_3 = wx.StaticText(self, wx.ID_ANY, 'Error')
[c8e1996]2839        sizer.Add(self.text2_3, (iy, ix), (1, 1),
2840                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[682c432]2841        if not self.is_mac:
2842            self.text2_3.Hide()
2843        ix += 1
[6f16e25]2844        self.text2_min = wx.StaticText(self, wx.ID_ANY, 'Min')
[c8e1996]2845        sizer.Add(self.text2_min, (iy, ix), (1, 1),
2846                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2847        # self.text2_min.Hide()
[682c432]2848        ix += 1
[6f16e25]2849        self.text2_max = wx.StaticText(self, wx.ID_ANY, 'Max')
[c8e1996]2850        sizer.Add(self.text2_max, (iy, ix), (1, 1),
2851                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2852        # self.text2_max.Hide()
[682c432]2853        ix += 1
[6f16e25]2854        self.text2_4 = wx.StaticText(self, wx.ID_ANY, '[Units]')
[c8e1996]2855        sizer.Add(self.text2_4, (iy, ix), (1, 1),
2856                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[682c432]2857        self.text2_4.Hide()
2858
[4c3be25]2859        CHECK_STATE = False
[682c432]2860        for item in keys:
2861
[c8e1996]2862            if item not in self.disp_list and not item in \
[682c432]2863                    self.model.orientation_params:
2864
[c8e1996]2865                # prepare a spot to store errors
2866                if item not in self.model.details:
[682c432]2867                    self.model.details[item] = ["", None, None]
2868
2869                iy += 1
2870                ix = 0
[c8e1996]2871                if (self.model.__class__ in
2872                    self.model_list_box["Multi-Functions"] or
[682c432]2873                    self.temp_multi_functional)\
2874                    and (item in self.model.non_fittable):
[6f16e25]2875                    non_fittable_name = wx.StaticText(self, wx.ID_ANY, item)
[c8e1996]2876                    sizer.Add(non_fittable_name, (iy, ix), (1, 1),
[682c432]2877                            wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 21)
[c8e1996]2878                    # add parameter value
[682c432]2879                    ix += 1
2880                    value = self.model.getParam(item)
2881                    if len(self.model.fun_list) > 0:
[c8e1996]2882                        # num = item.split('_')[1][5:7]
[6f16e25]2883                        fun_box = wx.ComboBox(self, wx.ID_ANY, size=(100, -1),
[682c432]2884                                    style=wx.CB_READONLY, name='%s' % item)
2885                        self._set_fun_box_list(fun_box)
2886                        fun_box.SetSelection(0)
[c8e1996]2887                        # self.fun_box.SetToolTipString("A function
[682c432]2888                        #    describing the interface")
[6f16e25]2889                        wx.EVT_COMBOBOX(fun_box, wx.ID_ANY, self._on_fun_box)
[682c432]2890                    else:
[6f16e25]2891                        fun_box = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]2892                                                size=(_BOX_WIDTH, 20),
[682c432]2893                                style=wx.TE_PROCESS_ENTER, name='%s' % item)
[c8e1996]2894                        fun_box.SetToolTipString(
[682c432]2895                                "Hit 'Enter' after typing to update the plot.")
2896                        fun_box.SetValue(format_number(value, True))
2897                    sizer.Add(fun_box, (iy, ix), (1, 1), wx.EXPAND)
2898                    self.str_parameters.append([None, item, fun_box,
2899                                                None, None, None,
2900                                                None, None])
2901                else:
[c8e1996]2902                    # add parameters name with checkbox for selecting to fit
[6f16e25]2903                    cb = wx.CheckBox(self, wx.ID_ANY, item)
[682c432]2904                    cb.SetValue(CHECK_STATE)
2905                    cb.SetToolTipString(" Check mark to fit.")
[c8e1996]2906                    # cb.SetValue(True)
[682c432]2907                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
2908
2909                    sizer.Add(cb, (iy, ix), (1, 1),
2910                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
2911
[c8e1996]2912                    # add parameter value
[682c432]2913                    ix += 1
2914                    value = self.model.getParam(item)
[6f16e25]2915                    ctl1 = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
[373d4ee]2916                                         style=wx.TE_PROCESS_ENTER)
[c8e1996]2917                    ctl1.SetToolTipString(
[682c432]2918                                "Hit 'Enter' after typing to update the plot.")
2919                    ctl1.SetValue(format_number(value, True))
2920                    sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
[c8e1996]2921                    # text to show error sign
[682c432]2922                    ix += 1
[6f16e25]2923                    text2 = wx.StaticText(self, wx.ID_ANY, '+/-')
[c8e1996]2924                    sizer.Add(text2, (iy, ix), (1, 1),
[682c432]2925                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2926                    if not self.is_mac:
2927                        text2.Hide()
2928                    ix += 1
[6f16e25]2929                    ctl2 = wx.TextCtrl(self, wx.ID_ANY,
[682c432]2930                                       size=(_BOX_WIDTH / 1.2, 20), style=0)
2931                    sizer.Add(ctl2, (iy, ix), (1, 1),
2932                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2933                    if not self.is_mac:
2934                        ctl2.Hide()
2935
2936                    ix += 1
[6f16e25]2937                    ctl3 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]2938                                         size=(_BOX_WIDTH / 1.9, 20),
2939                                         style=wx.TE_PROCESS_ENTER,
[682c432]2940                                text_enter_callback=self._onparamRangeEnter)
2941                    min_bound = self.model.details[item][1]
2942                    if min_bound is not None:
2943                        ctl3.SetValue(format_number(min_bound, True))
2944
2945                    sizer.Add(ctl3, (iy, ix), (1, 1),
2946                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2947
2948                    ix += 1
[6f16e25]2949                    ctl4 = ModelTextCtrl(self, wx.ID_ANY,
[373d4ee]2950                                         size=(_BOX_WIDTH / 1.9, 20),
2951                                         style=wx.TE_PROCESS_ENTER,
[682c432]2952                                text_enter_callback=self._onparamRangeEnter)
2953                    max_bound = self.model.details[item][2]
2954                    if max_bound is not None:
2955                        ctl4.SetValue(format_number(max_bound, True))
2956                    sizer.Add(ctl4, (iy, ix), (1, 1),
2957                              wx.EXPAND | wx.FIXED_MINSIZE, 0)
2958
2959                    ix += 1
2960                    # Units
2961                    if item in self.model.details:
[6f16e25]2962                        units = wx.StaticText(self, wx.ID_ANY,
[682c432]2963                            self.model.details[item][0], style=wx.ALIGN_LEFT)
2964                    else:
[6f16e25]2965                        units = wx.StaticText(self, wx.ID_ANY, "",
[682c432]2966                                              style=wx.ALIGN_LEFT)
2967                    sizer.Add(units, (iy, ix), (1, 1),
2968                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2969
2970                    self.parameters.append([cb, item, ctl1,
2971                                            text2, ctl2, ctl3, ctl4, units])
2972
2973        iy += 1
2974        sizer.Add((10, 10), (iy, ix), (1, 1),
2975                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2976
2977        # type can be either Guassian or Array
2978        if len(self.model.dispersion.values()) > 0:
2979            type = self.model.dispersion.values()[0]["type"]
2980        else:
2981            type = "Gaussian"
2982
2983        iy += 1
2984        ix = 0
[c8e1996]2985        # Add tile for orientational angle
[682c432]2986        for item in keys:
2987            if item in self.model.orientation_params:
[6f16e25]2988                orient_angle = wx.StaticText(self, wx.ID_ANY, '[For 2D only]:')
2989                mag_on_button = wx.Button(self, wx.ID_ANY, "Magnetic ON")
[7116dffd]2990                mag_on_button.SetToolTipString("Turn Pol Beam/Mag scatt on/off")
[682c432]2991                mag_on_button.Bind(wx.EVT_BUTTON, self._on_mag_on)
[c8e1996]2992                mag_angle_help_button = wx.Button(self, wx.ID_ANY,
2993                                                  "Magnetic angles?")
[7116dffd]2994                mag_angle_help_button.SetToolTipString("see angle definitions")
[6f16e25]2995                mag_help_button = wx.Button(self, wx.ID_ANY, "Mag HELP")
[7116dffd]2996                mag_help_button.SetToolTipString("Help on pol beam/mag fitting")
[682c432]2997                mag_help_button.Bind(wx.EVT_BUTTON, self._on_mag_help)
[c8e1996]2998                mag_angle_help_button.Bind(wx.EVT_BUTTON,
[7116dffd]2999                                            self._on_mag_angle_help)
[682c432]3000                sizer.Add(orient_angle, (iy, ix), (1, 1),
3001                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
3002                iy += 1
3003                sizer.Add(mag_on_button, (iy, ix), (1, 1),
3004                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[7116dffd]3005                ix += 1
3006                sizer.Add(mag_angle_help_button, (iy, ix), (1, 1),
3007                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[682c432]3008                sizer.Add(mag_help_button, (iy, ix + 1), (1, 1),
3009                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
3010
[c8e1996]3011                # handle the magnetic buttons
3012                # clean this up so that assume mag is off then turn
3013                # all buttons on IF mag has mag and has 2D
[682c432]3014                if not self._has_magnetic:
3015                    mag_on_button.Show(False)
3016                elif not self.data.__class__.__name__ == "Data2D":
3017                    mag_on_button.Show(False)
3018                else:
3019                    mag_on_button.Show(True)
3020                mag_help_button.Show(False)
[2b58fa5]3021                mag_angle_help_button.Show(False)
[682c432]3022                if mag_on_button.IsShown():
3023                    if self.magnetic_on:
3024                        mag_on_button.SetLabel("Magnetic OFF")
3025                        mag_help_button.Show(True)
[7116dffd]3026                        mag_angle_help_button.Show(True)
[682c432]3027                    else:
3028                        mag_on_button.SetLabel("Magnetic ON")
3029                        mag_help_button.Show(False)
[7116dffd]3030                        mag_angle_help_button.Show(False)
[682c432]3031
3032                if not self.data.__class__.__name__ == "Data2D" and \
3033                        not self.enable2D:
3034                    orient_angle.Hide()
3035                else:
3036                    orient_angle.Show(True)
3037                break
3038
[c8e1996]3039        # For Gaussian only
[682c432]3040        if type.lower() != "array":
3041            for item in self.model.orientation_params:
3042                if not self.magnetic_on:
3043                    if item in self.model.magnetic_params:
3044                        continue
[c8e1996]3045                if item not in self.disp_list:
3046                    # prepare a spot to store min max
3047                    if item not in self.model.details:
[682c432]3048                        self.model.details[item] = ["", None, None]
3049
3050                    iy += 1
3051                    ix = 0
[c8e1996]3052                    # add parameters name with checkbox for selecting to fit
[6f16e25]3053                    cb = wx.CheckBox(self, wx.ID_ANY, item)
[682c432]3054                    cb.SetValue(CHECK_STATE)
3055                    cb.SetToolTipString("Check mark to fit")
3056                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
3057                    if self.data.__class__.__name__ == "Data2D" or \
3058                            self.enable2D:
3059                        cb.Show(True)
3060                    else:
3061                        cb.Hide()
3062                    sizer.Add(cb, (iy, ix), (1, 1),
3063                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
3064
[c8e1996]3065                    # add parameter value
[682c432]3066                    ix += 1
3067                    value = self.model.getParam(item)
[373d4ee]3068                    ctl1 = ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
3069                                         style=wx.TE_PROCESS_ENTER)
[c8e1996]3070                    ctl1.SetToolTipString(
[682c432]3071                                "Hit 'Enter' after typing to update the plot.")
3072                    ctl1.SetValue(format_number(value, True))
3073                    if self.data.__class__.__name__ == "Data2D" or \
3074                            self.enable2D:
3075                        ctl1.Show(True)
3076                    else:
3077                        ctl1.Hide()
3078                    sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
[c8e1996]3079                    # text to show error sign
[682c432]3080                    ix += 1
3081                    text2 = wx.StaticText(self, -1, '+/-')
[c8e1996]3082                    sizer.Add(text2, (iy, ix), (1, 1),
[682c432]3083                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3084
3085                    text2.Hide()
3086                    ix += 1
3087                    ctl2 = wx.TextCtrl(self, -1,
3088                                       size=(_BOX_WIDTH / 1.2, 20), style=0)
3089                    sizer.Add(ctl2, (iy, ix), (1, 1),
3090                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3091
3092                    ctl2.Hide()
3093
3094                    ix += 1
[373d4ee]3095                    ctl3 = ModelTextCtrl(self, -1,
3096                                         size=(_BOX_WIDTH / 1.8, 20),
3097                                         style=wx.TE_PROCESS_ENTER,
[682c432]3098                                text_enter_callback=self._onparamRangeEnter)
3099
3100                    sizer.Add(ctl3, (iy, ix), (1, 1),
3101                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3102                    ctl3.Hide()
3103
3104                    ix += 1
[373d4ee]3105                    ctl4 = ModelTextCtrl(self, -1,
3106                                         size=(_BOX_WIDTH / 1.8, 20),
3107                                         style=wx.TE_PROCESS_ENTER,
[682c432]3108                            text_enter_callback=self._onparamRangeEnter)
3109                    sizer.Add(ctl4, (iy, ix), (1, 1),
3110                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3111
3112                    ctl4.Hide()
3113
3114                    if self.data.__class__.__name__ == "Data2D" or \
3115                            self.enable2D:
3116                        if self.is_mac:
3117                            text2.Show(True)
3118                            ctl2.Show(True)
3119                        ctl3.Show(True)
3120                        ctl4.Show(True)
3121
3122                    ix += 1
3123                    # Units
3124                    if item in self.model.details:
3125                        units = wx.StaticText(self, -1,
3126                                              self.model.details[item][0],
3127                                              style=wx.ALIGN_LEFT)
3128                    else:
3129                        units = wx.StaticText(self, -1, "",
3130                                              style=wx.ALIGN_LEFT)
3131                    if self.data.__class__.__name__ == "Data2D" or \
3132                            self.enable2D:
3133                        units.Show(True)
3134                    else:
3135                        units.Hide()
3136
3137                    sizer.Add(units, (iy, ix), (1, 1),
3138                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3139
3140                    self.parameters.append([cb, item, ctl1,
3141                                            text2, ctl2, ctl3, ctl4, units])
3142                    self.orientation_params.append([cb, item, ctl1,
3143                                            text2, ctl2, ctl3, ctl4, units])
3144
3145        iy += 1
3146        box_description.SetForegroundColour(wx.BLUE)
[c8e1996]3147        # Display units text on panel
[682c432]3148        for item in keys:
3149            if item in self.model.details:
3150                self.text2_4.Show()
[c8e1996]3151        # Fill the list of fittable parameters
[682c432]3152        self.get_all_checked_params()
3153        self.save_current_state_fit()
3154        boxsizer1.Add(sizer)
3155        self.sizer3.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3156        self.sizer3.Layout()
3157        self.Layout()
3158
3159    def on_right_down(self, event):
3160        """
3161        Get key stroke event
3162        """
[c8e1996]3163        if self.data is None:
[682c432]3164            return
3165        # Figuring out key combo: Cmd for copy, Alt for paste
3166        if event.AltDown() and event.ShiftDown():
3167            flag = True
3168        elif event.AltDown() or event.ShiftDown():
3169            flag = False
3170        else:
3171            return
3172        # make event free
3173        event.Skip()
3174        # messages depending on the flag
3175        if not flag:
3176            infor = 'warning'
3177            # inform msg to wx
3178            wx.PostEvent(self._manager.parent,
3179                        StatusEvent(status=msg, info=infor))
3180
3181    def _onModel2D(self, event):
3182        """
3183        toggle view of model from 1D to 2D  or 2D from 1D
3184        """
3185        if self.model_view.GetLabelText() == "Show 2D":
3186            self.model_view.SetLabel("Show 1D")
3187            self.enable2D = True
3188
3189        else:
3190            self.model_view.SetLabel("Show 2D")
3191            self.enable2D = False
3192        self.Show(False)
3193        self.create_default_data()
3194        self._manager.store_data(self.uid, data_list=[self.data])
3195
3196        self.set_model_param_sizer(self.model)
3197        self._set_sizer_dispersion()
3198        self._set_weight(is_2D=self.enable2D)
3199        self._set_smear_buttons()
3200        self.Show(True)
3201        self.SetupScrolling()
3202        self._draw_model()
3203
3204        self.state.enable2D = copy.deepcopy(self.enable2D)
3205
3206    def _set_smear_buttons(self):
3207        """
3208        Set semarer radio buttons
3209        """
3210        # more disables for 2D
3211        if self.data.__class__.__name__ == "Data2D" or \
3212                    self.enable2D:
3213            self.slit_smearer.Disable()
3214            self.pinhole_smearer.Enable(True)
3215            self.default_mask = copy.deepcopy(self.data.mask)
3216        else:
3217            self.slit_smearer.Enable(True)
3218            self.pinhole_smearer.Enable(True)
3219
3220
3221class BGTextCtrl(wx.TextCtrl):
3222    """
3223    Text control used to display outputs.
3224    No editing allowed. The background is
3225    grayed out. User can't select text.
3226    """
3227    def __init__(self, *args, **kwds):
3228        wx.TextCtrl.__init__(self, *args, **kwds)
3229        self.SetEditable(False)
3230        self.SetBackgroundColour(self.GetParent().parent.GetBackgroundColour())
3231
3232        # Bind to mouse event to avoid text highlighting
3233        # The event will be skipped once the call-back
3234        # is called.
3235        self.Bind(wx.EVT_MOUSE_EVENTS, self._click)
3236
3237    def _click(self, event):
3238        """
3239        Prevent further handling of the mouse event
3240        by not calling Skip().
3241        """
3242        pass
Note: See TracBrowser for help on using the repository browser.