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

Last change on this file since e4fe091 was bf44249e, checked in by wojciech, 8 years ago

Fixed a few pylint issues

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