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

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 5265420 was 5265420, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

Fix the resolution reset problem by removing the Fit button disable
step. For reasons unknown, self.btFit.Enable(False) somehow triggers
self.disable_smeared even though the buttons are of different type and
have different ids. This is a probably a bug in wx. Fixes #405.

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