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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 27ba79c was 27ba79c, checked in by krzywon, 7 years ago

Simplify and fix _on_select_data method.

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