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

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 f668101 was f668101, checked in by jhbakker, 7 years ago

Merge branch 'master' into Jurtest

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