source: sasview/src/sas/perspectives/fitting/fitpage.py @ 7801df8

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 7801df8 was 7801df8, checked in by butler, 9 years ago

Clean up minor errors in previous commits and add General Fitting Help
and button sizer and even handler for smearing help (but still need to
add button to panel so can be used.

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