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

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 9087214 was 9087214, checked in by jhbakker, 8 years ago

Merge branch 'master' into Jurrian1D, fingers crossed!

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