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

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 50fcb09 was 50fcb09, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

move qsmearing to fit package

  • Property mode set to 100644
File size: 126.0 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):
1146        """
1147        call back for model selection
1148        """
1149        self.Show(False)
1150        copy_flag = False
1151        is_poly_enabled = None
1152        if event is not None:
1153            if (event.GetEventObject() == self.formfactorbox
1154                    and self.structurebox.GetLabel() != 'None')\
1155                    or event.GetEventObject() == self.structurebox\
1156                    or event.GetEventObject() == self.multifactorbox:
1157                copy_flag = self.get_copy_params()
1158                is_poly_enabled = self.enable_disp.GetValue()
1159
1160        self._on_select_model_helper()
1161        self.set_model_param_sizer(self.model)
1162        if self.model is None:
1163            self._set_bookmark_flag(False)
1164            self._keep.Enable(False)
1165            self._set_save_flag(False)
1166        self.enable_disp.SetValue(False)
1167        self.disable_disp.SetValue(True)
1168        # TODO: should not have an untrapped exception when displaying disperser
1169        # TODO: do we need to create the disperser panel on every model change?
1170        # Note: if we fix this, then remove ID_DISPERSER_HELP from basepage
1171        try:
1172            self.set_dispers_sizer()
1173        except:
1174            pass
1175        self.state.enable_disp = self.enable_disp.GetValue()
1176        self.state.disable_disp = self.disable_disp.GetValue()
1177        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
1178        self.state.slit_smearer = self.slit_smearer.GetValue()
1179
1180        self.state.structurecombobox = self.structurebox.GetValue()
1181        self.state.formfactorcombobox = self.formfactorbox.GetValue()
1182        self.state.categorycombobox = self.categorybox.GetValue()
1183        self.enable_fit_button()
1184        if self.model is not None:
1185            self.m_name = self.model.name
1186            self.state.m_name = self.m_name
1187            self.rename_model()
1188            self._set_copy_flag(True)
1189            self._set_paste_flag(True)
1190            if self.data is not None:
1191                is_data = check_data_validity(self.data)
1192                if is_data:
1193                    self._set_bookmark_flag(not self.batch_on)
1194                    self._keep.Enable(not self.batch_on)
1195                    self._set_save_flag(True)
1196            #Setting smearing for cases with and without data.
1197            self._set_smear(self.data)
1198
1199            # more disables for 2D
1200            self._set_smear_buttons()
1201
1202            try:
1203                # update smearer sizer
1204                #This call for smearing set up caused double evaluation of
1205                #I(q) and double compilation as results
1206                #self.onSmear(None)
1207                temp_smear = None
1208                if not self.disable_smearer.GetValue():
1209                    # Set the smearer environments
1210                    temp_smear = self.current_smearer
1211            except:
1212                raise
1213                # error occured on chisqr computation
1214                # pass
1215            # event to post model to fit to fitting plugins
1216            (ModelEventbox, EVT_MODEL_BOX) = wx.lib.newevent.NewEvent()
1217
1218            # set smearing value whether or not data contain the smearing info
1219            evt = ModelEventbox(model=self.model,
1220                                smearer=temp_smear,
1221                                enable_smearer=not self.disable_smearer.GetValue(),
1222                                qmin=float(self.qmin_x),
1223                                uid=self.uid,
1224                                caption=self.window_caption,
1225                                qmax=float(self.qmax_x))
1226
1227            self._manager._on_model_panel(evt=evt)
1228            self.mbox_description.SetLabel("Model [ %s ]" %
1229                                           str(self.model.name))
1230            self.mbox_description.SetForegroundColour(wx.BLUE)
1231            self.state.model = self.model.clone()
1232            self.state.model.name = self.model.name
1233
1234        if event is not None:
1235            # post state to fit panel
1236            new_event = PageInfoEvent(page=self)
1237            wx.PostEvent(self.parent, new_event)
1238            # update list of plugins if new plugin is available
1239            custom_model = CUSTOM_MODEL
1240            mod_cat = self.categorybox.GetStringSelection()
1241            if mod_cat == custom_model:
1242                temp = self.parent.update_model_list()
1243                if temp:
1244                    self.model_list_box = temp
1245                    current_val = self.formfactorbox.GetLabel()
1246                    pos = self.formfactorbox.GetSelection()
1247                    self._show_combox_helper()
1248                    self.formfactorbox.SetSelection(pos)
1249                    self.formfactorbox.SetValue(current_val)
1250            # when select a model only from guictr/button
1251            if is_poly_enabled is not None:
1252                self.enable_disp.SetValue(is_poly_enabled)
1253                self.disable_disp.SetValue(not is_poly_enabled)
1254                self._set_dipers_Param(event=None)
1255                self.state.enable_disp = self.enable_disp.GetValue()
1256                self.state.disable_disp = self.disable_disp.GetValue()
1257
1258            # Keep the previous param values
1259            if copy_flag:
1260                self.get_paste_params(copy_flag)
1261            wx.CallAfter(self._onDraw, None)
1262
1263        else:
1264            self._draw_model()
1265
1266        if self.batch_on:
1267            self.slit_smearer.Enable(False)
1268            self.pinhole_smearer.Enable(False)
1269            self.btEditMask.Disable()
1270            self.EditMask_title.Disable()
1271
1272        self.Show(True)
1273        self.SetupScrolling()
1274
1275    def _onparamEnter(self, event):
1276        """
1277        when enter value on panel redraw model according to changed
1278        """
1279        if self.model is None:
1280            msg = "Please select a Model first..."
1281            wx.MessageBox(msg, 'Info')
1282            return
1283
1284        # default flag
1285        flag = False
1286        self.fitrange = True
1287        # get event object
1288        tcrtl = event.GetEventObject()
1289        # Clear msg if previously shown.
1290        msg = ""
1291        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1292
1293        if check_float(tcrtl):
1294            flag = self._onparamEnter_helper()
1295            self.show_npts2fit()
1296            if self.fitrange:
1297                temp_smearer = None
1298                if not self.disable_smearer.GetValue():
1299                    temp_smearer = self.current_smearer
1300                    # set smearing value whether or not data contain the
1301                    # smearing info
1302                    if self.slit_smearer.GetValue():
1303                        flag1 = self.update_slit_smear()
1304                        flag = flag or flag1
1305                    elif self.pinhole_smearer.GetValue():
1306                        flag1 = self.update_pinhole_smear()
1307                        flag = flag or flag1
1308                elif self.data.__class__.__name__ != "Data2D" and \
1309                        not self.enable2D:
1310                    enable_smearer = not self.disable_smearer.GetValue()
1311                    self._manager.set_smearer(smearer=temp_smearer,
1312                                              fid=self.data.id,
1313                                              uid=self.uid,
1314                                              qmin=float(self.qmin_x),
1315                                              qmax=float(self.qmax_x),
1316                                              enable_smearer=enable_smearer)
1317                if flag:
1318                    # self.compute_chisqr(smearer= temp_smearer)
1319
1320                    # new state posted
1321                    if self.state_change:
1322                        # self._undo.Enable(True)
1323                        event = PageInfoEvent(page=self)
1324                        wx.PostEvent(self.parent, event)
1325                    self.state_change = False
1326            else:
1327                # invalid fit range: do nothing here:
1328                # msg already displayed in validate
1329                return
1330        else:
1331            self.save_current_state()
1332            msg = "Cannot Plot: Must enter a number!!!  "
1333            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1334
1335        self.save_current_state()
1336        return
1337
1338    def _onparamRangeEnter(self, event):
1339        """
1340        Check validity of value enter in the parameters range field
1341        """
1342        tcrtl = event.GetEventObject()
1343        # Clear msg if previously shown.
1344        msg = ""
1345        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1346        # Flag to register when a parameter has changed.
1347        is_modified = False
1348        if tcrtl.GetValue().lstrip().rstrip() != "":
1349            try:
1350                tcrtl.SetBackgroundColour(wx.WHITE)
1351                self._check_value_enter(self.fittable_param)
1352                self._check_value_enter(self.parameters)
1353            except:
1354                tcrtl.SetBackgroundColour("pink")
1355                msg = "Model Error:wrong value entered : %s" % sys.exc_value
1356                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1357                return
1358        else:
1359            tcrtl.SetBackgroundColour(wx.WHITE)
1360
1361        # self._undo.Enable(True)
1362        self.save_current_state()
1363        event = PageInfoEvent(page=self)
1364        wx.PostEvent(self.parent, event)
1365        self.state_change = False
1366
1367    def qrang_set_focus(self, event=None):
1368        """
1369        ON Qrange focus
1370        """
1371        if event is not None:
1372            event.Skip()
1373        # tcrtl = event.GetEventObject()
1374        self._validate_qrange(self.qmin, self.qmax)
1375
1376    def qrange_click(self, event):
1377        """
1378        On Qrange textctrl click, make the qrange lines in the plot
1379        """
1380        if event is not None:
1381            event.Skip()
1382        if self.data.__class__.__name__ == "Data2D":
1383            return
1384        is_click = event.LeftDown()
1385        if is_click:
1386            d_id = self.data.id
1387            d_group_id = self.data.group_id
1388            act_ctrl = event.GetEventObject()
1389            wx.PostEvent(self._manager.parent,
1390                         PlotQrangeEvent(ctrl=[self.qmin, self.qmax], id=d_id,
1391                                     group_id=d_group_id, leftdown=is_click,
1392                                     active=act_ctrl))
1393
1394    def on_qrange_text(self, event):
1395        """
1396        #On q range value updated. DO not combine with qrange_click().
1397        """
1398        if event is not None:
1399            event.Skip()
1400        if self.data.__class__.__name__ == "Data2D":
1401            return
1402        act_ctrl = event.GetEventObject()
1403        d_id = self.data.id
1404        d_group_id = self.data.group_id
1405        wx.PostEvent(self._manager.parent,
1406                     PlotQrangeEvent(ctrl=[self.qmin, self.qmax], id=d_id,
1407                                     group_id=d_group_id, leftdown=False,
1408                                     active=act_ctrl))
1409        self._validate_qrange(self.qmin, self.qmax)
1410
1411    def on_key(self, event):
1412        """
1413        On Key down
1414        """
1415        event.Skip()
1416        if self.data.__class__.__name__ == "Data2D":
1417            return
1418        ctrl = event.GetEventObject()
1419        try:
1420            x_data = float(ctrl.GetValue())
1421        except:
1422            return
1423        key = event.GetKeyCode()
1424        length = len(self.data.x)
1425        indx = (np.abs(self.data.x - x_data)).argmin()
1426        # return array.flat[idx]
1427        if key == wx.WXK_PAGEUP or key == wx.WXK_NUMPAD_PAGEUP:
1428            indx += 1
1429            if indx >= length:
1430                indx = length - 1
1431        elif key == wx.WXK_PAGEDOWN or key == wx.WXK_NUMPAD_PAGEDOWN:
1432            indx -= 1
1433            if indx < 0:
1434                indx = 0
1435        else:
1436            return
1437        ctrl.SetValue(str(self.data.x[indx]))
1438        self._validate_qrange(self.qmin, self.qmax)
1439
1440    def _onQrangeEnter(self, event):
1441        """
1442        Check validity of value enter in the Q range field
1443        """
1444        tcrtl = event.GetEventObject()
1445        # Clear msg if previously shown.
1446        msg = ""
1447        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1448        # For theory mode
1449        if not self.data.is_data:
1450            self.npts_x = self.Npts_total.GetValue()
1451            self.Npts_fit.SetValue(self.npts_x)
1452            self.create_default_data()
1453        # Flag to register when a parameter has changed.
1454        if tcrtl.GetValue().lstrip().rstrip() != "":
1455            try:
1456                tcrtl.SetBackgroundColour(wx.WHITE)
1457                # If qmin and qmax have been modified, update qmin and qmax
1458                if self._validate_qrange(self.qmin, self.qmax):
1459                    tempmin = float(self.qmin.GetValue())
1460                    if tempmin != self.qmin_x:
1461                        self.qmin_x = tempmin
1462                    tempmax = float(self.qmax.GetValue())
1463                    if tempmax != self.qmax_x:
1464                        self.qmax_x = tempmax
1465                else:
1466                    tcrtl.SetBackgroundColour("pink")
1467                    msg = "Model Error:wrong value entered : %s" % sys.exc_value
1468                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1469                    return
1470            except:
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            # Check if # of points for theory model are valid(>0).
1476            # check for 2d
1477            if self.data.__class__.__name__ == "Data2D" or \
1478                    self.enable2D:
1479                # set mask
1480                radius = np.sqrt(self.data.qx_data * self.data.qx_data +
1481                                    self.data.qy_data * self.data.qy_data)
1482                index_data = ((self.qmin_x <= radius) & (radius <= self.qmax_x))
1483                index_data = (index_data) & (self.data.mask)
1484                index_data = (index_data) & (np.isfinite(self.data.data))
1485                if len(index_data[index_data]) < 10:
1486                    msg = "Cannot Plot :No or too little npts in"
1487                    msg += " that data range!!!  "
1488                    wx.PostEvent(self._manager.parent,
1489                                 StatusEvent(status=msg))
1490                    return
1491                else:
1492                    # self.data.mask = index_data
1493                    # self.Npts_fit.SetValue(str(len(self.data.mask)))
1494                    self.show_npts2fit()
1495            else:
1496                index_data = ((self.qmin_x <= self.data.x) &
1497                              (self.data.x <= self.qmax_x))
1498                self.Npts_fit.SetValue(str(len(self.data.x[index_data])))
1499
1500            self.npts_x = self.Npts_total.GetValue()
1501            self.create_default_data()
1502            self._save_plotting_range()
1503        else:
1504            tcrtl.SetBackgroundColour("pink")
1505            msg = "Model Error:wrong value entered!!!"
1506            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1507
1508        self._draw_model()
1509        self.save_current_state()
1510        event = PageInfoEvent(page=self)
1511        wx.PostEvent(self.parent, event)
1512        self.state_change = False
1513        return
1514
1515    def _clear_Err_on_Fit(self):
1516        """
1517        hide the error text control shown
1518        after fitting
1519        """
1520
1521        if self.is_mac:
1522            return
1523        if hasattr(self, "text2_3"):
1524            self.text2_3.Hide()
1525
1526        if len(self.parameters) > 0:
1527            for item in self.parameters:
1528                if item[0].IsShown():
1529                    # Skip the angle parameters if 1D data
1530                    if self.data.__class__.__name__ != "Data2D" and \
1531                            not self.enable2D:
1532                        if item in self.orientation_params:
1533                            continue
1534                    if item in self.param_toFit:
1535                        continue
1536                    # hide statictext +/-
1537                    if len(item) < 4:
1538                        continue
1539                    if item[3] is not None and item[3].IsShown():
1540                        item[3].Hide()
1541                    # hide textcrtl  for error after fit
1542                    if item[4] is not None and item[4].IsShown():
1543                        item[4].Hide()
1544
1545        if len(self.fittable_param) > 0:
1546            for item in self.fittable_param:
1547                if item[0].IsShown():
1548                    # Skip the angle parameters if 1D data
1549                    if self.data.__class__.__name__ != "Data2D" and \
1550                            not self.enable2D:
1551                        if item in self.orientation_params:
1552                            continue
1553                    if item in self.param_toFit:
1554                        continue
1555                    if len(item) < 4:
1556                        continue
1557                    # hide statictext +/-
1558                    if item[3] is not None and item[3].IsShown():
1559                        item[3].Hide()
1560                    # hide textcrtl  for error after fit
1561                    if item[4] is not None and item[4].IsShown():
1562                        item[4].Hide()
1563        return
1564
1565    def _get_defult_custom_smear(self):
1566        """
1567        Get the defult values for custum smearing.
1568        """
1569        # get the default values
1570        if self.dxl is None:
1571            self.dxl = 0.0
1572        if self.dxw is None:
1573            self.dxw = ""
1574        if self.dx_percent is None:
1575            self.dx_percent = SMEAR_SIZE_H
1576
1577    def _get_smear_info(self):
1578        """
1579        Get the smear info from data.
1580
1581        :return: self.smear_type, self.dq_l and self.dq_r,
1582            respectively the type of the smear, dq_min and
1583            dq_max for pinhole smear data
1584            while dxl and dxw for slit smear
1585        """
1586        # default
1587        self.smear_type = None
1588        self.dq_l = None
1589        self.dq_r = None
1590        data = self.data
1591        if self.data is None:
1592            return
1593        elif self.data.__class__.__name__ == "Data2D" or \
1594            self.enable2D:
1595            if data.dqx_data is None or data.dqy_data is None:
1596                return
1597            elif self.current_smearer is not None \
1598                and data.dqx_data.any() != 0 \
1599                and data.dqx_data.any() != 0:
1600                self.smear_type = "Pinhole2d"
1601                self.dq_l = format_number(np.average(data.dqx_data))
1602                self.dq_r = format_number(np.average(data.dqy_data))
1603                return
1604            else:
1605                return
1606        # check if it is pinhole smear and get min max if it is.
1607        if data.dx is not None and np.any(data.dx):
1608            self.smear_type = "Pinhole"
1609            self.dq_l = data.dx[0]
1610            self.dq_r = data.dx[-1]
1611
1612        # check if it is slit smear and get min max if it is.
1613        elif data.dxl is not None or data.dxw is not None:
1614            self.smear_type = "Slit"
1615            if data.dxl is not None and np.all(data.dxl, 0):
1616                self.dq_l = data.dxl[0]
1617            if data.dxw is not None and np.all(data.dxw, 0):
1618                self.dq_r = data.dxw[0]
1619        # return self.smear_type,self.dq_l,self.dq_r
1620
1621    def _show_smear_sizer(self):
1622        """
1623        Show only the sizers depending on smear selection
1624        """
1625        # smear disabled
1626        if self.disable_smearer.GetValue():
1627            self.smear_description_none.Show(True)
1628        # 2Dsmear
1629        elif self._is_2D():
1630            self.smear_description_accuracy_type.Show(True)
1631            self.smear_accuracy.Show(True)
1632            self.smear_description_accuracy_type.Show(True)
1633            self.smear_description_2d.Show(True)
1634            self.smear_description_2d_x.Show(True)
1635            self.smear_description_2d_y.Show(True)
1636            if self.pinhole_smearer.GetValue():
1637                self.smear_pinhole_percent.Show(True)
1638        # smear from data
1639        elif self.enable_smearer.GetValue():
1640
1641            self.smear_description_dqdata.Show(True)
1642            if self.smear_type is not None:
1643                self.smear_description_smear_type.Show(True)
1644                if self.smear_type == 'Slit':
1645                    self.smear_description_slit_height.Show(True)
1646                    self.smear_description_slit_width.Show(True)
1647                elif self.smear_type == 'Pinhole':
1648                    self.smear_description_pin_percent.Show(True)
1649                self.smear_description_smear_type.Show(True)
1650                self.smear_description_type.Show(True)
1651                self.smear_data_left.Show(True)
1652                self.smear_data_right.Show(True)
1653        # custom pinhole smear
1654        elif self.pinhole_smearer.GetValue():
1655            if self.smear_type == 'Pinhole':
1656                self.smear_message_new_p.Show(True)
1657                self.smear_description_pin_percent.Show(True)
1658
1659            self.smear_pinhole_percent.Show(True)
1660        # custom slit smear
1661        elif self.slit_smearer.GetValue():
1662            self.smear_message_new_s.Show(True)
1663            self.smear_description_slit_height.Show(True)
1664            self.smear_slit_height.Show(True)
1665            self.smear_description_slit_width.Show(True)
1666            self.smear_slit_width.Show(True)
1667
1668    def _hide_all_smear_info(self):
1669        """
1670        Hide all smearing messages in the set_smearer sizer
1671        """
1672        self.smear_description_none.Hide()
1673        self.smear_description_dqdata.Hide()
1674        self.smear_description_type.Hide()
1675        self.smear_description_smear_type.Hide()
1676        self.smear_description_accuracy_type.Hide()
1677        self.smear_description_2d_x.Hide()
1678        self.smear_description_2d_y.Hide()
1679        self.smear_description_2d.Hide()
1680
1681        self.smear_accuracy.Hide()
1682        self.smear_data_left.Hide()
1683        self.smear_data_right.Hide()
1684        self.smear_description_pin_percent.Hide()
1685        self.smear_pinhole_percent.Hide()
1686        self.smear_description_slit_height.Hide()
1687        self.smear_slit_height.Hide()
1688        self.smear_description_slit_width.Hide()
1689        self.smear_slit_width.Hide()
1690        self.smear_message_new_p.Hide()
1691        self.smear_message_new_s.Hide()
1692
1693    def _set_accuracy_list(self):
1694        """
1695        Set the list of an accuracy in 2D custum smear:
1696                Xhigh, High, Med, or Low
1697        """
1698        # list of accuracy choices
1699        list = ['Low', 'Med', 'High', 'Xhigh']
1700        for idx in range(len(list)):
1701            self.smear_accuracy.Append(list[idx], idx)
1702
1703    def _set_fun_box_list(self, fun_box):
1704        """
1705        Set the list of func for multifunctional models
1706        """
1707        # Check if it is multi_functional model
1708        if self.model.__class__ not in self.model_list_box["Multi-Functions"] \
1709                and not self.temp_multi_functional:
1710            return None
1711        # Get the func name list
1712        list = self.model.fun_list
1713        if len(list) == 0:
1714            return None
1715        # build function (combo)box
1716        ind = 0
1717        while(ind < len(list)):
1718            for key, val in list.iteritems():
1719                if (val == ind):
1720                    fun_box.Append(key, val)
1721                    break
1722            ind += 1
1723
1724    def _on_select_accuracy(self, event):
1725        """
1726        Select an accuracy in 2D custom smear: Xhigh, High, Med, or Low
1727        """
1728        # event.Skip()
1729        # Check if the accuracy is same as before
1730        # self.smear2d_accuracy = event.GetEventObject().GetValue()
1731        self.smear2d_accuracy = self.smear_accuracy.GetValue()
1732        if self.pinhole_smearer.GetValue():
1733            self.onPinholeSmear(event=None)
1734        else:
1735            self.onSmear(event=None)
1736            if self.current_smearer is not None:
1737                self.current_smearer.set_accuracy(accuracy=\
1738                                                  self.smear2d_accuracy)
1739        event.Skip()
1740
1741    def _on_fun_box(self, event):
1742        """
1743        Select an func: Erf,Rparabola,LParabola
1744        """
1745        fun_val = None
1746        fun_box = event.GetEventObject()
1747        name = fun_box.Name
1748        value = fun_box.GetValue()
1749        if value in self.model.fun_list:
1750            fun_val = self.model.fun_list[value]
1751
1752        self.model.setParam(name, fun_val)
1753        # save state
1754        self._copy_parameters_state(self.str_parameters,
1755                                    self.state.str_parameters)
1756        # update params
1757        self._update_paramv_on_fit()
1758        # draw
1759        self._draw_model()
1760        self.Refresh()
1761        # get ready for new event
1762        event.Skip()
1763
1764    def _onMask(self, event):
1765        """
1766        Build a panel to allow to edit Mask
1767        """
1768        from sas.sasgui.guiframe.local_perspectives.plotting.masking import \
1769            MaskPanel as MaskDialog
1770
1771        self.panel = MaskDialog(base=self, data=self.data, id=wx.NewId())
1772        self.panel.ShowModal()
1773
1774    def _draw_masked_model(self, event):
1775        """
1776        Draw model image w/mask
1777        """
1778        is_valid_qrange = self._update_paramv_on_fit()
1779
1780        if is_valid_qrange and self.model is not None:
1781            self.panel.MakeModal(False)
1782            event.Skip()
1783            # try re draw the model plot if it exists
1784            self._draw_model()
1785            self.show_npts2fit()
1786        elif self.model is None:
1787            self.panel.MakeModal(False)
1788            event.Skip()
1789            self.show_npts2fit()
1790            msg = "No model is found on updating MASK in the model plot... "
1791            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1792        else:
1793            event.Skip()
1794            msg = ' Please consider your Q range, too.'
1795            self.panel.ShowMessage(msg)
1796
1797    def _set_smear(self, data):
1798        """
1799        Set_smear
1800        """
1801        if data is None:
1802            return
1803        self.current_smearer = smear_selection(data, self.model)
1804        flag = self.disable_smearer.GetValue()
1805        if self.current_smearer is None:
1806            self.enable_smearer.Disable()
1807        else:
1808            self.enable_smearer.Enable()
1809        if not flag:
1810            self.onSmear(None)
1811
1812    def get_view_mode(self):
1813        """
1814        return True if the panel allow 2D or False if 1D
1815        """
1816        return self.enable2D
1817
1818    def compute_data_set_range(self, data_list):
1819        """
1820        find the range that include all data  in the set
1821        return the minimum and the maximum values
1822        """
1823        if data_list is not None and data_list != []:
1824            for data in data_list:
1825                qmin, qmax, npts = self.compute_data_range(data)
1826                self.qmin_data_set = min(self.qmin_data_set, qmin)
1827                self.qmax_data_set = max(self.qmax_data_set, qmax)
1828                self.npts_data_set += npts
1829        return self.qmin_data_set, self.qmax_data_set, self.npts_data_set
1830
1831    def compute_data_range(self, data):
1832        """
1833        compute the minimum and the maximum range of the data
1834        return the npts contains in data
1835        :param data:
1836        """
1837        qmin, qmax, npts = None, None, None
1838        if data is not None:
1839            if not hasattr(data, "data"):
1840                try:
1841                    qmin = min(data.x)
1842                    # Maximum value of data
1843                    qmax = max(data.x)
1844                    npts = len(data.x)
1845                except:
1846                    msg = "Unable to find min/max/length of \n data named %s" %\
1847                                data.filename
1848                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
1849                                               info="error"))
1850                    raise ValueError, msg
1851
1852            else:
1853                qmin = 0
1854                try:
1855                    x = max(math.fabs(data.xmin), math.fabs(data.xmax))
1856                    y = max(math.fabs(data.ymin), math.fabs(data.ymax))
1857                except:
1858                    msg = "Unable to find min/max of \n data named %s" % \
1859                                data.filename
1860                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
1861                                               info="error"))
1862                    raise ValueError, msg
1863                # Maximum value of data
1864                qmax = math.sqrt(x * x + y * y)
1865                npts = len(data.data)
1866        return qmin, qmax, npts
1867
1868    def set_data(self, data):
1869        """
1870        reset the current data
1871        """
1872        id = None
1873        flag = False
1874        is_data = False
1875        npts = 0
1876        try:
1877            old_id = self.data.id
1878            old_group_id = self.data.group_id
1879        except:
1880            old_id = id
1881            old_group_id = id
1882        if self.data is not None:
1883            is_data = check_data_validity(self.data)
1884        if not is_data and data is not None:
1885                flag = True
1886        if data is not None:
1887            if is_data:
1888                self.graph_id = self.data.group_id
1889                flag = (data.id != self.data.id)
1890        self.data = data
1891        if check_data_validity(data):
1892            self.graph_id = data.group_id
1893        self.data.group_id = self.graph_id
1894
1895        if self.data is None:
1896            data_name = ""
1897            self._set_bookmark_flag(False)
1898            self._keep.Enable(False)
1899            self._set_save_flag(False)
1900        else:
1901            if self.model is not None:
1902                self._set_bookmark_flag(not self.batch_on)
1903                self._keep.Enable(not self.batch_on)
1904            if self.data.is_data:
1905                self._set_save_flag(True)
1906                self._set_preview_flag(True)
1907
1908            # more disables for 2D
1909            di_flag = False
1910            dq_flag = False
1911            if self.data.__class__.__name__ == "Data2D" or \
1912                        self.enable2D:
1913                self.slit_smearer.Disable()
1914                self.pinhole_smearer.Enable(True)
1915                self.default_mask = copy.deepcopy(self.data.mask)
1916                if self.data.err_data is not None \
1917                        and np.any(self.data.err_data):
1918                    di_flag = True
1919                if self.data.dqx_data is not None \
1920                        and np.any(self.data.dqx_data):
1921                    dq_flag = True
1922            else:
1923                self.slit_smearer.Enable(True)
1924                self.pinhole_smearer.Enable(True)
1925                if self.data.dy is not None and np.any(self.data.dy):
1926                    di_flag = True
1927                if self.data.dx is not None and np.any(self.data.dx):
1928                    dq_flag = True
1929                elif self.data.dxl is not None and np.any(self.data.dxl):
1930                    dq_flag = True
1931
1932            if dq_flag:
1933                self.enable_smearer.Enable(True)
1934                self.enable_smearer.SetValue(True)
1935                self.disable_smearer.SetValue(False)
1936            else:
1937                self.enable_smearer.Disable()
1938                self.disable_smearer.Enable(True)
1939                self.disable_smearer.SetValue(True)
1940
1941            if di_flag:
1942                self.dI_didata.Enable(True)
1943                self.dI_didata.SetValue(True)
1944                self.weightbt_string = self.dI_didata.GetLabelText()
1945            else:
1946                self.dI_didata.Enable(False)
1947                self.dI_noweight.SetValue(True)
1948                self.weightbt_string = self.dI_noweight.GetLabelText()
1949
1950            # Enable weighting radio buttons
1951            self.dI_noweight.Enable(True)
1952            self.dI_sqrdata.Enable(True)
1953            self.dI_idata.Enable(True)
1954
1955            self.formfactorbox.Enable()
1956            self.structurebox.Enable()
1957            data_name = self.data.name
1958            _, _, npts = self.compute_data_range(self.data)
1959            # set maximum range for x in linear scale
1960            if not hasattr(self.data, "data"):  # Display only for 1D data fit
1961                self.btEditMask.Disable()
1962                self.EditMask_title.Disable()
1963            else:
1964                self.btEditMask.Enable()
1965                self.EditMask_title.Enable()
1966
1967        self.Npts_total.SetValue(str(npts))
1968        # default:number of data points selected to fit
1969        self.Npts_fit.SetValue(str(npts))
1970        self.Npts_total.SetEditable(False)
1971        self.Npts_total.SetBackgroundColour(
1972                                    self.GetParent().GetBackgroundColour())
1973
1974        self.Npts_total.Bind(wx.EVT_MOUSE_EVENTS, self._npts_click)
1975        self.pointsbox.Disable()
1976        self.dataSource.SetValue(data_name)
1977        self.state.data = data
1978        self.enable_fit_button()
1979        # send graph_id to page_finder
1980        self._manager.set_graph_id(uid=self.uid, graph_id=self.graph_id)
1981        # focus the page
1982        if check_data_validity(data):
1983            self.data_box_description.SetForegroundColour(wx.BLUE)
1984
1985        if self.batch_on:
1986            self.slit_smearer.Enable(False)
1987            self.pinhole_smearer.Enable(False)
1988            self.btEditMask.Disable()
1989            self.EditMask_title.Disable()
1990
1991        self.on_smear_helper()
1992        self.on_set_focus(None)
1993        self.Refresh()
1994        # update model plot with new data information
1995        if flag:
1996            if self.data.__class__.__name__ == "Data2D":
1997                self.enable2D = True
1998                self.model_view.SetLabel("2D Mode")
1999            else:
2000                self.enable2D = False
2001                self.model_view.SetLabel("1D Mode")
2002            self.model_view.Disable()
2003            #  replace data plot on combo box selection
2004            # by removing the previous selected data
2005            try:
2006                wx.PostEvent(self._manager.parent,
2007                             NewPlotEvent(action="delete",
2008                                          group_id=old_group_id, id=old_id))
2009            except:
2010                pass
2011            # plot the current selected data
2012            wx.PostEvent(self._manager.parent,
2013                         NewPlotEvent(action="check", plot=self.data,
2014                                      title=str(self.data.title)))
2015            self._draw_model()
2016
2017    def _npts_click(self, event):
2018        """
2019        Prevent further handling of the mouse event on Npts_total
2020        by not calling Skip().
2021        """
2022        pass
2023
2024    def reset_page(self, state, first=False):
2025        """
2026        reset the state
2027        """
2028        try:
2029            self.reset_page_helper(state)
2030
2031            self.select_param()
2032            # Save state_fit
2033            self.save_current_state_fit()
2034        except:
2035            self._show_combox_helper()
2036            msg = "Error: This model state has missing or outdated "
2037            msg += "information.\n"
2038            msg += traceback.format_exc()
2039            wx.PostEvent(self._manager.parent,
2040                         StatusEvent(status=msg, info="error"))
2041        self._lay_out()
2042        self.Refresh()
2043
2044    def get_range(self):
2045        """
2046        return the fitting range
2047        """
2048        return float(self.qmin_x), float(self.qmax_x)
2049
2050    def get_npts2fit(self):
2051        """
2052        return numbers of data points within qrange
2053
2054        :Note: This is to normalize chisq by Npts of fit
2055
2056        """
2057        if self.data is None:
2058            return
2059        npts2fit = 0
2060        qmin, qmax = self.get_range()
2061        if self.data.__class__.__name__ == "Data2D" or \
2062                        self.enable2D:
2063            radius = np.sqrt(self.data.qx_data * self.data.qx_data +
2064                                self.data.qy_data * self.data.qy_data)
2065            index_data = (self.qmin_x <= radius) & (radius <= self.qmax_x)
2066            index_data = (index_data) & (self.data.mask)
2067            index_data = (index_data) & (np.isfinite(self.data.data))
2068            npts2fit = len(self.data.data[index_data])
2069        else:
2070            for qx in self.data.x:
2071                if qmax >= qx >= qmin:
2072                    npts2fit += 1
2073        return npts2fit
2074
2075    def show_npts2fit(self):
2076        """
2077        setValue Npts for fitting
2078        """
2079        self.Npts_fit.SetValue(str(self.get_npts2fit()))
2080
2081    def get_chi2(self):
2082        """
2083        return the current chi2
2084        """
2085        return self.tcChi.GetValue()
2086
2087    def onsetValues(self, chisqr, p_name, out, cov):
2088        """
2089        Build the panel from the fit result
2090
2091        :param chisqr: Value of the goodness of fit metric
2092        :param p_name: the name of parameters
2093        :param out: list of parameter with the best value found during fitting
2094        :param cov: Covariance matrix
2095
2096        """
2097
2098        # make sure stop button to fit button all the time
2099        self._on_fit_complete()
2100        if out is None or not np.isfinite(chisqr):
2101            raise ValueError, "Fit error occured..."
2102
2103        is_modified = False
2104        has_error = False
2105        dispersity = ''
2106
2107        # Hide textctrl boxes of errors.
2108        self._clear_Err_on_Fit()
2109
2110        # Check if chi2 is finite
2111        if chisqr is not None and np.isfinite(chisqr):
2112            # format chi2
2113            chi2 = format_number(chisqr, True)
2114            self.tcChi.SetValue(chi2)
2115            self.tcChi.Refresh()
2116        else:
2117            self.tcChi.SetValue("-")
2118
2119        # Hide error title
2120        if self.text2_3.IsShown() and not self.is_mac:
2121            self.text2_3.Hide()
2122
2123        try:
2124            if self.enable_disp.GetValue():
2125                if hasattr(self, "text_disp_1"):
2126                    if self.text_disp_1 is not None and not self.is_mac:
2127                        self.text_disp_1.Hide()
2128        except:
2129            dispersity = None
2130            pass
2131
2132        i = 0
2133        # Set the panel when fit result are list
2134
2135        for item in self.param_toFit:
2136            if len(item) > 5 and item is not None:
2137
2138                if item[0].IsShown():
2139                    # reset error value to initial state
2140                    if not self.is_mac:
2141                        item[3].Hide()
2142                        item[4].Hide()
2143                    for ind in range(len(out)):
2144                        if item[1] == p_name[ind]:
2145                            break
2146                    if len(out) > 0 and out[ind] is not None:
2147                        val_out = format_number(out[ind], True)
2148                        item[2].SetValue(val_out)
2149
2150                    if(cov is not None and len(cov) == len(out)):
2151                        try:
2152                            if dispersity is not None:
2153                                if self.enable_disp.GetValue():
2154                                    if hasattr(self, "text_disp_1"):
2155                                        if self.text_disp_1 is not None:
2156                                            if not self.text_disp_1.IsShown()\
2157                                                  and not self.is_mac:
2158                                                self.text_disp_1.Show(True)
2159                        except:
2160                            pass
2161
2162                        if cov[ind] is not None:
2163                            if np.isfinite(float(cov[ind])):
2164                                val_err = format_number(cov[ind], True)
2165                                item[4].SetForegroundColour(wx.BLACK)
2166                            else:
2167                                val_err = 'NaN'
2168                                item[4].SetForegroundColour(wx.RED)
2169                            if not self.is_mac:
2170                                item[3].Show(True)
2171                                item[4].Show(True)
2172                            item[4].SetValue(val_err)
2173                            has_error = True
2174                i += 1
2175            else:
2176                raise ValueError, "onsetValues: Invalid parameters..."
2177        # Show error title when any errors displayed
2178        if has_error:
2179            if not self.text2_3.IsShown():
2180                self.text2_3.Show(True)
2181        # save current state
2182        self.save_current_state()
2183
2184        # plot model ( when drawing, do not update chisqr value again)
2185        self._draw_model(update_chisqr=False, source='fit')
2186
2187    def onWeighting(self, event):
2188        """
2189        On Weighting radio button event, sets the weightbt_string
2190        """
2191        self.weightbt_string = event.GetEventObject().GetLabelText()
2192        self._set_weight()
2193
2194    def _set_weight(self, is_2D=None):
2195        """
2196        Set weight in fit problem
2197        """
2198        # compute weight for the current data
2199        flag_weight = self.get_weight_flag()
2200        if is_2D is None:
2201            is_2D = self._is_2D()
2202        self._manager.set_fit_weight(uid=self.uid,
2203                                     flag=flag_weight,
2204                                     is2d=is_2D,
2205                                     fid=None)
2206
2207    def onPinholeSmear(self, event):
2208        """
2209        Create a custom pinhole smear object that will change the way residuals
2210        are compute when fitting
2211
2212        :Note: accuracy is given by strings'High','Med', 'Low' FOR 2d,
2213                     None for 1D
2214
2215        """
2216        # Need update param values
2217        self._update_paramv_on_fit()
2218
2219        if event is not None:
2220            tcrtl = event.GetEventObject()
2221            # event case of radio button
2222            if tcrtl.GetValue():
2223                self.dx_percent = 0.0
2224                is_new_pinhole = True
2225            else:
2226                is_new_pinhole = self._is_changed_pinhole()
2227        else:
2228            is_new_pinhole = True
2229        # if any value is changed
2230        if is_new_pinhole:
2231            self._set_pinhole_smear()
2232        # hide all silt sizer
2233        self._hide_all_smear_info()
2234
2235        # show relevant slit sizers
2236        self._show_smear_sizer()
2237
2238        self.sizer_set_smearer.Layout()
2239        # we need FitInside here not just self.Layout to ensure all the sizers
2240        # end up with the necessasary space to in the scroll panel. In
2241        # particular the compute and fit buttons end up on top of each other
2242        # PDB Nov 28 2015.
2243        self.FitInside()
2244
2245        if event is not None:
2246            event.Skip()
2247        # self._undo.Enable(True)
2248        self.save_current_state()
2249        event = PageInfoEvent(page=self)
2250        wx.PostEvent(self.parent, event)
2251
2252    def _is_changed_pinhole(self):
2253        """
2254        check if any of pinhole smear is changed
2255
2256        :return: True or False
2257
2258        """
2259        # get the values
2260        pin_percent = self.smear_pinhole_percent.GetValue()
2261
2262        # Check changes in slit heigth
2263        try:
2264            dx_percent = float(pin_percent)
2265        except:
2266            return True
2267        if self.dx_percent != dx_percent:
2268            return True
2269        return False
2270
2271    def _set_pinhole_smear(self):
2272        """
2273        Set custom pinhole smear
2274
2275        :return: msg
2276
2277        """
2278        # copy data
2279        data = copy.deepcopy(self.data)
2280        if self._is_2D():
2281            self.smear_type = 'Pinhole2d'
2282            len_data = len(data.data)
2283            data.dqx_data = np.zeros(len_data)
2284            data.dqy_data = np.zeros(len_data)
2285        else:
2286            self.smear_type = 'Pinhole'
2287            len_data = len(data.x)
2288            data.dx = np.zeros(len_data)
2289            data.dxl = None
2290            data.dxw = None
2291        msg = None
2292
2293        get_pin_percent = self.smear_pinhole_percent
2294
2295        if not check_float(get_pin_percent):
2296            get_pin_percent.SetBackgroundColour("pink")
2297            msg = "Model Error:wrong value entered!!!"
2298        else:
2299            if len_data < 2:
2300                len_data = 2
2301            self.dx_percent = float(get_pin_percent.GetValue())
2302            if self.dx_percent < 0:
2303                get_pin_percent.SetBackgroundColour("pink")
2304                msg = "Model Error:This value can not be negative!!!"
2305            elif self.dx_percent is not None:
2306                percent = self.dx_percent/100
2307                if self._is_2D():
2308                    data.dqx_data[data.dqx_data == 0] = percent * data.qx_data
2309                    data.dqy_data[data.dqy_data == 0] = percent * data.qy_data
2310                else:
2311                    data.dx = percent * data.x
2312            self.current_smearer = smear_selection(data, self.model)
2313            # 2D need to set accuracy
2314            if self._is_2D():
2315                self.current_smearer.set_accuracy(
2316                    accuracy=self.smear2d_accuracy)
2317
2318        if msg is not None:
2319            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2320        else:
2321            get_pin_percent.SetBackgroundColour("white")
2322        # set smearing value whether or not the data contain the smearing info
2323
2324        enable_smearer = not self.disable_smearer.GetValue()
2325        self._manager.set_smearer(smearer=self.current_smearer,
2326                                  fid=self.data.id,
2327                                  qmin=float(self.qmin_x),
2328                                  qmax=float(self.qmax_x),
2329                                  enable_smearer=enable_smearer,
2330                                  uid=self.uid)
2331        return msg
2332
2333    def update_pinhole_smear(self):
2334        """
2335        called by kill_focus on pinhole TextCntrl
2336        to update the changes
2337
2338        :return: False when wrong value was entered
2339
2340        """
2341        # msg default
2342        msg = None
2343        # check if any value is changed
2344        if self._is_changed_pinhole():
2345            msg = self._set_pinhole_smear()
2346        wx.CallAfter(self.save_current_state)
2347
2348        if msg is not None:
2349            return False
2350        else:
2351            return True
2352
2353    def onSlitSmear(self, event):
2354        """
2355        Create a custom slit smear object that will change the way residuals
2356        are compute when fitting
2357        """
2358        # Need update param values
2359        self._update_paramv_on_fit()
2360
2361        # msg default
2362        msg = None
2363        # for event given
2364        if event is not None:
2365            tcrtl = event.GetEventObject()
2366            # event case of radio button
2367            if tcrtl.GetValue():
2368                self.dxl = 0.0
2369                self.dxw = 0.0
2370                is_new_slit = True
2371            else:
2372                is_new_slit = self._is_changed_slit()
2373        else:
2374            is_new_slit = True
2375
2376        # if any value is changed
2377        if is_new_slit:
2378            msg = self._set_slit_smear()
2379
2380        # hide all silt sizer
2381        self._hide_all_smear_info()
2382        # show relevant slit sizers
2383        self._show_smear_sizer()
2384        self.sizer_set_smearer.Layout()
2385        # we need FitInside here not just self.Layout to ensure all the sizers
2386        # end up with the necessasary space to in the scroll panel. In
2387        # particular the compute and fit buttons end up on top of each other
2388        # PDB Nov 28 2015.
2389        self.FitInside()
2390
2391        if event is not None:
2392            event.Skip()
2393        self.save_current_state()
2394        event = PageInfoEvent(page=self)
2395        wx.PostEvent(self.parent, event)
2396        if msg is not None:
2397            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2398
2399    def _is_changed_slit(self):
2400        """
2401        check if any of slit lengths is changed
2402
2403        :return: True or False
2404
2405        """
2406        # get the values
2407        width = self.smear_slit_width.GetValue()
2408        height = self.smear_slit_height.GetValue()
2409
2410        # check and change the box bg color if it was pink
2411        #    but it should be white now
2412        # because this is the case that _set_slit_smear() will not handle
2413        if height.lstrip().rstrip() == "":
2414            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2415        if width.lstrip().rstrip() == "":
2416            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2417
2418        # Check changes in slit width
2419        if width == "":
2420            dxw = 0.0
2421        else:
2422            try:
2423                dxw = float(width)
2424            except:
2425                return True
2426        if self.dxw != dxw:
2427            return True
2428
2429        # Check changes in slit heigth
2430        if height == "":
2431            dxl = 0.0
2432        else:
2433            try:
2434                dxl = float(height)
2435            except:
2436                return True
2437        if self.dxl != dxl:
2438            return True
2439
2440        return False
2441
2442    def _set_slit_smear(self):
2443        """
2444        Set custom slit smear
2445
2446        :return: message to inform the user about the validity
2447            of the values entered for slit smear
2448        """
2449        if self.data.__class__.__name__ == "Data2D" or self.enable2D:
2450            return
2451        # make sure once more if it is smearer
2452        data = copy.deepcopy(self.data)
2453        data_len = len(data.x)
2454        data.dx = None
2455        data.dxl = None
2456        data.dxw = None
2457        msg = None
2458
2459        try:
2460            self.dxl = float(self.smear_slit_height.GetValue())
2461            data.dxl = self.dxl * np.ones(data_len)
2462            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2463        except:
2464            self.dxl = None
2465            data.dxl = np.zeros(data_len)
2466            if self.smear_slit_height.GetValue().lstrip().rstrip() != "":
2467                self.smear_slit_height.SetBackgroundColour("pink")
2468                msg = "Wrong value entered... "
2469            else:
2470                self.smear_slit_height.SetBackgroundColour(wx.WHITE)
2471        try:
2472            self.dxw = float(self.smear_slit_width.GetValue())
2473            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2474            data.dxw = self.dxw * np.ones(data_len)
2475        except:
2476            self.dxw = None
2477            data.dxw = np.zeros(data_len)
2478            if self.smear_slit_width.GetValue().lstrip().rstrip() != "":
2479                self.smear_slit_width.SetBackgroundColour("pink")
2480                msg = "Wrong Fit value entered... "
2481            else:
2482                self.smear_slit_width.SetBackgroundColour(wx.WHITE)
2483
2484        self.current_smearer = smear_selection(data, self.model)
2485        # set smearing value whether or not the data contain the smearing info
2486        enable_smearer = not self.disable_smearer.GetValue()
2487        self._manager.set_smearer(smearer=self.current_smearer,
2488                                  fid=self.data.id,
2489                                  qmin=float(self.qmin_x),
2490                                  qmax=float(self.qmax_x),
2491                                  enable_smearer=enable_smearer,
2492                                  uid=self.uid)
2493        return msg
2494
2495    def update_slit_smear(self):
2496        """
2497        called by kill_focus on pinhole TextCntrl
2498        to update the changes
2499
2500        :return: False when wrong value was entered
2501
2502        """
2503        # msg default
2504        msg = None
2505        # check if any value is changed
2506        if self._is_changed_slit():
2507            msg = self._set_slit_smear()
2508        # self._undo.Enable(True)
2509        self.save_current_state()
2510
2511        if msg is not None:
2512            return False
2513        else:
2514            return True
2515
2516    def onSmear(self, event):
2517        """
2518        Create a smear object that will change the way residuals
2519        are computed when fitting
2520        """
2521        if event is not None:
2522            event.Skip()
2523        if self.data is None:
2524            return
2525
2526        # Need update param values
2527        self._update_paramv_on_fit()
2528        if self.model is not None:
2529            if self.data.is_data:
2530                self._manager.page_finder[self.uid].add_data(data=self.data)
2531        temp_smearer = self.on_smear_helper()
2532
2533        self.sizer_set_smearer.Layout()
2534        # we need FitInside here not just self.Layout to ensure all the sizers
2535        # end up with the necessasary space to in the scroll panel. In
2536        # particular the compute and fit buttons end up on top of each other
2537        # PDB Nov 28 2015.
2538        self.FitInside()
2539        self._set_weight()
2540
2541        # set smearing value whether or not the data contain the smearing info
2542        enable_smearer = not self.disable_smearer.GetValue()
2543        wx.CallAfter(self._manager.set_smearer, uid=self.uid,
2544                     smearer=temp_smearer,
2545                     fid=self.data.id,
2546                     qmin=float(self.qmin_x),
2547                     qmax=float(self.qmax_x),
2548                     enable_smearer=enable_smearer)
2549
2550        self.state.enable_smearer = self.enable_smearer.GetValue()
2551        self.state.disable_smearer = self.disable_smearer.GetValue()
2552        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
2553        self.state.slit_smearer = self.slit_smearer.GetValue()
2554
2555    def on_smear_helper(self, update=False):
2556        """
2557        Help for onSmear
2558
2559        :param update: force or not to update
2560        """
2561        self._get_smear_info()
2562        # renew smear sizer
2563        if self.smear_type is not None:
2564            self.smear_description_smear_type.SetValue(str(self.smear_type))
2565            self.smear_data_left.SetValue(str(self.dq_l))
2566            self.smear_data_right.SetValue(str(self.dq_r))
2567
2568        self._hide_all_smear_info()
2569        data = copy.deepcopy(self.data)
2570
2571        # make sure once more if it is smearer
2572        temp_smearer = smear_selection(data, self.model)
2573        if self.current_smearer != temp_smearer or update:
2574            self.current_smearer = temp_smearer
2575        if self.enable_smearer.GetValue():
2576            if self.current_smearer is None:
2577                wx.PostEvent(self._manager.parent,
2578                    StatusEvent(status="Data contains no smearing information"))
2579            else:
2580                wx.PostEvent(self._manager.parent,
2581                    StatusEvent(status="Data contains smearing information"))
2582
2583            self.smear_data_left.Show(True)
2584            self.smear_data_right.Show(True)
2585            temp_smearer = self.current_smearer
2586        elif self.disable_smearer.GetValue():
2587            self.smear_description_none.Show(True)
2588        elif self.pinhole_smearer.GetValue():
2589            self.onPinholeSmear(None)
2590        elif self.slit_smearer.GetValue():
2591            self.onSlitSmear(None)
2592        self._show_smear_sizer()
2593
2594        return temp_smearer
2595
2596    def on_complete_chisqr(self, event):
2597        """
2598        Display result chisqr on the panel
2599        :event: activated by fitting/ complete after draw
2600        """
2601        try:
2602            if event is None:
2603                output = "-"
2604            elif not np.isfinite(event.output):
2605                output = "-"
2606            else:
2607                output = event.output
2608            self.tcChi.SetValue(str(format_number(output, True)))
2609            self.state.tcChi = self.tcChi.GetValue()
2610        except:
2611            pass
2612
2613    def get_all_checked_params(self):
2614        """
2615        Found all parameters current check and add them to list of parameters
2616        to fit
2617        """
2618        self.param_toFit = []
2619        for item in self.parameters:
2620            if item[0].GetValue() and item not in self.param_toFit:
2621                if item[0].IsShown():
2622                    self.param_toFit.append(item)
2623        for item in self.fittable_param:
2624            if item[0].GetValue() and item not in self.param_toFit:
2625                if item[0].IsShown():
2626                    self.param_toFit.append(item)
2627        self.save_current_state_fit()
2628
2629        event = PageInfoEvent(page=self)
2630        wx.PostEvent(self.parent, event)
2631        param2fit = []
2632        for item in self.param_toFit:
2633            if item[0] and item[0].IsShown():
2634                param2fit.append(item[1])
2635        self._manager.set_param2fit(self.uid, param2fit)
2636
2637    def select_param(self, event=None):
2638        """
2639        Select TextCtrl  checked for fitting purpose and stores them
2640        in  self.param_toFit=[] list
2641        """
2642        self.param_toFit = []
2643        for item in self.parameters:
2644            # Skip t ifhe angle parameters if 1D data
2645            if self.data.__class__.__name__ != "Data2D" and\
2646                        not self.enable2D:
2647                if item in self.orientation_params:
2648                    continue
2649            # Select parameters to fit for list of primary parameters
2650            if item[0].GetValue() and item[0].IsShown():
2651                if not (item in self.param_toFit):
2652                    self.param_toFit.append(item)
2653            else:
2654                #remove parameters from the fitting list
2655                if item in self.param_toFit:
2656                    self.param_toFit.remove(item)
2657
2658        # Select parameters to fit for list of fittable parameters
2659        #        with dispersion
2660        for item in self.fittable_param:
2661            # Skip t ifhe angle parameters if 1D data
2662            if self.data.__class__.__name__ != "Data2D" and\
2663                        not self.enable2D:
2664                if item in self.orientation_params:
2665                    continue
2666            if item[0].GetValue() and item[0].IsShown():
2667                if not (item in self.param_toFit):
2668                    self.param_toFit.append(item)
2669            else:
2670                # remove parameters from the fitting list
2671                if item in self.param_toFit:
2672                    self.param_toFit.remove(item)
2673
2674        # Calculate num. of angle parameters
2675        if self.data.__class__.__name__ == "Data2D" or \
2676                       self.enable2D:
2677            len_orient_para = 0
2678        else:
2679            len_orient_para = len(self.orientation_params)  # assume even len
2680        # Total num. of angle parameters
2681        if len(self.fittable_param) > 0:
2682            len_orient_para *= 2
2683
2684        self.save_current_state_fit()
2685        if event is not None:
2686            # post state to fit panel
2687            event = PageInfoEvent(page=self)
2688            wx.PostEvent(self.parent, event)
2689
2690        param2fit = []
2691        for item in self.param_toFit:
2692            if item[0] and item[0].IsShown():
2693                param2fit.append(item[1])
2694        self._manager.set_param2fit(self.uid, param2fit)
2695
2696    def set_model_param_sizer(self, model):
2697        """
2698        Build the panel from the model content
2699
2700        :param model: the model selected in combo box for fitting purpose
2701
2702        """
2703        self.sizer3.Clear(True)
2704        self.parameters = []
2705        self.str_parameters = []
2706        self.param_toFit = []
2707        self.fittable_param = []
2708        self.fixed_param = []
2709        self.orientation_params = []
2710        self.orientation_params_disp = []
2711
2712        if model is None:
2713            self.sizer3.Layout()
2714            self.SetupScrolling()
2715            return
2716
2717        box_description = wx.StaticBox(self, wx.ID_ANY, str("Model Parameters"))
2718        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
2719        sizer = wx.GridBagSizer(5, 5)
2720        # save the current model
2721        self.model = model
2722
2723        keys = self.model.getParamList()
2724
2725        # list of dispersion parameters
2726        self.disp_list = self.model.getDispParamList()
2727
2728        def custom_compare(a, b):
2729            """
2730            Custom compare to order, first by alphabets then second by number.
2731            """
2732            # number at the last digit
2733            a_last = a[len(a) - 1]
2734            b_last = b[len(b) - 1]
2735            # default
2736            num_a = None
2737            num_b = None
2738            # split the names
2739            a2 = a.lower().split('_')
2740            b2 = b.lower().split('_')
2741            # check length of a2, b2
2742            len_a2 = len(a2)
2743            len_b2 = len(b2)
2744            # check if it contains a int number(<10)
2745            try:
2746                num_a = int(a_last)
2747            except:
2748                pass
2749            try:
2750                num_b = int(b_last)
2751            except:
2752                pass
2753            # Put 'scale' near the top; happens
2754            # when numbered param name exists
2755            if a == 'scale':
2756                return -1
2757            # both have a number
2758            if num_a is not None and num_b is not None:
2759                if num_a > num_b:
2760                    return -1
2761                # same number
2762                elif num_a == num_b:
2763                    # different last names
2764                    if a2[len_a2 - 1] != b2[len_b2 - 1] and num_a != 0:
2765                        return -cmp(a2[len_a2 - 1], b2[len_b2 - 1])
2766                    else:
2767                        return cmp(a, b)
2768                else:
2769                    return 1
2770            # one of them has a number
2771            elif num_a is not None:
2772                return 1
2773            elif num_b is not None:
2774                return -1
2775            # no numbers
2776            else:
2777                return cmp(a.lower(), b.lower())
2778
2779        # keys obtained now from ordered dict, so commenting alphabetical
2780        # ordering keys.sort(custom_compare)
2781
2782        iy = 0
2783        ix = 0
2784        sizer.Add(wx.StaticText(self, wx.ID_ANY, 'Parameter'),
2785                  (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2786        ix += 1
2787        self.text2_2 = wx.StaticText(self, wx.ID_ANY, 'Value')
2788        sizer.Add(self.text2_2, (iy, ix), (1, 1),
2789                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2790        ix += 2
2791        self.text2_3 = wx.StaticText(self, wx.ID_ANY, 'Error')
2792        sizer.Add(self.text2_3, (iy, ix), (1, 1),
2793                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2794        if not self.is_mac:
2795            self.text2_3.Hide()
2796        ix += 1
2797        self.text2_min = wx.StaticText(self, wx.ID_ANY, 'Min')
2798        sizer.Add(self.text2_min, (iy, ix), (1, 1),
2799                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2800        # self.text2_min.Hide()
2801        ix += 1
2802        self.text2_max = wx.StaticText(self, wx.ID_ANY, 'Max')
2803        sizer.Add(self.text2_max, (iy, ix), (1, 1),
2804                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2805        # self.text2_max.Hide()
2806        ix += 1
2807        self.text2_4 = wx.StaticText(self, wx.ID_ANY, '[Units]')
2808        sizer.Add(self.text2_4, (iy, ix), (1, 1),
2809                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2810        self.text2_4.Hide()
2811
2812        CHECK_STATE = False
2813        for item in keys:
2814
2815            if item not in self.disp_list and not item in \
2816                    self.model.orientation_params:
2817
2818                # prepare a spot to store errors
2819                if item not in self.model.details:
2820                    self.model.details[item] = ["", None, None]
2821
2822                iy += 1
2823                ix = 0
2824                if (self.model.__class__ in
2825                    self.model_list_box["Multi-Functions"] or
2826                    self.temp_multi_functional)\
2827                    and (item in self.model.non_fittable):
2828                    non_fittable_name = wx.StaticText(self, wx.ID_ANY, item)
2829                    sizer.Add(non_fittable_name, (iy, ix), (1, 1),
2830                            wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 21)
2831                    # add parameter value
2832                    ix += 1
2833                    value = self.model.getParam(item)
2834                    if len(self.model.fun_list) > 0:
2835                        # num = item.split('_')[1][5:7]
2836                        fun_box = wx.ComboBox(self, wx.ID_ANY, size=(100, -1),
2837                                    style=wx.CB_READONLY, name='%s' % item)
2838                        self._set_fun_box_list(fun_box)
2839                        fun_box.SetSelection(0)
2840                        # self.fun_box.SetToolTipString("A function
2841                        #    describing the interface")
2842                        wx.EVT_COMBOBOX(fun_box, wx.ID_ANY, self._on_fun_box)
2843                    else:
2844                        fun_box = ModelTextCtrl(self, wx.ID_ANY,
2845                                                size=(_BOX_WIDTH, 20),
2846                                style=wx.TE_PROCESS_ENTER, name='%s' % item)
2847                        fun_box.SetToolTipString(
2848                                "Hit 'Enter' after typing to update the plot.")
2849                        fun_box.SetValue(format_number(value, True))
2850                    sizer.Add(fun_box, (iy, ix), (1, 1), wx.EXPAND)
2851                    self.str_parameters.append([None, item, fun_box,
2852                                                None, None, None,
2853                                                None, None])
2854                else:
2855                    # add parameters name with checkbox for selecting to fit
2856                    cb = wx.CheckBox(self, wx.ID_ANY, item)
2857                    cb.SetValue(CHECK_STATE)
2858                    cb.SetToolTipString(" Check mark to fit.")
2859                    # cb.SetValue(True)
2860                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
2861
2862                    sizer.Add(cb, (iy, ix), (1, 1),
2863                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
2864
2865                    # add parameter value
2866                    ix += 1
2867                    value = self.model.getParam(item)
2868                    ctl1 = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
2869                                         style=wx.TE_PROCESS_ENTER)
2870                    ctl1.SetToolTipString(
2871                                "Hit 'Enter' after typing to update the plot.")
2872                    ctl1.SetValue(format_number(value, True))
2873                    sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
2874                    # text to show error sign
2875                    ix += 1
2876                    text2 = wx.StaticText(self, wx.ID_ANY, '+/-')
2877                    sizer.Add(text2, (iy, ix), (1, 1),
2878                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2879                    if not self.is_mac:
2880                        text2.Hide()
2881                    ix += 1
2882                    ctl2 = wx.TextCtrl(self, wx.ID_ANY,
2883                                       size=(_BOX_WIDTH / 1.2, 20), style=0)
2884                    sizer.Add(ctl2, (iy, ix), (1, 1),
2885                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2886                    if not self.is_mac:
2887                        ctl2.Hide()
2888
2889                    ix += 1
2890                    ctl3 = ModelTextCtrl(self, wx.ID_ANY,
2891                                         size=(_BOX_WIDTH / 1.9, 20),
2892                                         style=wx.TE_PROCESS_ENTER,
2893                                text_enter_callback=self._onparamRangeEnter)
2894                    min_bound = self.model.details[item][1]
2895                    if min_bound is not None:
2896                        ctl3.SetValue(format_number(min_bound, True))
2897
2898                    sizer.Add(ctl3, (iy, ix), (1, 1),
2899                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2900
2901                    ix += 1
2902                    ctl4 = ModelTextCtrl(self, wx.ID_ANY,
2903                                         size=(_BOX_WIDTH / 1.9, 20),
2904                                         style=wx.TE_PROCESS_ENTER,
2905                                text_enter_callback=self._onparamRangeEnter)
2906                    max_bound = self.model.details[item][2]
2907                    if max_bound is not None:
2908                        ctl4.SetValue(format_number(max_bound, True))
2909                    sizer.Add(ctl4, (iy, ix), (1, 1),
2910                              wx.EXPAND | wx.FIXED_MINSIZE, 0)
2911
2912                    ix += 1
2913                    # Units
2914                    if item in self.model.details:
2915                        units = wx.StaticText(self, wx.ID_ANY,
2916                            self.model.details[item][0], style=wx.ALIGN_LEFT)
2917                    else:
2918                        units = wx.StaticText(self, wx.ID_ANY, "",
2919                                              style=wx.ALIGN_LEFT)
2920                    sizer.Add(units, (iy, ix), (1, 1),
2921                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
2922
2923                    self.parameters.append([cb, item, ctl1,
2924                                            text2, ctl2, ctl3, ctl4, units])
2925
2926        iy += 1
2927        sizer.Add((10, 10), (iy, ix), (1, 1),
2928                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2929
2930        # type can be either Guassian or Array
2931        if len(self.model.dispersion.values()) > 0:
2932            type = self.model.dispersion.values()[0]["type"]
2933        else:
2934            type = "Gaussian"
2935
2936        iy += 1
2937        ix = 0
2938        # Add tile for orientational angle
2939        for item in keys:
2940            if item in self.model.orientation_params:
2941                orient_angle = wx.StaticText(self, wx.ID_ANY, '[For 2D only]:')
2942                mag_on_button = wx.Button(self, wx.ID_ANY, "Magnetic ON")
2943                mag_on_button.SetToolTipString("Turn Pol Beam/Mag scatt on/off")
2944                mag_on_button.Bind(wx.EVT_BUTTON, self._on_mag_on)
2945                mag_angle_help_button = wx.Button(self, wx.ID_ANY,
2946                                                  "Magnetic angles?")
2947                mag_angle_help_button.SetToolTipString("see angle definitions")
2948                mag_help_button = wx.Button(self, wx.ID_ANY, "Mag HELP")
2949                mag_help_button.SetToolTipString("Help on pol beam/mag fitting")
2950                mag_help_button.Bind(wx.EVT_BUTTON, self._on_mag_help)
2951                mag_angle_help_button.Bind(wx.EVT_BUTTON,
2952                                            self._on_mag_angle_help)
2953                sizer.Add(orient_angle, (iy, ix), (1, 1),
2954                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2955                iy += 1
2956                sizer.Add(mag_on_button, (iy, ix), (1, 1),
2957                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2958                ix += 1
2959                sizer.Add(mag_angle_help_button, (iy, ix), (1, 1),
2960                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2961                sizer.Add(mag_help_button, (iy, ix + 1), (1, 1),
2962                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
2963
2964                # handle the magnetic buttons
2965                # clean this up so that assume mag is off then turn
2966                # all buttons on IF mag has mag and has 2D
2967                if not self._has_magnetic:
2968                    mag_on_button.Show(False)
2969                elif not self.data.__class__.__name__ == "Data2D":
2970                    mag_on_button.Show(False)
2971                else:
2972                    mag_on_button.Show(True)
2973                mag_help_button.Show(False)
2974                mag_angle_help_button.Show(False)
2975                if mag_on_button.IsShown():
2976                    if self.magnetic_on:
2977                        mag_on_button.SetLabel("Magnetic OFF")
2978                        mag_help_button.Show(True)
2979                        mag_angle_help_button.Show(True)
2980                    else:
2981                        mag_on_button.SetLabel("Magnetic ON")
2982                        mag_help_button.Show(False)
2983                        mag_angle_help_button.Show(False)
2984
2985                if not self.data.__class__.__name__ == "Data2D" and \
2986                        not self.enable2D:
2987                    orient_angle.Hide()
2988                else:
2989                    orient_angle.Show(True)
2990                break
2991
2992        # For Gaussian only
2993        if type.lower() != "array":
2994            for item in self.model.orientation_params:
2995                if not self.magnetic_on:
2996                    if item in self.model.magnetic_params:
2997                        continue
2998                if item not in self.disp_list:
2999                    # prepare a spot to store min max
3000                    if item not in self.model.details:
3001                        self.model.details[item] = ["", None, None]
3002
3003                    iy += 1
3004                    ix = 0
3005                    # add parameters name with checkbox for selecting to fit
3006                    cb = wx.CheckBox(self, wx.ID_ANY, item)
3007                    cb.SetValue(CHECK_STATE)
3008                    cb.SetToolTipString("Check mark to fit")
3009                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
3010                    if self.data.__class__.__name__ == "Data2D" or \
3011                            self.enable2D:
3012                        cb.Show(True)
3013                    else:
3014                        cb.Hide()
3015                    sizer.Add(cb, (iy, ix), (1, 1),
3016                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
3017
3018                    # add parameter value
3019                    ix += 1
3020                    value = self.model.getParam(item)
3021                    ctl1 = ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
3022                                         style=wx.TE_PROCESS_ENTER)
3023                    ctl1.SetToolTipString(
3024                                "Hit 'Enter' after typing to update the plot.")
3025                    ctl1.SetValue(format_number(value, True))
3026                    if self.data.__class__.__name__ == "Data2D" or \
3027                            self.enable2D:
3028                        ctl1.Show(True)
3029                    else:
3030                        ctl1.Hide()
3031                    sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
3032                    # text to show error sign
3033                    ix += 1
3034                    text2 = wx.StaticText(self, -1, '+/-')
3035                    sizer.Add(text2, (iy, ix), (1, 1),
3036                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3037
3038                    text2.Hide()
3039                    ix += 1
3040                    ctl2 = wx.TextCtrl(self, -1,
3041                                       size=(_BOX_WIDTH / 1.2, 20), style=0)
3042                    sizer.Add(ctl2, (iy, ix), (1, 1),
3043                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3044
3045                    ctl2.Hide()
3046
3047                    ix += 1
3048                    ctl3 = ModelTextCtrl(self, -1,
3049                                         size=(_BOX_WIDTH / 1.8, 20),
3050                                         style=wx.TE_PROCESS_ENTER,
3051                                text_enter_callback=self._onparamRangeEnter)
3052
3053                    sizer.Add(ctl3, (iy, ix), (1, 1),
3054                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3055                    ctl3.Hide()
3056
3057                    ix += 1
3058                    ctl4 = ModelTextCtrl(self, -1,
3059                                         size=(_BOX_WIDTH / 1.8, 20),
3060                                         style=wx.TE_PROCESS_ENTER,
3061                            text_enter_callback=self._onparamRangeEnter)
3062                    sizer.Add(ctl4, (iy, ix), (1, 1),
3063                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3064
3065                    ctl4.Hide()
3066
3067                    if self.data.__class__.__name__ == "Data2D" or \
3068                            self.enable2D:
3069                        if self.is_mac:
3070                            text2.Show(True)
3071                            ctl2.Show(True)
3072                        ctl3.Show(True)
3073                        ctl4.Show(True)
3074
3075                    ix += 1
3076                    # Units
3077                    if item in self.model.details:
3078                        units = wx.StaticText(self, -1,
3079                                              self.model.details[item][0],
3080                                              style=wx.ALIGN_LEFT)
3081                    else:
3082                        units = wx.StaticText(self, -1, "",
3083                                              style=wx.ALIGN_LEFT)
3084                    if self.data.__class__.__name__ == "Data2D" or \
3085                            self.enable2D:
3086                        units.Show(True)
3087                    else:
3088                        units.Hide()
3089
3090                    sizer.Add(units, (iy, ix), (1, 1),
3091                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
3092
3093                    self.parameters.append([cb, item, ctl1,
3094                                            text2, ctl2, ctl3, ctl4, units])
3095                    self.orientation_params.append([cb, item, ctl1,
3096                                            text2, ctl2, ctl3, ctl4, units])
3097
3098        iy += 1
3099        box_description.SetForegroundColour(wx.BLUE)
3100        # Display units text on panel
3101        for item in keys:
3102            if item in self.model.details:
3103                self.text2_4.Show()
3104        # Fill the list of fittable parameters
3105        self.get_all_checked_params()
3106        self.save_current_state_fit()
3107        boxsizer1.Add(sizer)
3108        self.sizer3.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3109        self.sizer3.Layout()
3110        self.Layout()
3111
3112    def on_right_down(self, event):
3113        """
3114        Get key stroke event
3115        """
3116        if self.data is None:
3117            return
3118        # Figuring out key combo: Cmd for copy, Alt for paste
3119        if event.AltDown() and event.ShiftDown():
3120            flag = True
3121        elif event.AltDown() or event.ShiftDown():
3122            flag = False
3123        else:
3124            return
3125        # make event free
3126        event.Skip()
3127        # messages depending on the flag
3128        if not flag:
3129            infor = 'warning'
3130            # inform msg to wx
3131            wx.PostEvent(self._manager.parent,
3132                        StatusEvent(status=msg, info=infor))
3133
3134    def _onModel2D(self, event):
3135        """
3136        toggle view of model from 1D to 2D  or 2D from 1D
3137        """
3138        if self.model_view.GetLabelText() == "Show 2D":
3139            self.model_view.SetLabel("Show 1D")
3140            self.enable2D = True
3141
3142        else:
3143            self.model_view.SetLabel("Show 2D")
3144            self.enable2D = False
3145        self.Show(False)
3146        self.create_default_data()
3147        self._manager.store_data(self.uid, data_list=[self.data])
3148
3149        self.set_model_param_sizer(self.model)
3150        self._set_sizer_dispersion()
3151        self._set_weight(is_2D=self.enable2D)
3152        self._set_smear_buttons()
3153        self.Show(True)
3154        self.SetupScrolling()
3155        self._draw_model()
3156
3157        self.state.enable2D = copy.deepcopy(self.enable2D)
3158
3159    def _set_smear_buttons(self):
3160        """
3161        Set semarer radio buttons
3162        """
3163        # more disables for 2D
3164        if self.data.__class__.__name__ == "Data2D" or \
3165                    self.enable2D:
3166            self.slit_smearer.Disable()
3167            self.pinhole_smearer.Enable(True)
3168            self.default_mask = copy.deepcopy(self.data.mask)
3169        else:
3170            self.slit_smearer.Enable(True)
3171            self.pinhole_smearer.Enable(True)
3172
3173
3174class BGTextCtrl(wx.TextCtrl):
3175    """
3176    Text control used to display outputs.
3177    No editing allowed. The background is
3178    grayed out. User can't select text.
3179    """
3180    def __init__(self, *args, **kwds):
3181        wx.TextCtrl.__init__(self, *args, **kwds)
3182        self.SetEditable(False)
3183        self.SetBackgroundColour(self.GetParent().parent.GetBackgroundColour())
3184
3185        # Bind to mouse event to avoid text highlighting
3186        # The event will be skipped once the call-back
3187        # is called.
3188        self.Bind(wx.EVT_MOUSE_EVENTS, self._click)
3189
3190    def _click(self, event):
3191        """
3192        Prevent further handling of the mouse event
3193        by not calling Skip().
3194        """
3195        pass
Note: See TracBrowser for help on using the repository browser.