source: sasview/fittingview/src/sans/perspectives/fitting/fitpage.py @ 0b477f6

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.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 0b477f6 was 0b477f6, checked in by Jae Cho <jhjcho@…>, 12 years ago

reducing warning 1

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