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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 2a399ca was 66acafe, checked in by GitHub <noreply@…>, 7 years ago

Merge pull request #104 from lewisodriscoll/ticket-861

Ticket 861

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