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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 132f3d6 was 1bcc48d, checked in by Adam Washington <adam.washington@…>, 7 years ago

Work around namespace bug

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