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

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 a5b8253 was 4581ac9, checked in by jhbakker, 8 years ago

Experimenting with optimization to Hankel trafo

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