source: sasview/src/sas/sasgui/perspectives/fitting/fitpage.py @ 6df015de

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 6df015de was 6df015de, checked in by jhbakker, 8 years ago

Replaced fitpage to include transform box

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