source: sasview/src/sas/perspectives/fitting/fitpage.py @ 686fd10

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 686fd10 was 60d08fd, checked in by gonzalezm, 10 years ago

Corrected bug affecting color of parameter errors

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