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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since d85c194 was d85c194, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 8 years ago

Remaining modules refactored

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