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

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 77910cf was 77910cf, checked in by krzywon, 7 years ago

Fixes #819: Category, form factor and structure factor names are now assigned to the pagestate properly. They all are saved and loaded as expected.

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