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

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 03137f1 was 03137f1, checked in by krzywon, 7 years ago

Default dQ is now None. dQ from data is selected automatically if dta has dQ smearing. #844

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