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

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 277257f was 277257f, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

clean up plugin-model handling code; preserve active parameter values when plugin is updated

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