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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249
Last change on this file since bd3a3ae was bd3a3ae, checked in by butler, 5 years ago

Fix reporting of pinhole resolution on FitPage? when taken from data

GUI now reports dQ/Q as the label suggested. Also labels were fixed to
be more clear. Finally a small bit of code cleanup was attempted. Note
that there is a serious problem with the 2D resolution part of that GUI.
This is a previously unreported problem which may be dealt with in
another ticket or may be done here depending on the effort required.

addresses #1206

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