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

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

Remove print preview menu items. Fix 2D plotting from fit perspective.

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