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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 6d3c4e7 was 6d3c4e7, checked in by Adam Washington <adam.washington@…>, 7 years ago

Tidy up oddly written conditional

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