source: sasview/src/sas/perspectives/fitting/fitpage.py @ 85ccd3a

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 85ccd3a was 85ccd3a, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Fix constrained fit option

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