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

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

Merge branch 'master' into ticket-1094-headless

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