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

ticket-1094-headless
Last change on this file since cb44d66 was cb44d66, checked in by Paul Kienzle <pkienzle@…>, 5 years ago

Merge branch 'master' into ticket-1094-headless

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