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

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since be36447 was be36447, checked in by krzywon, 7 years ago

Set dxl & dxw values when sending data to fitting and small cleanup of redundant code. Check if model is set before evalDistribution.

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