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

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 85130cb was 6f16e25, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

clean up wx id handling in fitting perspective

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