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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since ae69c690 was 6a455cd3, checked in by krzywon, 7 years ago

Merge branch 'master' into 4_1_issues

# Conflicts:
# docs/sphinx-docs/source/conf.py
# src/sas/sascalc/dataloader/readers/cansas_reader.py
# src/sas/sasgui/guiframe/documentation_window.py
# src/sas/sasgui/perspectives/fitting/models.py

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