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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249unittest-saveload
Last change on this file since dabcaf7 was dabcaf7, checked in by krzywon, 6 years ago

Change error boxes to match style of other non-modifiable boxes in fitting.

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