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

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

Merge branch 'master' into ticket-1094-headless

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