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

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 225aca8 was 225aca8, checked in by mathieu, 9 years ago

Merge pull request #3 from SasView?/standalone_cleanup

Standalone cleanup

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