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

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 f27dd1e was f27dd1e, checked in by butler, 8 years ago

Linked new help to fitpage.py - finishes all help on a fitpage panel

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