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

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

test branch for SESANS class in sesans.py

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