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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since ca3f89b was dbce805, checked in by Piotr Rozyczko <rozyczko@…>, 8 years ago

fix 'invalid Q range' when parameter value is min or max. Fixes #636

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