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

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 ff3f5821 was 4c3be25, checked in by Mathieu Doucet <doucetm@…>, 8 years ago

Remove "select all" box. Fixes #697 Fixes #698

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