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

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 b69edaa was 2b58fa5, checked in by butler, 10 years ago

Fix problem with magneti angle help showing up in 1D mode.

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