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

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

Align fit page controls

  • Property mode set to 100644
File size: 130.0 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        from sas.perspectives.fitting.utils import get_weight
2221        flag_weight = self.get_weight_flag()
2222        if is_2D == None:
2223            is_2D = self._is_2D()
2224        weight = get_weight(data=self.data,
2225                            is2d=is_2D,
2226                            flag=flag_weight)
2227        self._manager.set_fit_weight(uid=self.uid,
2228                                     flag=flag_weight,
2229                                     is2d=is_2D,
2230                                     fid=None)
2231
2232    def onPinholeSmear(self, event):
2233        """
2234        Create a custom pinhole smear object that will change the way residuals
2235        are compute when fitting
2236
2237        :Note: accuracy is given by strings'High','Med', 'Low' FOR 2d,
2238                     None for 1D
2239
2240        """
2241        # Need update param values
2242        self._update_paramv_on_fit()
2243
2244        # msg default
2245        msg = None
2246        if event != None:
2247            tcrtl = event.GetEventObject()
2248            # event case of radio button
2249            if tcrtl.GetValue() == True:
2250                self.dx_min = 0.0
2251                self.dx_max = 0.0
2252                is_new_pinhole = True
2253            else:
2254                is_new_pinhole = self._is_changed_pinhole()
2255        else:
2256            is_new_pinhole = True
2257        # if any value is changed
2258        if is_new_pinhole:
2259            msg = self._set_pinhole_smear()
2260        # hide all silt sizer
2261        self._hide_all_smear_info()
2262
2263        # show relevant slit sizers
2264        self._show_smear_sizer()
2265
2266        self.sizer_set_smearer.Layout()
2267        self.Layout()
2268
2269        if event != None:
2270            event.Skip()
2271        #self._undo.Enable(True)
2272        self.save_current_state()
2273        event = PageInfoEvent(page=self)
2274        wx.PostEvent(self.parent, event)
2275
2276    def _is_changed_pinhole(self):
2277        """
2278        check if any of pinhole smear is changed
2279
2280        :return: True or False
2281
2282        """
2283        # get the values
2284        pin_min = self.smear_pinhole_min.GetValue()
2285        pin_max = self.smear_pinhole_max.GetValue()
2286
2287        # Check changes in slit width
2288        try:
2289            dx_min = float(pin_min)
2290        except:
2291            return True
2292        if self.dx_min != dx_min:
2293            return True
2294
2295        # Check changes in slit heigth
2296        try:
2297            dx_max = float(pin_max)
2298        except:
2299            return True
2300        if self.dx_max != dx_max:
2301            return True
2302        return False
2303
2304    def _set_pinhole_smear(self):
2305        """
2306        Set custom pinhole smear
2307
2308        :return: msg
2309
2310        """
2311        # copy data
2312        data = copy.deepcopy(self.data)
2313        if self._is_2D():
2314            self.smear_type = 'Pinhole2d'
2315            len_data = len(data.data)
2316            data.dqx_data = numpy.zeros(len_data)
2317            data.dqy_data = numpy.zeros(len_data)
2318        else:
2319            self.smear_type = 'Pinhole'
2320            len_data = len(data.x)
2321            data.dx = numpy.zeros(len_data)
2322            data.dxl = None
2323            data.dxw = None
2324        msg = None
2325
2326        get_pin_min = self.smear_pinhole_min
2327        get_pin_max = self.smear_pinhole_max
2328
2329        if not check_float(get_pin_min):
2330            get_pin_min.SetBackgroundColour("pink")
2331            msg = "Model Error:wrong value entered!!!"
2332        elif not check_float(get_pin_max):
2333            get_pin_max.SetBackgroundColour("pink")
2334            msg = "Model Error:wrong value entered!!!"
2335        else:
2336            if len_data < 2:
2337                len_data = 2
2338            self.dx_min = float(get_pin_min.GetValue())
2339            self.dx_max = float(get_pin_max.GetValue())
2340            if self.dx_min < 0:
2341                get_pin_min.SetBackgroundColour("pink")
2342                msg = "Model Error:This value can not be negative!!!"
2343            elif self.dx_max < 0:
2344                get_pin_max.SetBackgroundColour("pink")
2345                msg = "Model Error:This value can not be negative!!!"
2346            elif self.dx_min != None and self.dx_max != None:
2347                if self._is_2D():
2348                    data.dqx_data[data.dqx_data == 0] = self.dx_min
2349                    data.dqy_data[data.dqy_data == 0] = self.dx_max
2350                elif self.dx_min == self.dx_max:
2351                    data.dx[data.dx == 0] = self.dx_min
2352                else:
2353                    step = (self.dx_max - self.dx_min) / (len_data - 1)
2354                    data.dx = numpy.arange(self.dx_min,
2355                                           self.dx_max + step / 1.1,
2356                                           step)
2357            elif self.dx_min != None:
2358                if self._is_2D():
2359                    data.dqx_data[data.dqx_data == 0] = self.dx_min
2360                else:
2361                    data.dx[data.dx == 0] = self.dx_min
2362            elif self.dx_max != None:
2363                if self._is_2D():
2364                    data.dqy_data[data.dqy_data == 0] = self.dx_max
2365                else:
2366                    data.dx[data.dx == 0] = self.dx_max
2367            self.current_smearer = smear_selection(data, self.model)
2368            # 2D need to set accuracy
2369            if self._is_2D():
2370                self.current_smearer.set_accuracy(accuracy=\
2371                                                  self.smear2d_accuracy)
2372
2373        if msg != None:
2374            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2375        else:
2376            get_pin_min.SetBackgroundColour("white")
2377            get_pin_max.SetBackgroundColour("white")
2378        ## set smearing value whether or not the data contain the smearing info
2379
2380        enable_smearer = not self.disable_smearer.GetValue()
2381        self._manager.set_smearer(smearer=self.current_smearer,
2382                                  fid=self.data.id,
2383                                  qmin=float(self.qmin_x),
2384                                  qmax=float(self.qmax_x),
2385                                  enable_smearer=enable_smearer,
2386                                  uid=self.uid)
2387        return msg
2388
2389    def update_pinhole_smear(self):
2390        """
2391        called by kill_focus on pinhole TextCntrl
2392        to update the changes
2393
2394        :return: False when wrong value was entered
2395
2396        """
2397        # msg default
2398        msg = None
2399        # check if any value is changed
2400        if self._is_changed_pinhole():
2401            msg = self._set_pinhole_smear()
2402        wx.CallAfter(self.save_current_state)
2403
2404        if msg != None:
2405            return False
2406        else:
2407            return True
2408
2409    def onSlitSmear(self, event):
2410        """
2411        Create a custom slit smear object that will change the way residuals
2412        are compute when fitting
2413        """
2414        # Need update param values
2415        self._update_paramv_on_fit()
2416
2417        # msg default
2418        msg = None
2419        # for event given
2420        if event != None:
2421            tcrtl = event.GetEventObject()
2422            # event case of radio button
2423            if tcrtl.GetValue():
2424                self.dxl = 0.0
2425                self.dxw = 0.0
2426                is_new_slit = True
2427            else:
2428                is_new_slit = self._is_changed_slit()
2429        else:
2430            is_new_slit = True
2431
2432        # if any value is changed
2433        if is_new_slit:
2434            msg = self._set_slit_smear()
2435
2436        # hide all silt sizer
2437        self._hide_all_smear_info()
2438        # show relevant slit sizers
2439        self._show_smear_sizer()
2440        self.sizer_set_smearer.Layout()
2441        self.Layout()
2442
2443        if event != None:
2444            event.Skip()
2445        self.save_current_state()
2446        event = PageInfoEvent(page=self)
2447        wx.PostEvent(self.parent, event)
2448        if msg != None:
2449            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2450
2451    def _is_changed_slit(self):
2452        """
2453        check if any of slit lengths is changed
2454
2455        :return: True or False
2456
2457        """
2458        # get the values
2459        width = self.smear_slit_width.GetValue()
2460        height = self.smear_slit_height.GetValue()
2461
2462        # check and change the box bg color if it was pink
2463        #    but it should be white now
2464        # because this is the case that _set_slit_smear() will not handle
2465        if height.lstrip().rstrip() == "":
2466            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2467        if width.lstrip().rstrip() == "":
2468            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2469
2470        # Check changes in slit width
2471        if width == "":
2472            dxw = 0.0
2473        else:
2474            try:
2475                dxw = float(width)
2476            except:
2477                return True
2478        if self.dxw != dxw:
2479            return True
2480
2481        # Check changes in slit heigth
2482        if height == "":
2483            dxl = 0.0
2484        else:
2485            try:
2486                dxl = float(height)
2487            except:
2488                return True
2489        if self.dxl != dxl:
2490            return True
2491
2492        return False
2493
2494    def _set_slit_smear(self):
2495        """
2496        Set custom slit smear
2497
2498        :return: message to inform the user about the validity
2499            of the values entered for slit smear
2500        """
2501        if self.data.__class__.__name__ == "Data2D" or self.enable2D:
2502            return
2503        # make sure once more if it is smearer
2504        data = copy.deepcopy(self.data)
2505        data_len = len(data.x)
2506        data.dx = None
2507        data.dxl = None
2508        data.dxw = None
2509        msg = None
2510
2511        try:
2512            self.dxl = float(self.smear_slit_height.GetValue())
2513            data.dxl = self.dxl * numpy.ones(data_len)
2514            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2515        except:
2516            self.dxl = None
2517            data.dxl = numpy.zeros(data_len)
2518            if self.smear_slit_height.GetValue().lstrip().rstrip() != "":
2519                self.smear_slit_height.SetBackgroundColour("pink")
2520                msg = "Wrong value entered... "
2521            else:
2522                self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2523        try:
2524            self.dxw = float(self.smear_slit_width.GetValue())
2525            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2526            data.dxw = self.dxw * numpy.ones(data_len)
2527        except:
2528            self.dxw = None
2529            data.dxw = numpy.zeros(data_len)
2530            if self.smear_slit_width.GetValue().lstrip().rstrip() != "":
2531                self.smear_slit_width.SetBackgroundColour("pink")
2532                msg = "Wrong Fit value entered... "
2533            else:
2534                self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2535
2536        self.current_smearer = smear_selection(data, self.model)
2537        ## set smearing value whether or not the data contain the smearing info
2538        enable_smearer = not self.disable_smearer.GetValue()
2539        self._manager.set_smearer(smearer=self.current_smearer,
2540                                  fid=self.data.id,
2541                                  qmin=float(self.qmin_x),
2542                                  qmax=float(self.qmax_x),
2543                                  enable_smearer=enable_smearer,
2544                                  uid=self.uid)
2545        return msg
2546
2547    def update_slit_smear(self):
2548        """
2549        called by kill_focus on pinhole TextCntrl
2550        to update the changes
2551
2552        :return: False when wrong value was entered
2553
2554        """
2555        # msg default
2556        msg = None
2557        # check if any value is changed
2558        if self._is_changed_slit():
2559            msg = self._set_slit_smear()
2560        #self._undo.Enable(True)
2561        self.save_current_state()
2562
2563        if msg != None:
2564            return False
2565        else:
2566            return True
2567
2568    def onSmear(self, event):
2569        """
2570        Create a smear object that will change the way residuals
2571        are compute when fitting
2572        """
2573        if event != None:
2574            event.Skip()
2575        if self.data is None:
2576            return
2577
2578        # Need update param values
2579        self._update_paramv_on_fit()
2580        if self.model is not None:
2581            if self.data.is_data:
2582                self._manager.page_finder[self.uid].add_data(data=self.data)
2583        temp_smearer = self.on_smear_helper()
2584
2585        self.sizer_set_smearer.Layout()
2586        self.Layout()
2587        self._set_weight()
2588
2589        ## set smearing value whether or not the data contain the smearing info
2590        enable_smearer = not self.disable_smearer.GetValue()
2591        wx.CallAfter(self._manager.set_smearer, uid=self.uid,
2592                     smearer=temp_smearer,
2593                     fid=self.data.id,
2594                     qmin=float(self.qmin_x),
2595                     qmax=float(self.qmax_x),
2596                     enable_smearer=enable_smearer,
2597                     draw=True)
2598
2599        self.state.enable_smearer = self.enable_smearer.GetValue()
2600        self.state.disable_smearer = self.disable_smearer.GetValue()
2601        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
2602        self.state.slit_smearer = self.slit_smearer.GetValue()
2603
2604    def on_smear_helper(self, update=False):
2605        """
2606        Help for onSmear
2607
2608        :param update: force or not to update
2609        """
2610        self._get_smear_info()
2611        #renew smear sizer
2612        if self.smear_type is not None:
2613            self.smear_description_smear_type.SetValue(str(self.smear_type))
2614            self.smear_data_left.SetValue(str(self.dq_l))
2615            self.smear_data_right.SetValue(str(self.dq_r))
2616
2617        self._hide_all_smear_info()
2618        data = copy.deepcopy(self.data)
2619
2620        # make sure once more if it is smearer
2621        temp_smearer = smear_selection(data, self.model)
2622        if self.current_smearer != temp_smearer or update:
2623            self.current_smearer = temp_smearer
2624        if self.enable_smearer.GetValue():
2625            if self.current_smearer is None:
2626                wx.PostEvent(self._manager.parent,
2627                    StatusEvent(status="Data contains no smearing information"))
2628            else:
2629                wx.PostEvent(self._manager.parent,
2630                    StatusEvent(status="Data contains smearing information"))
2631
2632            self.smear_data_left.Show(True)
2633            self.smear_data_right.Show(True)
2634            temp_smearer = self.current_smearer
2635        elif self.disable_smearer.GetValue():
2636            self.smear_description_none.Show(True)
2637        elif self.pinhole_smearer.GetValue():
2638            self.onPinholeSmear(None)
2639        elif self.slit_smearer.GetValue():
2640            self.onSlitSmear(None)
2641        self._show_smear_sizer()
2642
2643        return temp_smearer
2644
2645    def on_complete_chisqr(self, event):
2646        """
2647        Display result chisqr on the panel
2648        :event: activated by fitting/ complete after draw
2649        """
2650        try:
2651            if event == None:
2652                output = "-"
2653            elif not numpy.isfinite(event.output):
2654                output = "-"
2655            else:
2656                output = event.output
2657            self.tcChi.SetValue(str(format_number(output, True)))
2658            self.state.tcChi = self.tcChi.GetValue()
2659        except:
2660            pass
2661
2662    def get_all_checked_params(self):
2663        """
2664        Found all parameters current check and add them to list of parameters
2665        to fit
2666        """
2667        self.param_toFit = []
2668        for item in self.parameters:
2669            if item[0].GetValue() and item not in self.param_toFit:
2670                if item[0].IsShown():
2671                    self.param_toFit.append(item)
2672        for item in self.fittable_param:
2673            if item[0].GetValue() and item not in self.param_toFit:
2674                if item[0].IsShown():
2675                    self.param_toFit.append(item)
2676        self.save_current_state_fit()
2677
2678        event = PageInfoEvent(page=self)
2679        wx.PostEvent(self.parent, event)
2680        param2fit = []
2681        for item in self.param_toFit:
2682            if item[0] and item[0].IsShown():
2683                param2fit.append(item[1])
2684        self._manager.set_param2fit(self.uid, param2fit)
2685
2686    def select_all_param(self, event):
2687        """
2688        set to true or false all checkBox given the main checkbox value cb1
2689        """
2690        self.param_toFit = []
2691        if  self.parameters != []:
2692            if  self.cb1.GetValue():
2693                for item in self.parameters:
2694                    if item[0].IsShown():
2695                        ## for data2D select all to fit
2696                        if self.data.__class__.__name__ == "Data2D" or \
2697                                self.enable2D:
2698                            item[0].SetValue(True)
2699                            self.param_toFit.append(item)
2700                        else:
2701                            ## for 1D all parameters except orientation
2702                            if not item in self.orientation_params:
2703                                item[0].SetValue(True)
2704                                self.param_toFit.append(item)
2705                    else:
2706                        item[0].SetValue(False)
2707                #if len(self.fittable_param)>0:
2708                for item in self.fittable_param:
2709                    if item[0].IsShown():
2710                        if self.data.__class__.__name__ == "Data2D" or \
2711                                self.enable2D:
2712                            item[0].SetValue(True)
2713                            self.param_toFit.append(item)
2714                            try:
2715                                if len(self.values[item[1]]) > 0:
2716                                    item[0].SetValue(False)
2717                            except:
2718                                pass
2719
2720                        else:
2721                            ## for 1D all parameters except orientation
2722                            if not item in self.orientation_params_disp:
2723                                item[0].SetValue(True)
2724                                self.param_toFit.append(item)
2725                                try:
2726                                    if len(self.values[item[1]]) > 0:
2727                                        item[0].SetValue(False)
2728                                except:
2729                                    pass
2730                    else:
2731                        item[0].SetValue(False)
2732
2733            else:
2734                for item in self.parameters:
2735                    item[0].SetValue(False)
2736                for item in self.fittable_param:
2737                    item[0].SetValue(False)
2738                self.param_toFit = []
2739
2740        self.save_current_state_fit()
2741
2742        if event != None:
2743            #self._undo.Enable(True)
2744            ## post state to fit panel
2745            event = PageInfoEvent(page=self)
2746            wx.PostEvent(self.parent, event)
2747        param2fit = []
2748        for item in self.param_toFit:
2749            if item[0] and item[0].IsShown():
2750                param2fit.append(item[1])
2751        self.parent._manager.set_param2fit(self.uid, param2fit)
2752
2753    def select_param(self, event):
2754        """
2755        Select TextCtrl  checked for fitting purpose and stores them
2756        in  self.param_toFit=[] list
2757        """
2758        self.param_toFit = []
2759        for item in self.parameters:
2760            #Skip t ifhe angle parameters if 1D data
2761            if self.data.__class__.__name__ != "Data2D" and\
2762                        not self.enable2D:
2763                if item in self.orientation_params:
2764                    continue
2765            #Select parameters to fit for list of primary parameters
2766            if item[0].GetValue() and item[0].IsShown():
2767                if not (item in self.param_toFit):
2768                    self.param_toFit.append(item)
2769            else:
2770                #remove parameters from the fitting list
2771                if item in self.param_toFit:
2772                    self.param_toFit.remove(item)
2773
2774        #Select parameters to fit for list of fittable parameters
2775        #        with dispersion
2776        for item in self.fittable_param:
2777            #Skip t ifhe angle parameters if 1D data
2778            if self.data.__class__.__name__ != "Data2D" and\
2779                        not self.enable2D:
2780                if item in self.orientation_params:
2781                    continue
2782            if item[0].GetValue() and item[0].IsShown():
2783                if not (item in self.param_toFit):
2784                    self.param_toFit.append(item)
2785            else:
2786                #remove parameters from the fitting list
2787                if item in self.param_toFit:
2788                    self.param_toFit.remove(item)
2789
2790        #Calculate num. of angle parameters
2791        if self.data.__class__.__name__ == "Data2D" or \
2792                       self.enable2D:
2793            len_orient_para = 0
2794        else:
2795            len_orient_para = len(self.orientation_params)  # assume even len
2796        #Total num. of angle parameters
2797        if len(self.fittable_param) > 0:
2798            len_orient_para *= 2
2799        #Set the value of checkbox that selected every checkbox or not
2800        if len(self.parameters) + len(self.fittable_param) - len_orient_para \
2801            == len(self.param_toFit):
2802            self.cb1.SetValue(True)
2803        else:
2804            self.cb1.SetValue(False)
2805
2806        self.save_current_state_fit()
2807        if event != None:
2808            ## post state to fit panel
2809            event = PageInfoEvent(page=self)
2810            wx.PostEvent(self.parent, event)
2811
2812        param2fit = []
2813        for item in self.param_toFit:
2814            if item[0] and item[0].IsShown():
2815                param2fit.append(item[1])
2816        self._manager.set_param2fit(self.uid, param2fit)
2817
2818    def set_model_param_sizer(self, model):
2819        """
2820        Build the panel from the model content
2821
2822        :param model: the model selected in combo box for fitting purpose
2823
2824        """
2825        self.sizer3.Clear(True)
2826        self.parameters = []
2827        self.str_parameters = []
2828        self.param_toFit = []
2829        self.fittable_param = []
2830        self.fixed_param = []
2831        self.orientation_params = []
2832        self.orientation_params_disp = []
2833
2834        if model == None:
2835            self.sizer3.Layout()
2836            self.SetupScrolling()
2837            return
2838
2839        box_description = wx.StaticBox(self, -1, str("Model Parameters"))
2840        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
2841        sizer = wx.GridBagSizer(5, 5)
2842        ## save the current model
2843        self.model = model
2844
2845        keys = self.model.getParamList()
2846
2847        #list of dispersion parameters
2848        self.disp_list = self.model.getDispParamList()
2849
2850        def custom_compare(a, b):
2851            """
2852            Custom compare to order, first by alphabets then second by number.
2853            """
2854            # number at the last digit
2855            a_last = a[len(a) - 1]
2856            b_last = b[len(b) - 1]
2857            # default
2858            num_a = None
2859            num_b = None
2860            # split the names
2861            a2 = a.lower().split('_')
2862            b2 = b.lower().split('_')
2863            # check length of a2, b2
2864            len_a2 = len(a2)
2865            len_b2 = len(b2)
2866            # check if it contains a int number(<10)
2867            try:
2868                num_a = int(a_last)
2869            except:
2870                pass
2871            try:
2872                num_b = int(b_last)
2873            except:
2874                pass
2875            # Put 'scale' near the top; happens
2876            # when numbered param name exists
2877            if a == 'scale':
2878                return -1
2879            # both have a number
2880            if num_a != None and num_b != None:
2881                if num_a > num_b:
2882                    return -1
2883                # same number
2884                elif num_a == num_b:
2885                    # different last names
2886                    if a2[len_a2 - 1] != b2[len_b2 - 1] and num_a != 0:
2887                        return -cmp(a2[len_a2 - 1], b2[len_b2 - 1])
2888                    else:
2889                        return cmp(a, b)
2890                else:
2891                    return 1
2892            # one of them has a number
2893            elif num_a != None:
2894                return 1
2895            elif num_b != None:
2896                return -1
2897            # no numbers
2898            else:
2899                return cmp(a.lower(), b.lower())
2900
2901        keys.sort(custom_compare)
2902
2903        iy = 0
2904        ix = 0
2905        select_text = "Select All"
2906        self.cb1 = wx.CheckBox(self, -1, str(select_text), (10, 10))
2907        wx.EVT_CHECKBOX(self, self.cb1.GetId(), self.select_all_param)
2908        self.cb1.SetToolTipString("To check/uncheck all the boxes below.")
2909        self.cb1.SetValue(True)
2910
2911        sizer.Add(self.cb1, (iy, ix), (1, 1), \
2912                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
2913        ix += 1
2914        self.text2_2 = wx.StaticText(self, -1, 'Value')
2915        sizer.Add(self.text2_2, (iy, ix), (1, 1), \
2916                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2917        ix += 2
2918        self.text2_3 = wx.StaticText(self, -1, 'Error')
2919        sizer.Add(self.text2_3, (iy, ix), (1, 1), \
2920                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2921        if not self.is_mac:
2922            self.text2_3.Hide()
2923        ix += 1
2924        self.text2_min = wx.StaticText(self, -1, 'Min')
2925        sizer.Add(self.text2_min, (iy, ix), (1, 1), \
2926                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2927        #self.text2_min.Hide()
2928        ix += 1
2929        self.text2_max = wx.StaticText(self, -1, 'Max')
2930        sizer.Add(self.text2_max, (iy, ix), (1, 1), \
2931                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2932        #self.text2_max.Hide()
2933        ix += 1
2934        self.text2_4 = wx.StaticText(self, -1, '[Units]')
2935        sizer.Add(self.text2_4, (iy, ix), (1, 1), \
2936                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2937        self.text2_4.Hide()
2938
2939        CHECK_STATE = self.cb1.GetValue()
2940        for item in keys:
2941
2942            if not item in self.disp_list and not item in \
2943                    self.model.orientation_params:
2944
2945                ##prepare a spot to store errors
2946                if not item in self.model.details:
2947                    self.model.details[item] = ["", None, None]
2948
2949                iy += 1
2950                ix = 0
2951                if (self.model.__class__ in \
2952                    self.model_list_box["Multi-Functions"] or \
2953                    self.temp_multi_functional)\
2954                    and (item in self.model.non_fittable):
2955                    non_fittable_name = wx.StaticText(self, -1, item)
2956                    sizer.Add(non_fittable_name, (iy, ix), (1, 1), \
2957                            wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 21)
2958                    ## add parameter value
2959                    ix += 1
2960                    value = self.model.getParam(item)
2961                    if len(self.model.fun_list) > 0:
2962                        #num = item.split('_')[1][5:7]
2963                        fun_box = wx.ComboBox(self, -1, size=(100, -1),
2964                                    style=wx.CB_READONLY, name='%s' % item)
2965                        self._set_fun_box_list(fun_box)
2966                        fun_box.SetSelection(0)
2967                        #self.fun_box.SetToolTipString("A function
2968                        #    describing the interface")
2969                        wx.EVT_COMBOBOX(fun_box, -1, self._on_fun_box)
2970                    else:
2971                        fun_box = ModelTextCtrl(self, -1,
2972                                                size=(_BOX_WIDTH, 20),
2973                                style=wx.TE_PROCESS_ENTER, name='%s' % item)
2974                        fun_box.SetToolTipString(\
2975                                "Hit 'Enter' after typing to update the plot.")
2976                        fun_box.SetValue(format_number(value, True))
2977                    sizer.Add(fun_box, (iy, ix), (1, 1), wx.EXPAND)
2978                    self.str_parameters.append([None, item, fun_box,
2979                                                None, None, None,
2980                                                None, None])
2981                else:
2982                    ## add parameters name with checkbox for selecting to fit
2983                    cb = wx.CheckBox(self, -1, item)
2984                    cb.SetValue(CHECK_STATE)
2985                    cb.SetToolTipString(" Check mark to fit.")
2986                    #cb.SetValue(True)
2987                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
2988
2989                    sizer.Add(cb, (iy, ix), (1, 1),
2990                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
2991
2992                    ## add parameter value
2993                    ix += 1
2994                    value = self.model.getParam(item)
2995                    ctl1 = ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
2996                                         style=wx.TE_PROCESS_ENTER)
2997                    ctl1.SetToolTipString(\
2998                                "Hit 'Enter' after typing to update the plot.")
2999                    ctl1.SetValue(format_number(value, True))
3000                    sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
3001                    ## text to show error sign
3002                    ix += 1
3003                    text2 = wx.StaticText(self, -1, '+/-')
3004                    sizer.Add(text2, (iy, ix), (1, 1), \
3005                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3006                    if not self.is_mac:
3007                        text2.Hide()
3008                    ix += 1
3009                    ctl2 = wx.TextCtrl(self, -1,
3010                                       size=(_BOX_WIDTH / 1.2, 20), style=0)
3011                    sizer.Add(ctl2, (iy, ix), (1, 1),
3012                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3013                    if not self.is_mac:
3014                        ctl2.Hide()
3015
3016                    ix += 1
3017                    ctl3 = ModelTextCtrl(self, -1,
3018                                         size=(_BOX_WIDTH / 1.9, 20),
3019                                         style=wx.TE_PROCESS_ENTER,
3020                                text_enter_callback=self._onparamRangeEnter)
3021                    min_bound = self.model.details[item][1]
3022                    if min_bound is not None:
3023                        ctl3.SetValue(format_number(min_bound, True))
3024
3025                    sizer.Add(ctl3, (iy, ix), (1, 1),
3026                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3027
3028                    ix += 1
3029                    ctl4 = ModelTextCtrl(self, -1,
3030                                         size=(_BOX_WIDTH / 1.9, 20),
3031                                         style=wx.TE_PROCESS_ENTER,
3032                                text_enter_callback=self._onparamRangeEnter)
3033                    max_bound = self.model.details[item][2]
3034                    if max_bound is not None:
3035                        ctl4.SetValue(format_number(max_bound, True))
3036                    sizer.Add(ctl4, (iy, ix), (1, 1),
3037                              wx.EXPAND | wx.FIXED_MINSIZE, 0)
3038
3039                    ix += 1
3040                    # Units
3041                    if item in self.model.details:
3042                        units = wx.StaticText(self, -1,
3043                            self.model.details[item][0], style=wx.ALIGN_LEFT)
3044                    else:
3045                        units = wx.StaticText(self, -1, "",
3046                                              style=wx.ALIGN_LEFT)
3047                    sizer.Add(units, (iy, ix), (1, 1),
3048                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3049
3050                    self.parameters.append([cb, item, ctl1,
3051                                            text2, ctl2, ctl3, ctl4, units])
3052
3053        iy += 1
3054        sizer.Add((10, 10), (iy, ix), (1, 1),
3055                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
3056
3057        # type can be either Guassian or Array
3058        if len(self.model.dispersion.values()) > 0:
3059            type = self.model.dispersion.values()[0]["type"]
3060        else:
3061            type = "Gaussian"
3062
3063        iy += 1
3064        ix = 0
3065        #Add tile for orientational angle
3066        for item in keys:
3067            if item in self.model.orientation_params:
3068                orient_angle = wx.StaticText(self, -1, '[For 2D only]:')
3069                mag_on_button = wx.Button(self, -1, "Magnetic ON")
3070                mag_on_button.SetToolTipString("Turn Pol Beam/Mag scatt on/off")
3071                mag_on_button.Bind(wx.EVT_BUTTON, self._on_mag_on)
3072                mag_angle_help_button = wx.Button(self, -1, "Magnetic angles?")
3073                mag_angle_help_button.SetToolTipString("see angle definitions")
3074                mag_help_button = wx.Button(self, -1, "Mag HELP")
3075                mag_help_button.SetToolTipString("Help on pol beam/mag fitting")
3076                mag_help_button.Bind(wx.EVT_BUTTON, self._on_mag_help)
3077                mag_angle_help_button.Bind(wx.EVT_BUTTON, \
3078                                            self._on_mag_angle_help)
3079                sizer.Add(orient_angle, (iy, ix), (1, 1),
3080                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
3081                iy += 1
3082                sizer.Add(mag_on_button, (iy, ix), (1, 1),
3083                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
3084                ix += 1
3085                sizer.Add(mag_angle_help_button, (iy, ix), (1, 1),
3086                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
3087                sizer.Add(mag_help_button, (iy, ix + 1), (1, 1),
3088                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
3089
3090                #handle the magnetic buttons
3091                #clean this up so that assume mag is off then turn
3092                #all buttons on IF mag has mag and has 2D
3093                if not self._has_magnetic:
3094                    mag_on_button.Show(False)
3095                elif not self.data.__class__.__name__ == "Data2D":
3096                    mag_on_button.Show(False)
3097                else:
3098                    mag_on_button.Show(True)
3099                mag_help_button.Show(False)
3100                mag_angle_help_button.Show(False)
3101                if mag_on_button.IsShown():
3102                    if self.magnetic_on:
3103                        mag_on_button.SetLabel("Magnetic OFF")
3104                        mag_help_button.Show(True)
3105                        mag_angle_help_button.Show(True)
3106                    else:
3107                        mag_on_button.SetLabel("Magnetic ON")
3108                        mag_help_button.Show(False)
3109                        mag_angle_help_button.Show(False)
3110
3111                if not self.data.__class__.__name__ == "Data2D" and \
3112                        not self.enable2D:
3113                    orient_angle.Hide()
3114                else:
3115                    orient_angle.Show(True)
3116                break
3117
3118        #For Gaussian only
3119        if type.lower() != "array":
3120            for item in self.model.orientation_params:
3121                if not self.magnetic_on:
3122                    if item in self.model.magnetic_params:
3123                        continue
3124                if not item in self.disp_list:
3125                    ##prepare a spot to store min max
3126                    if not item in self.model.details:
3127                        self.model.details[item] = ["", None, None]
3128
3129                    iy += 1
3130                    ix = 0
3131                    ## add parameters name with checkbox for selecting to fit
3132                    cb = wx.CheckBox(self, -1, item)
3133                    cb.SetValue(CHECK_STATE)
3134                    cb.SetToolTipString("Check mark to fit")
3135                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
3136                    if self.data.__class__.__name__ == "Data2D" or \
3137                            self.enable2D:
3138                        cb.Show(True)
3139                    else:
3140                        cb.Hide()
3141                    sizer.Add(cb, (iy, ix), (1, 1),
3142                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
3143
3144                    ## add parameter value
3145                    ix += 1
3146                    value = self.model.getParam(item)
3147                    ctl1 = ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
3148                                         style=wx.TE_PROCESS_ENTER)
3149                    ctl1.SetToolTipString(\
3150                                "Hit 'Enter' after typing to update the plot.")
3151                    ctl1.SetValue(format_number(value, True))
3152                    if self.data.__class__.__name__ == "Data2D" or \
3153                            self.enable2D:
3154                        ctl1.Show(True)
3155                    else:
3156                        ctl1.Hide()
3157                    sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
3158                    ## text to show error sign
3159                    ix += 1
3160                    text2 = wx.StaticText(self, -1, '+/-')
3161                    sizer.Add(text2, (iy, ix), (1, 1), \
3162                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3163
3164                    text2.Hide()
3165                    ix += 1
3166                    ctl2 = wx.TextCtrl(self, -1,
3167                                       size=(_BOX_WIDTH / 1.2, 20), style=0)
3168                    sizer.Add(ctl2, (iy, ix), (1, 1),
3169                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3170
3171                    ctl2.Hide()
3172
3173                    ix += 1
3174                    ctl3 = ModelTextCtrl(self, -1,
3175                                         size=(_BOX_WIDTH / 1.8, 20),
3176                                         style=wx.TE_PROCESS_ENTER,
3177                                text_enter_callback=self._onparamRangeEnter)
3178
3179                    sizer.Add(ctl3, (iy, ix), (1, 1),
3180                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3181                    ctl3.Hide()
3182
3183                    ix += 1
3184                    ctl4 = ModelTextCtrl(self, -1,
3185                                         size=(_BOX_WIDTH / 1.8, 20),
3186                                         style=wx.TE_PROCESS_ENTER,
3187                            text_enter_callback=self._onparamRangeEnter)
3188                    sizer.Add(ctl4, (iy, ix), (1, 1),
3189                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3190
3191                    ctl4.Hide()
3192
3193                    if self.data.__class__.__name__ == "Data2D" or \
3194                            self.enable2D:
3195                        if self.is_mac:
3196                            text2.Show(True)
3197                            ctl2.Show(True)
3198                        ctl3.Show(True)
3199                        ctl4.Show(True)
3200
3201                    ix += 1
3202                    # Units
3203                    if item in self.model.details:
3204                        units = wx.StaticText(self, -1,
3205                                              self.model.details[item][0],
3206                                              style=wx.ALIGN_LEFT)
3207                    else:
3208                        units = wx.StaticText(self, -1, "",
3209                                              style=wx.ALIGN_LEFT)
3210                    if self.data.__class__.__name__ == "Data2D" or \
3211                            self.enable2D:
3212                        units.Show(True)
3213                    else:
3214                        units.Hide()
3215
3216                    sizer.Add(units, (iy, ix), (1, 1),
3217                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3218
3219                    self.parameters.append([cb, item, ctl1,
3220                                            text2, ctl2, ctl3, ctl4, units])
3221                    self.orientation_params.append([cb, item, ctl1,
3222                                            text2, ctl2, ctl3, ctl4, units])
3223
3224        iy += 1
3225        box_description.SetForegroundColour(wx.BLUE)
3226        #Display units text on panel
3227        for item in keys:
3228            if item in self.model.details:
3229                self.text2_4.Show()
3230        #Fill the list of fittable parameters
3231        self.get_all_checked_params()
3232        self.save_current_state_fit()
3233        boxsizer1.Add(sizer)
3234        self.sizer3.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3235        self.sizer3.Layout()
3236        self.Layout()
3237
3238    def on_right_down(self, event):
3239        """
3240        Get key stroke event
3241        """
3242        if self.data == None:
3243            return
3244        # Figuring out key combo: Cmd for copy, Alt for paste
3245        if event.AltDown() and event.ShiftDown():
3246            flag = True
3247        elif event.AltDown() or event.ShiftDown():
3248            flag = False
3249        else:
3250            return
3251        # make event free
3252        event.Skip()
3253        # messages depending on the flag
3254        if not flag:
3255            infor = 'warning'
3256            # inform msg to wx
3257            wx.PostEvent(self._manager.parent,
3258                        StatusEvent(status=msg, info=infor))
3259
3260    def _onModel2D(self, event):
3261        """
3262        toggle view of model from 1D to 2D  or 2D from 1D
3263        """
3264        if self.model_view.GetLabelText() == "Show 2D":
3265            self.model_view.SetLabel("Show 1D")
3266            self.enable2D = True
3267
3268        else:
3269            self.model_view.SetLabel("Show 2D")
3270            self.enable2D = False
3271        self.Show(False)
3272        self.create_default_data()
3273        self._manager.store_data(self.uid, data_list=[self.data])
3274
3275        self.set_model_param_sizer(self.model)
3276        self._set_sizer_dispersion()
3277        self._set_weight(is_2D=self.enable2D)
3278        self._set_smear_buttons()
3279        self.Show(True)
3280        self.SetupScrolling()
3281        self._draw_model()
3282
3283        self.state.enable2D = copy.deepcopy(self.enable2D)
3284
3285    def _set_smear_buttons(self):
3286        """
3287        Set semarer radio buttons
3288        """
3289        # more disables for 2D
3290        if self.data.__class__.__name__ == "Data2D" or \
3291                    self.enable2D:
3292            self.slit_smearer.Disable()
3293            self.pinhole_smearer.Enable(True)
3294            self.default_mask = copy.deepcopy(self.data.mask)
3295        else:
3296            self.slit_smearer.Enable(True)
3297            self.pinhole_smearer.Enable(True)
3298
3299
3300class BGTextCtrl(wx.TextCtrl):
3301    """
3302    Text control used to display outputs.
3303    No editing allowed. The background is
3304    grayed out. User can't select text.
3305    """
3306    def __init__(self, *args, **kwds):
3307        wx.TextCtrl.__init__(self, *args, **kwds)
3308        self.SetEditable(False)
3309        self.SetBackgroundColour(self.GetParent().parent.GetBackgroundColour())
3310
3311        # Bind to mouse event to avoid text highlighting
3312        # The event will be skipped once the call-back
3313        # is called.
3314        self.Bind(wx.EVT_MOUSE_EVENTS, self._click)
3315
3316    def _click(self, event):
3317        """
3318        Prevent further handling of the mouse event
3319        by not calling Skip().
3320        """
3321        pass
Note: See TracBrowser for help on using the repository browser.