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

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

remove references to internal _model_info attribute from sasview

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