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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249
Last change on this file since 462e019 was 462e019, checked in by butler, 5 years ago

Fix minor logic error in 2D smearing.

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