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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 3a22ce7 was 6c382da, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

support alternate distributions in save/load and copy/paste. Closes #669

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