source: sasview/src/sas/perspectives/fitting/basepage.py @ 7cd87c2

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 7cd87c2 was 7cd87c2, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

pylint fixes and remove except followed by pass

  • Property mode set to 100644
File size: 145.9 KB
RevLine 
[0b477f6]1"""
2Base Page for fitting
3"""
[340c2b3]4import sys
5import os
[c77d859]6import wx
7import numpy
[a074145]8import time
[ef26c83]9import copy
[904168e1]10import math
[edd166b]11import string
[27b7acc]12import json
[7cd87c2]13import logging
[df7a7e3]14from collections import defaultdict
[340c2b3]15from wx.lib.scrolledpanel import ScrolledPanel
[79492222]16from sas.guiframe.panel_base import PanelBase
17from sas.guiframe.utils import format_number, check_float
18from sas.guiframe.events import PanelOnFocusEvent
19from sas.guiframe.events import StatusEvent
20from sas.guiframe.events import AppendBookmarkEvent
21from sas.guiframe.dataFitting import Data2D
22from sas.guiframe.dataFitting import Data1D
23from sas.guiframe.dataFitting import check_data_validity
24from sas.guiframe.gui_style import GUIFRAME_ID
25from sas.dataloader.data_info import Detector
26from sas.dataloader.data_info import Source
27from sas.perspectives.fitting.pagestate import PageState
28from sas.guiframe.CategoryInstaller import CategoryInstaller
[c1694f8]29from sas.guiframe.documentation_window import DocumentationWindow
30
[df7a7e3]31
[ef26c83]32(PageInfoEvent, EVT_PAGE_INFO) = wx.lib.newevent.NewEvent()
33(PreviousStateEvent, EVT_PREVIOUS_STATE) = wx.lib.newevent.NewEvent()
34(NextStateEvent, EVT_NEXT_STATE) = wx.lib.newevent.NewEvent()
[f72333f]35
[e7b1ccf]36_BOX_WIDTH = 76
[6bbeacd4]37_QMIN_DEFAULT = 0.0005
38_QMAX_DEFAULT = 0.5
[6d8053c]39_NPTS_DEFAULT = 50
[ef26c83]40#Control panel width
41if sys.platform.count("win32") > 0:
[b421b1a]42    PANEL_WIDTH = 450
[f1aa385]43    FONT_VARIANT = 0
[35c9d31]44    ON_MAC = False
[9170547]45else:
[c99a6c5]46    PANEL_WIDTH = 500
[f1aa385]47    FONT_VARIANT = 1
[35c9d31]48    ON_MAC = True
[6bbeacd4]49
50
[015d109]51class BasicPage(ScrolledPanel, PanelBase):
[c77d859]52    """
[5062bbf]53    This class provide general structure of  fitpanel page
[c77d859]54    """
[ef26c83]55    ## Internal name for the AUI manager
[6bbeacd4]56    window_name = "Fit Page"
[b787e68c]57    ## Title to appear on top of the window
[6bbeacd4]58    window_caption = "Fit Page "
[7cd87c2]59
[ef26c83]60    def __init__(self, parent, color='blue', **kwargs):
[5062bbf]61        """
62        """
[015d109]63        ScrolledPanel.__init__(self, parent, **kwargs)
[e030625]64        PanelBase.__init__(self, parent)
[340c2b3]65        self.SetupScrolling()
[ef26c83]66        #Set window's font size
[f1aa385]67        self.SetWindowVariant(variant=FONT_VARIANT)
[7cd87c2]68
[6bbeacd4]69        self.SetBackgroundColour(color)
[c77d859]70        ## parent of the page
71        self.parent = parent
[77e23a2]72        ## manager is the fitting plugin
[c77d859]73        ## owner of the page (fitting plugin)
[4a7ad5f]74        self.event_owner = None
[ef26c83]75        ## current model
[ffa69b6]76        self.model = None
[be1ec9f]77        self.m_name = None
[ecf26e1]78        self.index_model = None
[be1ec9f]79        self.panel = None
[cfc0913]80        ## data
[ffa69b6]81        self.data = None
[cc31608]82        #list of available data
83        self.data_list = []
[e575db9]84        self.mask = None
[ecf26e1]85        self.uid = wx.NewId()
[5e48acb]86        self.graph_id = None
[4225aed]87        #Q range for data set
[a6d3553]88        self.qmin_data_set = numpy.inf
[4225aed]89        self.qmax_data_set = None
[67ae937]90        self.npts_data_set = 0
[0b12abb5]91        ## Q range
[f95301b]92        self.qmin = None
93        self.qmax = None
[6bbeacd4]94        self.qmax_x = _QMAX_DEFAULT
95        self.qmin_x = _QMIN_DEFAULT
96        self.npts_x = _NPTS_DEFAULT
[0b12abb5]97        ## total number of point: float
98        self.npts = None
[be1ec9f]99        self.num_points = None
[0b12abb5]100        ## default fitengine type
[bf5e985]101        self.engine_type = 'bumps'
[0b12abb5]102        ## smear default
103        self.current_smearer = None
104        ## 2D smear accuracy default
105        self.smear2d_accuracy = 'Low'
[ef26c83]106        ## slit smear:
[0b12abb5]107        self.dxl = None
108        self.dxw = None
[ef26c83]109        ## pinhole smear
[0b12abb5]110        self.dx_min = None
111        self.dx_max = None
[7d514fc]112        ##semar attrbs
[7b7ff27]113        self.enable_smearer = None
114        self.disable_smearer = None
115        self.pinhole_smearer = None
116        self.slit_smearer = None
[7d514fc]117        ##weigth attrbs
[7b7ff27]118        self.dI_noweight = None
119        self.dI_didata = None
120        self.dI_sqrdata = None
121        self.dI_idata = None
[7d514fc]122        ##other attrbs
123        self.dq_l = None
124        self.dq_r = None
125        self.tcChi = None
126        self.disp_box = None
[fcd6c6a]127        self.model_disp = None
[7d514fc]128        self.Npts_fit = None
[f71625f]129        self.Npts_total = None
[7cd87c2]130        self.theory_qmin = None
[7d514fc]131        self.theory_qmax = None
[fcd6c6a]132        self.theory_qmin_x = None
133        self.theory_qmax_x = None
[f71625f]134        self.cb1 = None
135        self.btEditMask = None
136        self.btFit = None
[be1ec9f]137        self.sld_axes = None
138        self.multi_factor = None
[7cd87c2]139
[0b12abb5]140        self.disp_cb_dict = {}
[7cd87c2]141
[0b477f6]142        #self.state = PageState(parent=parent)
[c77d859]143        ## dictionary containing list of models
[6bbeacd4]144        self.model_list_box = {}
[7cd87c2]145
[c77d859]146        ## Data member to store the dispersion object created
147        self._disp_obj_dict = {}
[cfc0913]148        ## selected parameters to apply dispersion
[ef26c83]149        self.disp_cb_dict = {}
[997131a]150        ## smearer object
[69f9084]151        self.enable2D = False
[318b5bbb]152        self._has_magnetic = False
153        self.magnetic_on = False
[cdb042d]154        self.is_mac = ON_MAC
[940aca7]155        self.formfactorbox = None
156        self.structurebox = None
[ea5fa58]157        self.categorybox = None
[c77d859]158        ##list of model parameters. each item must have same length
159        ## each item related to a given parameters
160        ##[cb state, name, value, "+/-", error of fit, min, max , units]
[4a7ad5f]161        self.parameters = []
[fb59ed9]162        # non-fittable parameter whose value is astring
163        self.str_parameters = []
[c77d859]164        ## list of parameters to fit , must be like self.parameters
[4a7ad5f]165        self.param_toFit = []
[c77d859]166        ## list of looking like parameters but with non fittable parameters info
[4a7ad5f]167        self.fixed_param = []
[c77d859]168        ## list of looking like parameters but with  fittable parameters info
[4a7ad5f]169        self.fittable_param = []
[c77d859]170        ##list of dispersion parameters
[4a7ad5f]171        self.disp_list = []
172        self.disp_name = ""
[7cd87c2]173
[780d095]174        ## list of orientation parameters
[4a7ad5f]175        self.orientation_params = []
176        self.orientation_params_disp = []
[116e1a7]177#       Self.model should ALWAYS be None here.  It was set to none above in
178#       this long init setting.  no obvious function call in between setting
179#       and this - commenting out on 4/8/2014 by PDB.  Remove once clear
180#       it is pointless.
181#        if self.model != None:
182#            self.disp_list = self.model.getDispParamList()
[8960479]183        self.temp_multi_functional = False
[c77d859]184        ##enable model 2D draw
[4a7ad5f]185        self.enable2D = False
[c77d859]186        ## check that the fit range is correct to plot the model again
[4a7ad5f]187        self.fitrange = True
[1b69256]188        ## Create memento to save the current state
[4a7ad5f]189        self.state = PageState(parent=self.parent,
190                               model=self.model, data=self.data)
[240b9966]191        ## flag to determine if state has change
[4a7ad5f]192        self.state_change = False
[71f0373]193        ## save customized array
[4a7ad5f]194        self.values = []
195        self.weights = []
[a074145]196        ## retrieve saved state
[4a7ad5f]197        self.number_saved_state = 0
[a074145]198        ## dictionary of saved state
[ef26c83]199        self.saved_states = {}
[1b69256]200        ## Create context menu for page
201        self.popUpMenu = wx.Menu()
[7cd87c2]202
[6d91073]203        id = wx.NewId()
[ef26c83]204        self._keep = wx.MenuItem(self.popUpMenu, id, "Add bookmark",
[4a7ad5f]205                                 " Keep the panel status to recall it later")
[6d91073]206        self.popUpMenu.AppendItem(self._keep)
[2657df9]207        self._keep.Enable(False)
[20b228a0]208        self._set_bookmark_flag(False)
209        self._set_save_flag(False)
[9bc499b6]210        wx.EVT_MENU(self, id, self.on_bookmark)
[6d91073]211        self.popUpMenu.AppendSeparator()
[7cd87c2]212
[60132ef]213        ## Default locations
[ef26c83]214        self._default_save_location = os.getcwd()
[a074145]215        ## save initial state on context menu
[1b69256]216        #self.onSave(event=None)
[a074145]217        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
[7cd87c2]218
[2296316]219        # bind key event
220        self.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
[7cd87c2]221
[cfc0913]222        ## create the basic structure of the panel with empty sizer
223        self.define_page_structure()
[ef26c83]224        ## drawing Initial dispersion parameters sizer
[c77d859]225        self.set_dispers_sizer()
[7cd87c2]226
[c77d859]227        ## layout
228        self.set_layout()
[7cd87c2]229
[ecf26e1]230    def set_index_model(self, index):
231        """
232        Index related to this page
233        """
234        self.index_model = index
[7cd87c2]235
[ecf26e1]236    def create_default_data(self):
237        """
[ef26c83]238        Given the user selection, creates a 1D or 2D data
[ecf26e1]239        Only when the page is on theory mode.
240        """
[6e4c9fe]241        if not hasattr(self, "model_view"):
242            return
243        toggle_mode_on = self.model_view.IsEnabled()
244        if toggle_mode_on:
245            if self.enable2D and not check_data_validity(self.data):
246                self._create_default_2d_data()
247            else:
[cb270ad2]248                if self.pointsbox.GetValue():
249                    self._create_log_1d_data()
250                else:
251                    self._create_default_1d_data()
[7cd87c2]252
[33477fd]253            if self.model != None:
254                if not self.data.is_data:
[be1ec9f]255                    self._manager.page_finder[self.uid].set_fit_data(data=\
256                                                                [self.data])
[33477fd]257            self.on_smear_helper(update=True)
[ef26c83]258            self.state.enable_smearer = self.enable_smearer.GetValue()
259            self.state.disable_smearer = self.disable_smearer.GetValue()
[33477fd]260            self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
261            self.state.slit_smearer = self.slit_smearer.GetValue()
[7cd87c2]262
[ecf26e1]263    def _create_default_1d_data(self):
264        """
[ef26c83]265        Create default data for fitting perspective
[ecf26e1]266        Only when the page is on theory mode.
267        :warning: This data is never plotted.
[7cd87c2]268
[ecf26e1]269        """
[ef26c83]270        x = numpy.linspace(start=self.qmin_x, stop=self.qmax_x,
[ecf26e1]271                           num=self.npts_x, endpoint=True)
272        self.data = Data1D(x=x)
[ef26c83]273        self.data.xaxis('\\rm{Q}', "A^{-1}")
[ecf26e1]274        self.data.yaxis('\\rm{Intensity}', "cm^{-1}")
275        self.data.is_data = False
[ef26c83]276        self.data.id = str(self.uid) + " data"
277        self.data.group_id = str(self.uid) + " Model1D"
[7cd87c2]278
[cb270ad2]279    def _create_log_1d_data(self):
280        """
281        Create log-spaced data for fitting perspective
282        Only when the page is on theory mode.
283        :warning: This data is never plotted.
[7cd87c2]284
[cb270ad2]285        """
286        if self.qmin_x >= 1.e-10:
287            qmin = numpy.log10(self.qmin_x)
288        else:
[7cd87c2]289            qmin = -10.
290
[cb270ad2]291        if self.qmax_x <= 1.e10:
292            qmax = numpy.log10(self.qmax_x)
293        else:
[7cd87c2]294            qmax = 10.
295
[cb270ad2]296        x = numpy.logspace(start=qmin, stop=qmax,
297                           num=self.npts_x, endpoint=True, base=10.0)
298        self.data = Data1D(x=x)
299        self.data.xaxis('\\rm{Q}', "A^{-1}")
300        self.data.yaxis('\\rm{Intensity}', "cm^{-1}")
301        self.data.is_data = False
302        self.data.id = str(self.uid) + " data"
303        self.data.group_id = str(self.uid) + " Model1D"
[7cd87c2]304
[ecf26e1]305    def _create_default_2d_data(self):
306        """
307        Create 2D data by default
308        Only when the page is on theory mode.
309        :warning: This data is never plotted.
310        """
311        self.data = Data2D()
[2e95da4]312        qmax = self.qmax_x / math.sqrt(2)
[ecf26e1]313        self.data.xaxis('\\rm{Q_{x}}', 'A^{-1}')
314        self.data.yaxis('\\rm{Q_{y}}', 'A^{-1}')
315        self.data.is_data = False
[ef26c83]316        self.data.id = str(self.uid) + " data"
317        self.data.group_id = str(self.uid) + " Model2D"
318        ## Default values
319        self.data.detector.append(Detector())
[ecf26e1]320        index = len(self.data.detector) - 1
[ef26c83]321        self.data.detector[index].distance = 8000   # mm
322        self.data.source.wavelength = 6             # A
[ecf26e1]323        self.data.detector[index].pixel_size.x = 5  # mm
324        self.data.detector[index].pixel_size.y = 5  # mm
325        self.data.detector[index].beam_center.x = qmax
326        self.data.detector[index].beam_center.y = qmax
327        ## create x_bins and y_bins of the model 2D
[ef26c83]328        #pixel_width_x = self.data.detector[index].pixel_size.x
329        #pixel_width_y = self.data.detector[index].pixel_size.y
330        #center_x = self.data.detector[index].beam_center.x/pixel_width_x
331        #center_y = self.data.detector[index].beam_center.y/pixel_width_y
332        # theory default: assume the beam
[ecf26e1]333        #center is located at the center of sqr detector
[ef26c83]334        xmax = qmax
[32cd80c]335        xmin = -qmax
336        ymax = qmax
337        ymin = -qmax
[ecf26e1]338        qstep = self.npts_x
[2e95da4]339
[ef26c83]340        x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)
[ecf26e1]341        y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True)
342        ## use data info instead
343        new_x = numpy.tile(x, (len(y), 1))
344        new_y = numpy.tile(y, (len(x), 1))
[ef26c83]345        new_y = new_y.swapaxes(0, 1)
[ecf26e1]346        # all data reuire now in 1d array
347        qx_data = new_x.flatten()
348        qy_data = new_y.flatten()
[ef26c83]349        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
[ecf26e1]350        # set all True (standing for unmasked) as default
351        mask = numpy.ones(len(qx_data), dtype=bool)
352        # calculate the range of qx and qy: this way,
353        # it is a little more independent
[ef26c83]354        #x_size = xmax - xmin
355        #y_size = ymax - ymin
[ecf26e1]356        # store x and y bin centers in q space
[ef26c83]357        x_bins = x
358        y_bins = y
[ecf26e1]359        # bin size: x- & y-directions
[ef26c83]360        #xstep = x_size / len(x_bins - 1)
361        #ystep = y_size / len(y_bins - 1)
[7cd87c2]362
[ecf26e1]363        self.data.source = Source()
[32cd80c]364        self.data.data = numpy.ones(len(mask))
[ecf26e1]365        self.data.err_data = numpy.ones(len(mask))
[ef26c83]366        self.data.qx_data = qx_data
367        self.data.qy_data = qy_data
368        self.data.q_data = q_data
369        self.data.mask = mask
370        self.data.x_bins = x_bins
371        self.data.y_bins = y_bins
[ecf26e1]372        # max and min taking account of the bin sizes
[ef26c83]373        self.data.xmin = xmin
[ecf26e1]374        self.data.xmax = xmax
[ef26c83]375        self.data.ymin = ymin
376        self.data.ymax = ymax
[2e95da4]377
[3cd5806]378    def on_set_focus(self, event):
379        """
[dafc36f]380        On Set Focus, update guimanger and menu
[3cd5806]381        """
382        if self._manager is not None:
383            wx.PostEvent(self._manager.parent, PanelOnFocusEvent(panel=self))
[58fc26c]384            self.on_tap_focus()
[7cd87c2]385
[58fc26c]386    def on_tap_focus(self):
387        """
388        Update menu1 on cliking the page tap
389        """
390        if self._manager.menu1 != None:
391            chain_menu = self._manager.menu1.FindItemById(\
[ef26c83]392                                                   self._manager.id_reset_flag)
[58fc26c]393            chain_menu.Enable(self.batch_on)
394            sim_menu = self._manager.menu1.FindItemById(self._manager.id_simfit)
[fa02d95]395            flag = self.data.is_data\
[ef26c83]396                            and (self.model != None)
[fa02d95]397            sim_menu.Enable(not self.batch_on and flag)
[be1ec9f]398            batch_menu = \
399                    self._manager.menu1.FindItemById(self._manager.id_batchfit)
[ef26c83]400            batch_menu.Enable(self.batch_on and flag)
[7cd87c2]401
[6f2c919]402    class ModelTextCtrl(wx.TextCtrl):
403        """
[5062bbf]404        Text control for model and fit parameters.
405        Binds the appropriate events for user interactions.
406        Default callback methods can be overwritten on initialization
[7cd87c2]407
[5062bbf]408        :param kill_focus_callback: callback method for EVT_KILL_FOCUS event
409        :param set_focus_callback:  callback method for EVT_SET_FOCUS event
410        :param mouse_up_callback:   callback method for EVT_LEFT_UP event
411        :param text_enter_callback: callback method for EVT_TEXT_ENTER event
[7cd87c2]412
[6f2c919]413        """
[be1ec9f]414        ## Set to True when the mouse is clicked while whole string is selected
[39b7019]415        full_selection = False
416        ## Call back for EVT_SET_FOCUS events
417        _on_set_focus_callback = None
[7cd87c2]418
419        def __init__(self, parent, id= -1,
[ef26c83]420                     value=wx.EmptyString,
421                     pos=wx.DefaultPosition,
[4a7ad5f]422                     size=wx.DefaultSize,
[ef26c83]423                     style=0,
[4a7ad5f]424                     validator=wx.DefaultValidator,
425                     name=wx.TextCtrlNameStr,
426                     kill_focus_callback=None,
427                     set_focus_callback=None,
428                     mouse_up_callback=None,
[ef26c83]429                     text_enter_callback=None):
[7cd87c2]430
[4a7ad5f]431            wx.TextCtrl.__init__(self, parent, id, value, pos,
432                                  size, style, validator, name)
[7cd87c2]433
[156a0b2]434            # Bind appropriate events
435            self._on_set_focus_callback = parent.onSetFocus \
436                      if set_focus_callback is None else set_focus_callback
437            self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
[7609f1a]438            self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus \
[ef26c83]439                      if kill_focus_callback is None else kill_focus_callback)
[6f2c919]440            self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter \
441                      if text_enter_callback is None else text_enter_callback)
[ef26c83]442            if not ON_MAC:
443                self.Bind(wx.EVT_LEFT_UP, self._highlight_text \
[35c9d31]444                          if mouse_up_callback is None else mouse_up_callback)
[7cd87c2]445
[39b7019]446        def _on_set_focus(self, event):
447            """
[5062bbf]448            Catch when the text control is set in focus to highlight the whole
449            text if necessary
[7cd87c2]450
[5062bbf]451            :param event: mouse event
[7cd87c2]452
[39b7019]453            """
454            event.Skip()
455            self.full_selection = True
456            return self._on_set_focus_callback(event)
[7cd87c2]457
[39b7019]458        def _highlight_text(self, event):
459            """
[5062bbf]460            Highlight text of a TextCtrl only of no text has be selected
[7cd87c2]461
[5062bbf]462            :param event: mouse event
[7cd87c2]463
[39b7019]464            """
465            # Make sure the mouse event is available to other listeners
466            event.Skip()
[ef26c83]467            control = event.GetEventObject()
[39b7019]468            if self.full_selection:
469                self.full_selection = False
470                # Check that we have a TextCtrl
471                if issubclass(control.__class__, wx.TextCtrl):
[ef26c83]472                    # Check whether text has been selected,
[39b7019]473                    # if not, select the whole string
474                    (start, end) = control.GetSelection()
[ef26c83]475                    if start == end:
476                        control.SetSelection(-1, -1)
[7cd87c2]477
[ef26c83]478        def _silent_kill_focus(self, event):
[7609f1a]479            """
[3c44c66]480            Save the state of the page
[7609f1a]481            """
[7cd87c2]482
[7609f1a]483            event.Skip()
[be1ec9f]484            #pass
[7cd87c2]485
[ffa69b6]486    def set_page_info(self, page_info):
487        """
[5062bbf]488        set some page important information at once
[ffa69b6]489        """
[116e1a7]490#       THIS METHOD/FUNCTION NO LONGE APPEARS TO BE CALLED.  Started up program
491#       and started new fit window and PR and Invariant and a fit in fitting
492#       but never entered this routine which should be an initialization
493#       routine.  Leave for a while but probably something to clean up at
494#       some point?
495#
496#       PDB April 13 2014
497#
[ef26c83]498        ##window_name
[ffa69b6]499        self.window_name = page_info.window_name
500        ##window_caption
501        self.window_caption = page_info.window_caption
502        ## manager is the fitting plugin
[ef26c83]503        self._manager = page_info.manager
[ffa69b6]504        ## owner of the page (fitting plugin)
[ef26c83]505        self.event_owner = page_info.event_owner
506        ## current model
[ffa69b6]507        self.model = page_info.model
508        ## data
509        self.data = page_info.data
510        ## dictionary containing list of models
511        self.model_list_box = page_info.model_list_box
512        ## Data member to store the dispersion object created
[eddb6ec]513        self.populate_box(model_dict=self.model_list_box)
[7cd87c2]514
[ef26c83]515    def onContextMenu(self, event):
[a074145]516        """
[5062bbf]517        Retrieve the state selected state
[a074145]518        """
[6d1235b]519        # Skipping the save state functionality for release 0.9.0
[dad49a0]520        #return
[7cd87c2]521
[a074145]522        pos = event.GetPosition()
523        pos = self.ScreenToClient(pos)
[7cd87c2]524
[ef26c83]525        self.PopupMenu(self.popUpMenu, pos)
[7cd87c2]526
[1b69256]527    def onUndo(self, event):
528        """
[5062bbf]529        Cancel the previous action
[1b69256]530        """
[ef26c83]531        event = PreviousStateEvent(page=self)
[330573d]532        wx.PostEvent(self.parent, event)
[7cd87c2]533
[1b69256]534    def onRedo(self, event):
535        """
[ef26c83]536        Restore the previous action cancelled
[1b69256]537        """
[ef26c83]538        event = NextStateEvent(page=self)
[330573d]539        wx.PostEvent(self.parent, event)
[7cd87c2]540
[c77d859]541    def define_page_structure(self):
542        """
[5062bbf]543        Create empty sizer for a panel
[c77d859]544        """
[ef26c83]545        self.vbox = wx.BoxSizer(wx.VERTICAL)
[c77d859]546        self.sizer0 = wx.BoxSizer(wx.VERTICAL)
547        self.sizer1 = wx.BoxSizer(wx.VERTICAL)
548        self.sizer2 = wx.BoxSizer(wx.VERTICAL)
549        self.sizer3 = wx.BoxSizer(wx.VERTICAL)
550        self.sizer4 = wx.BoxSizer(wx.VERTICAL)
551        self.sizer5 = wx.BoxSizer(wx.VERTICAL)
[0a518e4c]552        self.sizer6 = wx.BoxSizer(wx.VERTICAL)
[7cd87c2]553
[ef26c83]554        self.sizer0.SetMinSize((PANEL_WIDTH, -1))
555        self.sizer1.SetMinSize((PANEL_WIDTH, -1))
556        self.sizer2.SetMinSize((PANEL_WIDTH, -1))
557        self.sizer3.SetMinSize((PANEL_WIDTH, -1))
558        self.sizer4.SetMinSize((PANEL_WIDTH, -1))
559        self.sizer5.SetMinSize((PANEL_WIDTH, -1))
560        self.sizer6.SetMinSize((PANEL_WIDTH, -1))
[7cd87c2]561
[c77d859]562        self.vbox.Add(self.sizer0)
563        self.vbox.Add(self.sizer1)
564        self.vbox.Add(self.sizer2)
565        self.vbox.Add(self.sizer3)
566        self.vbox.Add(self.sizer4)
567        self.vbox.Add(self.sizer5)
[0b12abb5]568        self.vbox.Add(self.sizer6)
[7cd87c2]569
[c77d859]570    def set_layout(self):
571        """
[5062bbf]572        layout
[c77d859]573        """
574        self.vbox.Layout()
[ef26c83]575        self.vbox.Fit(self)
[c77d859]576        self.SetSizer(self.vbox)
577        self.Centre()
[7cd87c2]578
[ef26c83]579    def set_owner(self, owner):
580        """
[5062bbf]581        set owner of fitpage
[7cd87c2]582
[5062bbf]583        :param owner: the class responsible of plotting
[7cd87c2]584
[c77d859]585        """
[ef26c83]586        self.event_owner = owner
[cfc0913]587        self.state.event_owner = owner
[7cd87c2]588
[c8deee5]589    def get_state(self):
590        """
591        """
592        return self.state
[7cd87c2]593
[2f189dc]594    def get_data(self):
595        """
[ef26c83]596        return the current data
[2f189dc]597        """
[ef26c83]598        return self.data
[7cd87c2]599
[cc31608]600    def get_data_list(self):
601        """
[ef26c83]602        return the current data
[cc31608]603        """
[ef26c83]604        return self.data_list
[7cd87c2]605
[c77d859]606    def set_manager(self, manager):
607        """
[5062bbf]608        set panel manager
[7cd87c2]609
[5062bbf]610        :param manager: instance of plugin fitting
[7cd87c2]611
[c77d859]612        """
[ef26c83]613        self._manager = manager
[cfc0913]614        self.state.manager = manager
[7cd87c2]615
[eddb6ec]616    def populate_box(self, model_dict):
[c77d859]617        """
[5062bbf]618        Store list of model
[7cd87c2]619
[eddb6ec]620        :param model_dict: dictionary containing list of models
[7cd87c2]621
[c77d859]622        """
[eddb6ec]623        self.model_list_box = model_dict
[ea5fa58]624        self.state.model_list_box = self.model_list_box
625        self.initialize_combox()
[7cd87c2]626
[df7a7e3]627    def set_model_dictionary(self, model_dict):
628        """
629        Store a dictionary linking model name -> model object
630
631        :param model_dict: dictionary containing list of models
632        """
633        self.model_dict = model_dict
634
[ef26c83]635    def initialize_combox(self):
[ffa69b6]636        """
[ef26c83]637        put default value in the combobox
[7cd87c2]638        """
[ffa69b6]639        ## fill combox box
640        if self.model_list_box is None:
641            return
[ea5fa58]642        if len(self.model_list_box) > 0:
[116e1a7]643        ## This is obsolete code since form factor box is no longer static.
644        ## It is now set dynamically through _show_combox and _show_combos_helper
645        ## These are called for first time by formfactor_combo_init
646        ## itself called from fitpanel only.  If we find that I'm wrong and
647        ## we DO need to initialize somehow here - do it by a call to
648        ## formfactor_combo_init
649        ## self.formfator_combo_init()
650        ## BUT NOT HERE -- make it last line of this
651        ## method so that structure box is populated before _show_comboox_helper
652        ## is called.  Otherwise wx will complain mightily:-)
653        ##
654        ## Also change the name to initiatlize_structurebox along with changes
655        ## to other combobox methods (_populate_listbox --> _populate_categorybox
656        ## etc )
657        ##
658        ##     PDB 4/26/2014
659#            self._populate_box(self.formfactorbox,
660#                               self.model_list_box["Shapes"])
[4a7ad5f]661            self._populate_box(self.structurebox,
[ffa69b6]662                                self.model_list_box["Structure Factors"])
[4a7ad5f]663            self.structurebox.Insert("None", 0, None)
[ffa69b6]664            self.structurebox.SetSelection(0)
665            self.structurebox.Hide()
666            self.text2.Hide()
667            self.structurebox.Disable()
668            self.text2.Disable()
[7cd87c2]669
[ffa69b6]670            if self.model.__class__ in self.model_list_box["P(Q)*S(Q)"]:
671                self.structurebox.Show()
672                self.text2.Show()
673                self.structurebox.Enable()
[ef26c83]674                self.text2.Enable()
[116e1a7]675
[7cd87c2]676
[c77d859]677    def set_dispers_sizer(self):
678        """
[5062bbf]679        fill sizer containing dispersity info
[c77d859]680        """
681        self.sizer4.Clear(True)
[ef26c83]682        name = "Polydispersity and Orientational Distribution"
683        box_description = wx.StaticBox(self, -1, name)
[de5036c]684        box_description.SetForegroundColour(wx.BLUE)
[c77d859]685        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
686        #----------------------------------------------------
[4a7ad5f]687        self.disable_disp = wx.RadioButton(self, -1, 'Off', (10, 10),
688                                            style=wx.RB_GROUP)
[fa58441]689        self.enable_disp = wx.RadioButton(self, -1, 'On', (10, 30))
[6f23daf]690        # best size for MAC and PC
691        if ON_MAC:
[ef26c83]692            size_q = (30, 20)
[6f23daf]693        else:
[ef26c83]694            size_q = (20, 15)
695        self.disp_help_bt = wx.Button(self, wx.NewId(), '?',
696                                      style=wx.BU_EXACTFIT,
[6f23daf]697                                      size=size_q)
[ef26c83]698        self.disp_help_bt.Bind(wx.EVT_BUTTON,
699                        self.on_pd_help_clicked, id=self.disp_help_bt.GetId())
700        self.disp_help_bt.SetToolTipString("Helps for Polydispersion.")
[7cd87c2]701
[4a7ad5f]702        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
703                     id=self.disable_disp.GetId())
704        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
705                   id=self.enable_disp.GetId())
[ff8f99b]706        #MAC needs SetValue
707        self.disable_disp.SetValue(True)
[c77d859]708        sizer_dispersion = wx.BoxSizer(wx.HORIZONTAL)
[ef26c83]709        sizer_dispersion.Add((20, 20))
710        name = ""  # Polydispersity and \nOrientational Distribution "
711        sizer_dispersion.Add(wx.StaticText(self, -1, name))
712        sizer_dispersion.Add(self.enable_disp)
713        sizer_dispersion.Add((20, 20))
714        sizer_dispersion.Add(self.disable_disp)
715        sizer_dispersion.Add((25, 20))
[2296316]716        sizer_dispersion.Add(self.disp_help_bt)
[7cd87c2]717
[ef26c83]718        ## fill a sizer for dispersion
719        boxsizer1.Add(sizer_dispersion, 0,
720                wx.TOP | wx.BOTTOM | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE,
721                border=5)
722        self.sizer4_4 = wx.GridBagSizer(6, 5)
[2296316]723
[ef26c83]724        boxsizer1.Add(self.sizer4_4)
[c77d859]725        #-----------------------------------------------------
[ef26c83]726        self.sizer4.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
[3b605bb]727        self.sizer4_4.Layout()
[c77d859]728        self.sizer4.Layout()
[3b605bb]729        self.Layout()
[7cd87c2]730
[3b605bb]731        self.Refresh()
[71f0373]732        ## saving the state of enable dispersity button
[ef26c83]733        self.state.enable_disp = self.enable_disp.GetValue()
734        self.state.disable_disp = self.disable_disp.GetValue()
[ce92ded]735        self.SetupScrolling()
[7cd87c2]736
[a074145]737    def onResetModel(self, event):
738        """
[5062bbf]739        Reset model state
[a074145]740        """
[20b228a0]741        menu = event.GetEventObject()
[ef26c83]742        ## post help message for the selected model
[20b228a0]743        msg = menu.GetHelpString(event.GetId())
[ef26c83]744        msg += " reloaded"
[ae84427]745        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[01642fec]746        self.Show(False)
[20b228a0]747        name = menu.GetLabel(event.GetId())
[cbf01fd]748        self._on_select_model_helper()
[93f0a862]749        if self.model != None:
750            self.m_name = self.model.name
[a074145]751        if name in self.saved_states.keys():
752            previous_state = self.saved_states[name]
[f20767b]753            ## reset state of checkbox,textcrtl  and  regular parameters value
[7cd87c2]754
[ef26c83]755            self.reset_page(previous_state)
[93f0a862]756        self.state.m_name = self.m_name
[01642fec]757        self.Show(True)
[7cd87c2]758
[aad74b3]759    def on_preview(self, event):
[2296316]760        """
761        Report the current fit results
[ef26c83]762        """
[2296316]763        # Get plot image from plotpanel
764        images, canvases = self.get_images()
765        # get the report dialog
766        self.state.report(images, canvases)
[7cd87c2]767
[ef26c83]768    def on_save(self, event):
[9bc499b6]769        """
[5062bbf]770        Save the current state into file
[ef26c83]771        """
[bb70474]772        self.save_current_state()
773        new_state = self.state.clone()
774        # Ask the user the location of the file to write to.
775        path = None
[ef26c83]776        if self.parent != None:
[baf1ef3]777            self._default_save_location = \
[ae84427]778                        self._manager.parent._default_save_location
[bb70474]779        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location,
[baf1ef3]780                                        self.window_caption, "*.fitv", wx.SAVE)
781
[bb70474]782        if dlg.ShowModal() == wx.ID_OK:
783            path = dlg.GetPath()
784            self._default_save_location = os.path.dirname(path)
[7cd87c2]785            self._manager.parent._default_save_location = \
[baf1ef3]786                                 self._default_save_location
[bb70474]787        else:
788            return None
[23cdeab]789        # MAC always needs the extension for saving
790        extens = ".fitv"
791        # Make sure the ext included in the file name
792        fName = os.path.splitext(path)[0] + extens
[bb70474]793        #the manager write the state into file
[23cdeab]794        self._manager.save_fit_state(filepath=fName, fitstate=new_state)
[ef26c83]795        return new_state
[7cd87c2]796
[07c8630]797    def on_copy(self, event):
798        """
799        Copy Parameter values to the clipboad
800        """
801        if event != None:
802            event.Skip()
[2d603a2]803        # It seems MAC needs wxCallAfter
[34dbaf4]804        if event.GetId() == GUIFRAME_ID.COPYEX_ID:
805            print "copy excel"
806            wx.CallAfter(self.get_copy_excel)
807        elif event.GetId() == GUIFRAME_ID.COPYLAT_ID:
808            print "copy latex"
809            wx.CallAfter(self.get_copy_latex)
810        else:
811            wx.CallAfter(self.get_copy)
812
[7cd87c2]813
[07c8630]814    def on_paste(self, event):
815        """
816        Paste Parameter values to the panel if possible
817        """
[2a66329]818        #if event != None:
819        #    event.Skip()
[ef26c83]820        # It seems MAC needs wxCallAfter for the setvalues
821        # for multiple textctrl items, otherwise it tends to crash once a while
[6694604]822        wx.CallAfter(self.get_paste)
[07c8630]823        # messages depending on the flag
[977a965]824        #self._copy_info(True)
[7cd87c2]825
[07c8630]826    def _copy_info(self, flag):
827        """
828        Send event dpemding on flag
[7cd87c2]829
[07c8630]830        : Param flag: flag that distinguish event
831        """
832        # messages depending on the flag
833        if flag == None:
834            msg = " Parameter values are copied to the clipboard..."
835            infor = 'warning'
836        elif flag:
[6e653582]837            msg = " Parameter values are pasted from the clipboard..."
[07c8630]838            infor = "warning"
839        else:
[6e653582]840            msg = "Error occurred: "
[6fe5100]841            msg += "No valid parameter values to paste from the clipboard..."
[6e653582]842            infor = "warning"
[07c8630]843        # inform msg to wx
[ae84427]844        wx.PostEvent(self._manager.parent,
[ef26c83]845                    StatusEvent(status=msg, info=infor))
[7cd87c2]846
[20b228a0]847    def _get_time_stamp(self):
848        """
849        return time and date stings
850        """
[ef26c83]851        # date and time
[be1ec9f]852        year, month, day, hour, minute, second, _, _, _ = time.localtime()
[ef26c83]853        current_time = str(hour) + ":" + str(minute) + ":" + str(second)
854        current_date = str(month) + "/" + str(day) + "/" + str(year)
[20b228a0]855        return current_time, current_date
[7cd87c2]856
[9bc499b6]857    def on_bookmark(self, event):
[00c3aac]858        """
[5062bbf]859        save history of the data and model
[00c3aac]860        """
[ef26c83]861        if self.model == None:
862            msg = "Can not bookmark; Please select Data and Model first..."
[4470b10]863            wx.MessageBox(msg, 'Info')
[ef26c83]864            return
[0b12abb5]865        self.save_current_state()
866        new_state = self.state.clone()
867        ##Add model state on context menu
868        self.number_saved_state += 1
[20b228a0]869        current_time, current_date = self._get_time_stamp()
[ef26c83]870        #name= self.model.name+"[%g]"%self.number_saved_state
871        name = "Fitting: %g]" % self.number_saved_state
872        name += self.model.__class__.__name__
[20b228a0]873        name += "bookmarked at %s on %s" % (current_time, current_date)
[ef26c83]874        self.saved_states[name] = new_state
[7cd87c2]875
[0b12abb5]876        ## Add item in the context menu
[ef26c83]877        msg = "Model saved at %s on %s" % (current_time, current_date)
878        ## post help message for the selected model
879        msg += " Saved! right click on this page to retrieve this model"
[ae84427]880        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[7cd87c2]881
[1258aa3]882        id = wx.NewId()
[ef26c83]883        self.popUpMenu.Append(id, name, str(msg))
[1258aa3]884        wx.EVT_MENU(self, id, self.onResetModel)
[ae84427]885        wx.PostEvent(self._manager.parent,
[ef26c83]886                     AppendBookmarkEvent(title=name,
887                                         hint=str(msg),
888                                         handler=self._back_to_bookmark))
[7cd87c2]889
[ef26c83]890    def _back_to_bookmark(self, event):
[95b2bad]891        """
892        Back to bookmark
893        """
894        self._manager.on_perspective(event)
895        self.onResetModel(event)
[7fd4afd]896        self._draw_model()
[7cd87c2]897
[1328e03]898    def onSetFocus(self, evt):
[77e23a2]899        """
[ef26c83]900        highlight the current textcrtl and hide the error text control shown
[5062bbf]901        after fitting
[77e23a2]902        """
[1328e03]903        return
[7cd87c2]904
[c77d859]905    def read_file(self, path):
906        """
[5062bbf]907        Read two columns file
[7cd87c2]908
[5062bbf]909        :param path: the path to the file to read
[7cd87c2]910
[c77d859]911        """
912        try:
[ef26c83]913            if path == None:
[ae84427]914                wx.PostEvent(self._manager.parent,
[7cd87c2]915                            StatusEvent(status=\
[d49c956]916                            " Selected Distribution was not loaded: %s" % path))
[c77d859]917                return None, None
918            input_f = open(path, 'r')
919            buff = input_f.read()
920            lines = buff.split('\n')
[d49c956]921            input_f.close()
[c77d859]922            angles = []
[ef26c83]923            weights = []
[c77d859]924            for line in lines:
925                toks = line.split()
[502de0a]926                try:
927                    angle = float(toks[0])
928                    weight = float(toks[1])
[d49c956]929                    angles.append(angle)
930                    weights.append(weight)
[502de0a]931                except:
932                    # Skip non-data lines
[7cd87c2]933                    logging.error(sys.exc_value)
[c77d859]934            return numpy.array(angles), numpy.array(weights)
935        except:
[ef26c83]936            raise
[5062bbf]937
[cfc0913]938    def createMemento(self):
939        """
[5062bbf]940        return the current state of the page
[cfc0913]941        """
942        return self.state.clone()
[7cd87c2]943
[cfc0913]944    def save_current_state(self):
945        """
[5062bbf]946        Store current state
[cfc0913]947        """
[e3d1423]948        self.state.engine_type = copy.deepcopy(self.engine_type)
[0aeabc6]949        ## save model option
[ef26c83]950        if self.model != None:
951            self.disp_list = self.model.getDispParamList()
952            self.state.disp_list = copy.deepcopy(self.disp_list)
[87fbc60]953            self.state.model = self.model.clone()
[7cd87c2]954
[940aca7]955            #model combobox: complex code because of mac's silent error
956            if self.structurebox != None:
957                if self.structurebox.IsShown():
958                    self.state.structurecombobox = 'None'
959                    s_select = self.structurebox.GetSelection()
960                    if s_select > 0:
961                        self.state.structurecombobox = self.structurebox.\
962                        GetString(s_select)
[ea5fa58]963            if self.formfactorbox != None:
964                f_select = self.formfactorbox.GetSelection()
965                if f_select > 0:
[eddb6ec]966                    self.state.formfactorcombobox = self.formfactorbox.\
967                    GetString(f_select)
[ea5fa58]968        if self.categorybox != None:
[eddb6ec]969            cb_select = self.categorybox.GetSelection()
970            if cb_select > 0:
971                self.state.categorycombobox = self.categorybox.\
972                GetString(cb_select)
[7cd87c2]973
[c477b31]974        self.state.enable2D = copy.deepcopy(self.enable2D)
[ef26c83]975        self.state.values = copy.deepcopy(self.values)
976        self.state.weights = copy.deepcopy(self.weights)
977        ## save data
978        self.state.data = copy.deepcopy(self.data)
[0b12abb5]979        self.state.qmax_x = self.qmax_x
980        self.state.qmin_x = self.qmin_x
[55bb249c]981        self.state.dI_noweight = copy.deepcopy(self.dI_noweight.GetValue())
982        self.state.dI_didata = copy.deepcopy(self.dI_didata.GetValue())
983        self.state.dI_sqrdata = copy.deepcopy(self.dI_sqrdata.GetValue())
984        self.state.dI_idata = copy.deepcopy(self.dI_idata.GetValue())
[db8590a]985        self.state.dq_l = self.dq_l
986        self.state.dq_r = self.dq_r
[ef26c83]987        if hasattr(self, "enable_disp"):
988            self.state.enable_disp = self.enable_disp.GetValue()
[fc6ea43]989            self.state.disable_disp = self.disable_disp.GetValue()
[7cd87c2]990
[6e4c9fe]991        self.state.smearer = copy.deepcopy(self.current_smearer)
[ef26c83]992        if hasattr(self, "enable_smearer"):
[4a7ad5f]993            self.state.enable_smearer = \
994                                copy.deepcopy(self.enable_smearer.GetValue())
995            self.state.disable_smearer = \
996                                copy.deepcopy(self.disable_smearer.GetValue())
[7609f1a]997
[4a7ad5f]998        self.state.pinhole_smearer = \
999                                copy.deepcopy(self.pinhole_smearer.GetValue())
[db8fd5b]1000        self.state.dx_max = copy.deepcopy(self.dx_max)
1001        self.state.dx_min = copy.deepcopy(self.dx_min)
1002        self.state.dxl = copy.deepcopy(self.dxl)
1003        self.state.dxw = copy.deepcopy(self.dxw)
[ef26c83]1004        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())
[7cd87c2]1005
[ef26c83]1006        if len(self._disp_obj_dict) > 0:
1007            for k, v in self._disp_obj_dict.iteritems():
1008                self.state._disp_obj_dict[k] = v
1009
[71f0373]1010            self.state.values = copy.deepcopy(self.values)
1011            self.state.weights = copy.deepcopy(self.weights)
[0aeabc6]1012        ## save plotting range
[b787e68c]1013        self._save_plotting_range()
[7cd87c2]1014
[2296316]1015        self.state.orientation_params = []
1016        self.state.orientation_params_disp = []
1017        self.state.parameters = []
1018        self.state.fittable_param = []
1019        self.state.fixed_param = []
1020        self.state.str_parameters = []
[f20767b]1021
[cfc0913]1022        ## save checkbutton state and txtcrtl values
[ef26c83]1023        self._copy_parameters_state(self.str_parameters,
[2296316]1024                                    self.state.str_parameters)
[fc6ea43]1025        self._copy_parameters_state(self.orientation_params,
1026                                     self.state.orientation_params)
1027        self._copy_parameters_state(self.orientation_params_disp,
1028                                     self.state.orientation_params_disp)
[7cd87c2]1029
[d2d0cfdf]1030        self._copy_parameters_state(self.parameters, self.state.parameters)
[4a7ad5f]1031        self._copy_parameters_state(self.fittable_param,
1032                                     self.state.fittable_param)
[d2d0cfdf]1033        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
[3c44c66]1034        #save chisqr
1035        self.state.tcChi = self.tcChi.GetValue()
[7cd87c2]1036
[52cac46]1037    def save_current_state_fit(self):
1038        """
[5062bbf]1039        Store current state for fit_page
[52cac46]1040        """
1041        ## save model option
[ef26c83]1042        if self.model != None:
1043            self.disp_list = self.model.getDispParamList()
1044            self.state.disp_list = copy.deepcopy(self.disp_list)
[52cac46]1045            self.state.model = self.model.clone()
[9bc499b6]1046        if hasattr(self, "engine_type"):
1047            self.state.engine_type = copy.deepcopy(self.engine_type)
[7cd87c2]1048
[52cac46]1049        self.state.enable2D = copy.deepcopy(self.enable2D)
[ef26c83]1050        self.state.values = copy.deepcopy(self.values)
1051        self.state.weights = copy.deepcopy(self.weights)
1052        ## save data
1053        self.state.data = copy.deepcopy(self.data)
[7cd87c2]1054
[ef26c83]1055        if hasattr(self, "enable_disp"):
1056            self.state.enable_disp = self.enable_disp.GetValue()
[52cac46]1057            self.state.disable_disp = self.disable_disp.GetValue()
[7cd87c2]1058
[6e4c9fe]1059        self.state.smearer = copy.deepcopy(self.current_smearer)
[ef26c83]1060        if hasattr(self, "enable_smearer"):
[4a7ad5f]1061            self.state.enable_smearer = \
1062                                copy.deepcopy(self.enable_smearer.GetValue())
1063            self.state.disable_smearer = \
1064                                copy.deepcopy(self.disable_smearer.GetValue())
[7cd87c2]1065
[4a7ad5f]1066        self.state.pinhole_smearer = \
1067                                copy.deepcopy(self.pinhole_smearer.GetValue())
[ef26c83]1068        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())
[55bb249c]1069        self.state.dI_noweight = copy.deepcopy(self.dI_noweight.GetValue())
1070        self.state.dI_didata = copy.deepcopy(self.dI_didata.GetValue())
1071        self.state.dI_sqrdata = copy.deepcopy(self.dI_sqrdata.GetValue())
[ef26c83]1072        self.state.dI_idata = copy.deepcopy(self.dI_idata.GetValue())
[7d514fc]1073        if hasattr(self, "disp_box") and self.disp_box != None:
[52cac46]1074            self.state.disp_box = self.disp_box.GetCurrentSelection()
1075
[4a7ad5f]1076            if len(self.disp_cb_dict) > 0:
1077                for k, v in self.disp_cb_dict.iteritems():
[ef26c83]1078                    if v == None:
[4a7ad5f]1079                        self.state.disp_cb_dict[k] = v
[52cac46]1080                    else:
1081                        try:
[4a7ad5f]1082                            self.state.disp_cb_dict[k] = v.GetValue()
[52cac46]1083                        except:
[4a7ad5f]1084                            self.state.disp_cb_dict[k] = None
1085            if len(self._disp_obj_dict) > 0:
[ef26c83]1086                for k, v in self._disp_obj_dict.iteritems():
[4a7ad5f]1087                    self.state._disp_obj_dict[k] = v
[7cd87c2]1088
[52cac46]1089            self.state.values = copy.deepcopy(self.values)
1090            self.state.weights = copy.deepcopy(self.weights)
[7cd87c2]1091
[52cac46]1092        ## save plotting range
1093        self._save_plotting_range()
[7cd87c2]1094
[52cac46]1095        ## save checkbutton state and txtcrtl values
1096        self._copy_parameters_state(self.orientation_params,
1097                                     self.state.orientation_params)
1098        self._copy_parameters_state(self.orientation_params_disp,
1099                                     self.state.orientation_params_disp)
1100        self._copy_parameters_state(self.parameters, self.state.parameters)
[4a7ad5f]1101        self._copy_parameters_state(self.fittable_param,
1102                                             self.state.fittable_param)
[52cac46]1103        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
[7cd87c2]1104
[ef26c83]1105    def check_invalid_panel(self):
[ffa69b6]1106        """
[5062bbf]1107        check if the user can already perform some action with this panel
[ef26c83]1108        """
[4470b10]1109        if self.data is None:
1110            self.disable_smearer.SetValue(True)
1111            self.disable_disp.SetValue(True)
1112            msg = "Please load Data and select Model to start..."
[ffa69b6]1113            wx.MessageBox(msg, 'Info')
1114            return  True
[7cd87c2]1115
[e88ebfd]1116    def set_model_state(self, state):
1117        """
1118        reset page given a model state
1119        """
1120        self.disp_cb_dict = state.disp_cb_dict
1121        self.disp_list = state.disp_list
[7cd87c2]1122
[ea5fa58]1123        ## set the state of the radio box
1124        #self.shape_rbutton.SetValue(state.shape_rbutton)
1125        #self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
1126        #self.struct_rbutton.SetValue(state.struct_rbutton)
1127        #self.plugin_rbutton.SetValue(state.plugin_rbutton)
[7cd87c2]1128
[ea5fa58]1129        ## fill model combobox
[e88ebfd]1130        self._show_combox_helper()
[ea5fa58]1131        #select the current model
1132        try:
1133            # to support older version
1134            category_pos = int(state.categorycombobox)
1135        except:
1136            category_pos = 0
1137            for ind_cat in range(self.categorybox.GetCount()):
1138                if self.categorycombobox.GetString(ind_form) == \
1139                                        state.categorycombobox:
1140                    category_pos = int(ind_cat)
1141                    break
[7cd87c2]1142
[ea5fa58]1143        self.categorybox.Select(category_pos)
1144        try:
1145            # to support older version
1146            formfactor_pos = int(state.formfactorcombobox)
1147        except:
1148            formfactor_pos = 0
1149            for ind_form in range(self.formfactorbox.GetCount()):
1150                if self.formfactorbox.GetString(ind_form) == \
1151                                        state.formfactorcombobox:
1152                    formfactor_pos = int(ind_form)
1153                    break
[7cd87c2]1154
[ea5fa58]1155        self.formfactorbox.Select(formfactor_pos)
[7cd87c2]1156
[d40038e]1157        try:
1158            # to support older version
1159            structfactor_pos = int(state.structurecombobox)
1160        except:
1161            structfactor_pos = 0
1162            for ind_struct in range(self.structurebox.GetCount()):
[cce182ae]1163                if self.structurebox.GetString(ind_struct) == \
1164                                        state.structurecombobox:
[d40038e]1165                    structfactor_pos = int(ind_struct)
1166                    break
[7cd87c2]1167
[d40038e]1168        self.structurebox.SetSelection(structfactor_pos)
[7cd87c2]1169
[e88ebfd]1170        if state.multi_factor != None:
1171            self.multifactorbox.SetSelection(state.multi_factor)
[7cd87c2]1172
[ef26c83]1173        ## reset state of checkbox,textcrtl  and  regular parameters value
[e88ebfd]1174        self._reset_parameters_state(self.orientation_params_disp,
1175                                     state.orientation_params_disp)
1176        self._reset_parameters_state(self.orientation_params,
1177                                     state.orientation_params)
1178        self._reset_parameters_state(self.str_parameters,
1179                                     state.str_parameters)
[ef26c83]1180        self._reset_parameters_state(self.parameters, state.parameters)
1181        ## display dispersion info layer
[e88ebfd]1182        self.enable_disp.SetValue(state.enable_disp)
1183        self.disable_disp.SetValue(state.disable_disp)
[7cd87c2]1184
[7d514fc]1185        if hasattr(self, "disp_box") and self.disp_box != None:
[ef26c83]1186            self.disp_box.SetSelection(state.disp_box)
1187            n = self.disp_box.GetCurrentSelection()
1188            dispersity = self.disp_box.GetClientData(n)
1189            name = dispersity.__name__
[e88ebfd]1190
1191            self._set_dipers_Param(event=None)
[7cd87c2]1192
[e88ebfd]1193            if name == "ArrayDispersion":
[7cd87c2]1194
[e88ebfd]1195                for item in self.disp_cb_dict.keys():
[7cd87c2]1196
[ef26c83]1197                    if hasattr(self.disp_cb_dict[item], "SetValue"):
[e88ebfd]1198                        self.disp_cb_dict[item].SetValue(\
1199                                                    state.disp_cb_dict[item])
1200                        # Create the dispersion objects
[79492222]1201                        from sas.models.dispersion_models import ArrayDispersion
[e88ebfd]1202                        disp_model = ArrayDispersion()
[ef26c83]1203                        if hasattr(state, "values") and \
[e88ebfd]1204                                 self.disp_cb_dict[item].GetValue() == True:
[ef26c83]1205                            if len(state.values) > 0:
1206                                self.values = state.values
1207                                self.weights = state.weights
[e88ebfd]1208                                disp_model.set_weights(self.values,
[ef26c83]1209                                                       state.weights)
[e88ebfd]1210                            else:
1211                                self._reset_dispersity()
[7cd87c2]1212
[e88ebfd]1213                        self._disp_obj_dict[item] = disp_model
[ef26c83]1214                        # Set the new model as the dispersion object
[e88ebfd]1215                        #for the selected parameter
1216                        self.model.set_dispersion(item, disp_model)
[7cd87c2]1217
[e88ebfd]1218                        self.model._persistency_dict[item] = \
1219                                                [state.values, state.weights]
[7cd87c2]1220
[e88ebfd]1221            else:
1222                keys = self.model.getParamList()
1223                for item in keys:
1224                    if item in self.disp_list and \
[ef26c83]1225                        not item in self.model.details:
[e88ebfd]1226                        self.model.details[item] = ["", None, None]
[be1ec9f]1227                self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1228                self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
[ef26c83]1229        ## smearing info  restore
[e88ebfd]1230        if hasattr(self, "enable_smearer"):
[ef26c83]1231            ## set smearing value whether or not the data
[e88ebfd]1232            #contain the smearing info
1233            self.enable_smearer.SetValue(state.enable_smearer)
1234            self.disable_smearer.SetValue(state.disable_smearer)
[ef26c83]1235            self.onSmear(event=None)
[e88ebfd]1236        self.pinhole_smearer.SetValue(state.pinhole_smearer)
1237        self.slit_smearer.SetValue(state.slit_smearer)
[7cd87c2]1238
[55bb249c]1239        self.dI_noweight.SetValue(state.dI_noweight)
1240        self.dI_didata.SetValue(state.dI_didata)
1241        self.dI_sqrdata.SetValue(state.dI_sqrdata)
1242        self.dI_idata.SetValue(state.dI_idata)
[7cd87c2]1243
[e88ebfd]1244        ## we have two more options for smearing
[ef26c83]1245        if self.pinhole_smearer.GetValue():
1246            self.onPinholeSmear(event=None)
1247        elif self.slit_smearer.GetValue():
1248            self.onSlitSmear(event=None)
[7cd87c2]1249
[e88ebfd]1250        ## reset state of checkbox,textcrtl  and dispersity parameters value
[ef26c83]1251        self._reset_parameters_state(self.fittable_param, state.fittable_param)
1252        self._reset_parameters_state(self.fixed_param, state.fixed_param)
[7cd87c2]1253
[e88ebfd]1254        ## draw the model with previous parameters value
1255        self._onparamEnter_helper()
[ef26c83]1256        self.select_param(event=None)
[e88ebfd]1257        #Save state_fit
1258        self.save_current_state_fit()
1259        self._lay_out()
1260        self.Refresh()
[7cd87c2]1261
[240b9966]1262    def reset_page_helper(self, state):
[cfc0913]1263        """
[5062bbf]1264        Use page_state and change the state of existing page
[7cd87c2]1265
[5062bbf]1266        :precondition: the page is already drawn or created
[7cd87c2]1267
[5062bbf]1268        :postcondition: the state of the underlying data change as well as the
[71f0373]1269            state of the graphic interface
[cfc0913]1270        """
[4a7ad5f]1271        if state == None:
[ef26c83]1272            return
[2296316]1273        # set data, etc. from the state
[2657df9]1274        # reset page between theory and fitting from bookmarking
1275        #if state.data == None:
1276        #    data = None
1277        #else:
1278        data = state.data
1279
1280        if data == None:
1281            data_min = state.qmin
1282            data_max = state.qmax
1283            self.qmin_x = data_min
1284            self.qmax_x = data_max
1285            self.qmin.SetValue(str(data_min))
1286            self.qmax.SetValue(str(data_max))
1287
1288            self.state.data = data
1289            self.state.qmin = self.qmin_x
1290            self.state.qmax = self.qmax_x
1291        else:
1292            self.set_data(data)
[7cd87c2]1293
[ef26c83]1294        self.enable2D = state.enable2D
[318b5bbb]1295        try:
1296            self.magnetic_on = state.magnetic_on
1297        except:
1298            # Backward compatibility (for older state files)
1299            self.magnetic_on = False
[0b12abb5]1300        self.engine_type = state.engine_type
1301
1302        self.disp_cb_dict = state.disp_cb_dict
1303        self.disp_list = state.disp_list
[7cd87c2]1304
[ea5fa58]1305        ## set the state of the radio box
1306        #self.shape_rbutton.SetValue(state.shape_rbutton)
1307        #self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
1308        #self.struct_rbutton.SetValue(state.struct_rbutton)
1309        #self.plugin_rbutton.SetValue(state.plugin_rbutton)
[7cd87c2]1310
[0b12abb5]1311        ## fill model combobox
1312        self._show_combox_helper()
1313        #select the current model
[ea5fa58]1314        try:
1315            # to support older version
1316            category_pos = int(state.categorycombobox)
1317        except:
1318            category_pos = 0
1319            for ind_cat in range(self.categorybox.GetCount()):
1320                if self.categorybox.GetString(ind_cat) == \
1321                                        state.categorycombobox:
1322                    category_pos = int(ind_cat)
1323                    break
[7cd87c2]1324
[ea5fa58]1325        self.categorybox.Select(category_pos)
1326        self._show_combox(None)
1327        try:
1328            # to support older version
1329            formfactor_pos = int(state.formfactorcombobox)
1330        except:
1331            formfactor_pos = 0
1332            for ind_form in range(self.formfactorbox.GetCount()):
1333                if self.formfactorbox.GetString(ind_form) == \
1334                                                    (state.formfactorcombobox):
1335                    formfactor_pos = int(ind_form)
1336                    break
[7cd87c2]1337
[ea5fa58]1338        self.formfactorbox.Select(formfactor_pos)
[7cd87c2]1339
[d40038e]1340        try:
1341            # to support older version
1342            structfactor_pos = int(state.structurecombobox)
1343        except:
1344            structfactor_pos = 0
1345            for ind_struct in range(self.structurebox.GetCount()):
[be1ec9f]1346                if self.structurebox.GetString(ind_struct) == \
1347                                                    (state.structurecombobox):
[d40038e]1348                    structfactor_pos = int(ind_struct)
1349                    break
[7cd87c2]1350
[d40038e]1351        self.structurebox.SetSelection(structfactor_pos)
1352
[4523b68]1353        if state.multi_factor != None:
[cf6a192]1354            self.multifactorbox.SetSelection(state.multi_factor)
[4523b68]1355
[0b12abb5]1356        #reset the fitting engine type
1357        self.engine_type = state.engine_type
[ef26c83]1358        #draw the pnael according to the new model parameter
[0b12abb5]1359        self._on_select_model(event=None)
[7cd87c2]1360
[2657df9]1361        # take care of 2D button
1362        if data == None and self.model_view.IsEnabled():
1363            if self.enable2D:
[6371c9c]1364                self.model_view.SetLabel("2D Mode")
[2657df9]1365            else:
[6371c9c]1366                self.model_view.SetLabel("1D Mode")
[2657df9]1367        # else:
[7cd87c2]1368
[ef26c83]1369        if self._manager != None and self.engine_type != None:
[3cd5806]1370            self._manager._on_change_engine(engine=self.engine_type)
[0b12abb5]1371        ## set the select all check box to the a given state
1372        self.cb1.SetValue(state.cb1)
[7cd87c2]1373
[0b12abb5]1374        ## reset state of checkbox,textcrtl  and  regular parameters value
1375        self._reset_parameters_state(self.orientation_params_disp,
1376                                     state.orientation_params_disp)
1377        self._reset_parameters_state(self.orientation_params,
1378                                     state.orientation_params)
[fb59ed9]1379        self._reset_parameters_state(self.str_parameters,
1380                                     state.str_parameters)
[ef26c83]1381        self._reset_parameters_state(self.parameters, state.parameters)
1382        ## display dispersion info layer
[0b12abb5]1383        self.enable_disp.SetValue(state.enable_disp)
1384        self.disable_disp.SetValue(state.disable_disp)
[2296316]1385        # If the polydispersion is ON
1386        if state.enable_disp:
1387            # reset dispersion according the state
[0b12abb5]1388            self._set_dipers_Param(event=None)
[2296316]1389            self._reset_page_disp_helper(state)
[ef26c83]1390        ##plotting range restore
[0b12abb5]1391        self._reset_plotting_range(state)
1392        ## smearing info  restore
1393        if hasattr(self, "enable_smearer"):
[ef26c83]1394            ## set smearing value whether or not the data
[4a7ad5f]1395            #contain the smearing info
[0b12abb5]1396            self.enable_smearer.SetValue(state.enable_smearer)
1397            self.disable_smearer.SetValue(state.disable_smearer)
[ef26c83]1398            self.onSmear(event=None)
[0b12abb5]1399        self.pinhole_smearer.SetValue(state.pinhole_smearer)
1400        self.slit_smearer.SetValue(state.slit_smearer)
[ea8283d]1401        try:
1402            self.dI_noweight.SetValue(state.dI_noweight)
1403            self.dI_didata.SetValue(state.dI_didata)
1404            self.dI_sqrdata.SetValue(state.dI_sqrdata)
1405            self.dI_idata.SetValue(state.dI_idata)
1406        except:
1407            # to support older state file formats
1408            self.dI_noweight.SetValue(False)
1409            self.dI_didata.SetValue(True)
1410            self.dI_sqrdata.SetValue(False)
1411            self.dI_idata.SetValue(False)
[7cd87c2]1412
[0b12abb5]1413        ## we have two more options for smearing
[ef26c83]1414        if self.pinhole_smearer.GetValue():
[db8fd5b]1415            self.dx_min = state.dx_min
1416            self.dx_max = state.dx_max
1417            if self.dx_min != None:
1418                self.smear_pinhole_min.SetValue(str(self.dx_min))
1419            if self.dx_max != None:
1420                self.smear_pinhole_max.SetValue(str(self.dx_max))
[ef26c83]1421            self.onPinholeSmear(event=None)
1422        elif self.slit_smearer.GetValue():
[db8fd5b]1423            self.dxl = state.dxl
1424            self.dxw = state.dxw
1425            if self.dxl != None:
1426                self.smear_slit_height.SetValue(str(self.dxl))
1427            if self.dxw != None:
[7cd87c2]1428                self.smear_slit_width.SetValue(str(self.dxw))
[0d795bf]1429            else:
[7cd87c2]1430                self.smear_slit_width.SetValue('')
[ef26c83]1431            self.onSlitSmear(event=None)
[7cd87c2]1432
[0b12abb5]1433        ## reset state of checkbox,textcrtl  and dispersity parameters value
[ef26c83]1434        self._reset_parameters_state(self.fittable_param, state.fittable_param)
1435        self._reset_parameters_state(self.fixed_param, state.fixed_param)
[7cd87c2]1436
[0b12abb5]1437        ## draw the model with previous parameters value
1438        self._onparamEnter_helper()
[3c44c66]1439        #reset the value of chisqr when not consistent with the value computed
1440        self.tcChi.SetValue(str(self.state.tcChi))
[0b12abb5]1441        ## reset context menu items
1442        self._reset_context_menu()
[7cd87c2]1443
[0b12abb5]1444        ## set the value of the current state to the state given as parameter
[ef26c83]1445        self.state = state.clone()
[93f0a862]1446        self.state.m_name = self.m_name
[7cd87c2]1447
[2296316]1448    def _reset_page_disp_helper(self, state):
1449        """
1450        Help to rest page for dispersions
1451        """
1452        keys = self.model.getParamList()
1453        for item in keys:
1454            if item in self.disp_list and \
[ef26c83]1455                not item in self.model.details:
[2296316]1456                self.model.details[item] = ["", None, None]
1457        #for k,v in self.state.disp_cb_dict.iteritems():
[ef26c83]1458        self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
[2296316]1459        self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1460        self.values = copy.deepcopy(state.values)
1461        self.weights = copy.deepcopy(state.weights)
[7cd87c2]1462
[2296316]1463        for key, disp in state._disp_obj_dict.iteritems():
1464            # From saved file, disp_model can not be sent in model obj.
1465            # it will be sent as a string here, then converted to model object.
1466            if disp.__class__.__name__ == 'str':
[480fe4f]1467                disp_model = None
[79492222]1468                com_str = "from sas.models.dispersion_models "
[480fe4f]1469                com_str += "import %s as disp_func \ndisp_model = disp_func()"
[2296316]1470                exec com_str % disp
[f20767b]1471            else:
[2296316]1472                disp_model = disp
[ef26c83]1473            self._disp_obj_dict[key] = disp_model
[2296316]1474            param_name = key.split('.')[0]
1475            # Try to set dispersion only when available
[ef26c83]1476            # for eg., pass the orient. angles for 1D Cal
[2296316]1477            try:
1478                self.model.set_dispersion(param_name, disp_model)
1479                self.model._persistency_dict[key] = \
1480                                 [state.values, state.weights]
1481            except:
[7cd87c2]1482                logging.error(sys.exc_value)
[2296316]1483            selection = self._find_polyfunc_selection(disp_model)
1484            for list in self.fittable_param:
1485                if list[1] == key and list[7] != None:
1486                    list[7].SetSelection(selection)
[ef26c83]1487                    # For the array disp_model, set the values and weights
[2296316]1488                    if selection == 1:
[ef26c83]1489                        disp_model.set_weights(self.values[key],
1490                                               self.weights[key])
[2296316]1491                        try:
1492                            # Diables all fittable params for array
1493                            list[0].SetValue(False)
1494                            list[0].Disable()
1495                            list[2].Disable()
1496                            list[5].Disable()
1497                            list[6].Disable()
1498                        except:
[7cd87c2]1499                            logging.error(sys.exc_value)
[2296316]1500            # For array, disable all fixed params
1501            if selection == 1:
1502                for item in self.fixed_param:
1503                    if item[1].split(".")[0] == key.split(".")[0]:
1504                        # try it and pass it for the orientation for 1D
1505                        try:
1506                            item[2].Disable()
1507                        except:
[7cd87c2]1508                            logging.error(sys.exc_value)
1509
[2296316]1510        # Make sure the check box updated when all checked
1511        if self.cb1.GetValue():
[ef26c83]1512            self.select_all_param(None)
[7cd87c2]1513
[c77d859]1514    def _selectDlg(self):
1515        """
[7cd87c2]1516        open a dialog file to selected the customized dispersity
[c77d859]1517        """
[ef26c83]1518        if self.parent != None:
[baf1ef3]1519            self._default_save_location = \
[ae84427]1520                        self._manager.parent.get_save_location()
[60132ef]1521        dlg = wx.FileDialog(self, "Choose a weight file",
[ef26c83]1522                                self._default_save_location, "",
[4a7ad5f]1523                                "*.*", wx.OPEN)
[c77d859]1524        path = None
1525        if dlg.ShowModal() == wx.ID_OK:
1526            path = dlg.GetPath()
1527        dlg.Destroy()
1528        return path
[5062bbf]1529
[a074145]1530    def _reset_context_menu(self):
1531        """
[5062bbf]1532        reset the context menu
[a074145]1533        """
[be1ec9f]1534        for name, _ in self.state.saved_states.iteritems():
[a074145]1535            self.number_saved_state += 1
1536            ## Add item in the context menu
1537            id = wx.NewId()
[4a7ad5f]1538            msg = 'Save model and state %g' % self.number_saved_state
1539            self.popUpMenu.Append(id, name, msg)
[a074145]1540            wx.EVT_MENU(self, id, self.onResetModel)
[7cd87c2]1541
[0aeabc6]1542    def _reset_plotting_range(self, state):
[cfc0913]1543        """
[5062bbf]1544        Reset the plotting range to a given state
[cfc0913]1545        """
[f95301b]1546        self.qmin.SetValue(str(state.qmin))
[ef26c83]1547        self.qmax.SetValue(str(state.qmax))
[0b12abb5]1548
[240b9966]1549    def _save_typeOfmodel(self):
1550        """
[5062bbf]1551        save radiobutton containing the type model that can be selected
[240b9966]1552        """
[ea5fa58]1553        #self.state.shape_rbutton = self.shape_rbutton.GetValue()
1554        #self.state.shape_indep_rbutton = self.shape_indep_rbutton.GetValue()
1555        #self.state.struct_rbutton = self.struct_rbutton.GetValue()
1556        #self.state.plugin_rbutton = self.plugin_rbutton.GetValue()
[940aca7]1557        self.state.structurecombobox = self.structurebox.GetLabel()
[ea5fa58]1558        self.state.formfactorcombobox = self.formfactorbox.GetLabel()
1559        self.state.categorycombobox = self.categorybox.GetLabel()
[7cd87c2]1560
[240b9966]1561        ## post state to fit panel
[ef26c83]1562        event = PageInfoEvent(page=self)
[240b9966]1563        wx.PostEvent(self.parent, event)
[7cd87c2]1564
[ef26c83]1565    def _save_plotting_range(self):
[cfc0913]1566        """
[ef26c83]1567        save the state of plotting range
[cfc0913]1568        """
[b787e68c]1569        self.state.qmin = self.qmin_x
[ef26c83]1570        self.state.qmax = self.qmax_x
[6bbeacd4]1571        self.state.npts = self.npts_x
[7cd87c2]1572
[c77d859]1573    def _onparamEnter_helper(self):
1574        """
[ef26c83]1575        check if values entered by the user are changed and valid to replot
[5062bbf]1576        model
[c77d859]1577        """
[ef26c83]1578        # Flag to register when a parameter has changed.
[7975f2b]1579        is_modified = False
[4a7ad5f]1580        self.fitrange = True
[51a71a3]1581        is_2Ddata = False
[3a37fe0]1582        #self._undo.Enable(True)
[51a71a3]1583        # check if 2d data
[4a7ad5f]1584        if self.data.__class__.__name__ == "Data2D":
[51a71a3]1585            is_2Ddata = True
[ef26c83]1586        if self.model != None:
[edd166b]1587            try:
[4a7ad5f]1588                is_modified = self._check_value_enter(self.fittable_param,
1589                                                     is_modified)
1590                is_modified = self._check_value_enter(self.fixed_param,
1591                                                      is_modified)
1592                is_modified = self._check_value_enter(self.parameters,
[ef26c83]1593                                                      is_modified)
[edd166b]1594            except:
[7cd87c2]1595                logging.error(sys.exc_value)
[c99a6c5]1596
[c77d859]1597            # Here we should check whether the boundaries have been modified.
[ef26c83]1598            # If qmin and qmax have been modified, update qmin and qmax and
[c77d859]1599            # set the is_modified flag to True
[f95301b]1600            if self._validate_qrange(self.qmin, self.qmax):
1601                tempmin = float(self.qmin.GetValue())
[7975f2b]1602                if tempmin != self.qmin_x:
1603                    self.qmin_x = tempmin
[c77d859]1604                    is_modified = True
[7975f2b]1605                tempmax = float(self.qmax.GetValue())
1606                if tempmax != self.qmax_x:
1607                    self.qmax_x = tempmax
[c77d859]1608                    is_modified = True
[7cd87c2]1609
[51a71a3]1610                if is_2Ddata:
[ef26c83]1611                    # set mask
[51a71a3]1612                    is_modified = self._validate_Npts()
[7cd87c2]1613
[51a71a3]1614            else:
[ef26c83]1615                self.fitrange = False
[7cd87c2]1616
[d2c4c06]1617            if not self.data.is_data:
1618                is_modified = True
1619
[c77d859]1620            ## if any value is modify draw model with new value
[51a71a3]1621            if not self.fitrange:
[247cb58]1622                #self.btFit.Disable()
[ef26c83]1623                if is_2Ddata:
1624                    self.btEditMask.Disable()
[51a71a3]1625            else:
[ef26c83]1626                if is_2Ddata and self.data.is_data and not self.batch_on:
[054f004]1627                    self.btEditMask.Enable(True)
[51a71a3]1628            if is_modified and self.fitrange:
[ecf26e1]1629                # Theory case: need to get npts value to draw
1630                self.npts_x = float(self.Npts_total.GetValue())
1631                self.create_default_data()
[ef26c83]1632                self.state_change = True
1633                self._draw_model()
[7975f2b]1634                self.Refresh()
1635        return is_modified
[7cd87c2]1636
[35c9d31]1637    def _update_paramv_on_fit(self):
1638        """
[5062bbf]1639        make sure that update param values just before the fitting
[35c9d31]1640        """
1641        #flag for qmin qmax check values
[7609f1a]1642        flag = True
[51a71a3]1643        self.fitrange = True
[35c9d31]1644        is_modified = False
[51a71a3]1645
[66ff250]1646        #wx.PostEvent(self._manager.parent, StatusEvent(status=" \
1647        #updating ... ",type="update"))
[4fbc93e]1648
[35c9d31]1649        ##So make sure that update param values on_Fit.
1650        #self._undo.Enable(True)
[ef26c83]1651        if self.model != None:
[33477fd]1652            if self.Npts_total.GetValue() != self.Npts_fit.GetValue():
1653                if not self.data.is_data:
1654                    self._manager.page_finder[self.uid].set_fit_data(data=\
[ef26c83]1655                                                                [self.data])
[35c9d31]1656            ##Check the values
[ef26c83]1657            self._check_value_enter(self.fittable_param, is_modified)
1658            self._check_value_enter(self.fixed_param, is_modified)
1659            self._check_value_enter(self.parameters, is_modified)
[35c9d31]1660
1661            # If qmin and qmax have been modified, update qmin and qmax and
[ef26c83]1662            # Here we should check whether the boundaries have been modified.
[7975f2b]1663            # If qmin and qmax have been modified, update qmin and qmax and
1664            # set the is_modified flag to True
[f95301b]1665            self.fitrange = self._validate_qrange(self.qmin, self.qmax)
[51a71a3]1666            if self.fitrange:
[f95301b]1667                tempmin = float(self.qmin.GetValue())
[7975f2b]1668                if tempmin != self.qmin_x:
1669                    self.qmin_x = tempmin
1670                tempmax = float(self.qmax.GetValue())
1671                if tempmax != self.qmax_x:
1672                    self.qmax_x = tempmax
[7609f1a]1673                if tempmax == tempmin:
[ef26c83]1674                    flag = False
[7609f1a]1675                temp_smearer = None
1676                if not self.disable_smearer.GetValue():
[ef26c83]1677                    temp_smearer = self.current_smearer
[7609f1a]1678                    if self.slit_smearer.GetValue():
1679                        flag = self.update_slit_smear()
1680                    elif self.pinhole_smearer.GetValue():
1681                        flag = self.update_pinhole_smear()
1682                    else:
[6bbeacd4]1683                        self._manager.set_smearer(smearer=temp_smearer,
[66ff250]1684                                                  uid=self.uid,
[ecf26e1]1685                                                  fid=self.data.id,
[ef26c83]1686                                                  qmin=float(self.qmin_x),
1687                                                  qmax=float(self.qmax_x),
[ecf26e1]1688                            enable_smearer=not self.disable_smearer.GetValue(),
[6bbeacd4]1689                                                      draw=False)
[4fbc93e]1690                elif not self._is_2D():
[3cd5806]1691                    self._manager.set_smearer(smearer=temp_smearer,
[4a7ad5f]1692                                              qmin=float(self.qmin_x),
[ef26c83]1693                                              uid=self.uid,
[ecf26e1]1694                                              fid=self.data.id,
[ef26c83]1695                                              qmax=float(self.qmax_x),
[ecf26e1]1696                            enable_smearer=not self.disable_smearer.GetValue(),
[18e430a]1697                                                 draw=False)
[0cf97c5]1698                    if self.data != None:
[ef26c83]1699                        index_data = ((self.qmin_x <= self.data.x) & \
[0cf97c5]1700                                      (self.data.x <= self.qmax_x))
[ef26c83]1701                        val = str(len(self.data.x[index_data == True]))
[0cf97c5]1702                        self.Npts_fit.SetValue(val)
1703                    else:
1704                        # No data in the panel
1705                        try:
1706                            self.npts_x = float(self.Npts_total.GetValue())
1707                        except:
1708                            flag = False
1709                            return flag
[51a71a3]1710                    flag = True
[4fbc93e]1711                if self._is_2D():
[ef26c83]1712                    # only 2D case set mask
[51a71a3]1713                    flag = self._validate_Npts()
1714                    if not flag:
1715                        return flag
[ef26c83]1716            else:
1717                flag = False
1718        else:
[7975f2b]1719            flag = False
[51a71a3]1720
[ef26c83]1721        #For invalid q range, disable the mask editor and fit button, vs.
[51a71a3]1722        if not self.fitrange:
[55a3257]1723            if self._is_2D():
1724                self.btEditMask.Disable()
[51a71a3]1725        else:
[33477fd]1726            if self._is_2D() and  self.data.is_data and not self.batch_on:
[55a3257]1727                self.btEditMask.Enable(True)
[51a71a3]1728
[7609f1a]1729        if not flag:
[4a7ad5f]1730            msg = "Cannot Plot or Fit :Must select a "
1731            msg += " model or Fitting range is not valid!!!  "
[ae84427]1732            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[7cd87c2]1733
[33477fd]1734        try:
1735            self.save_current_state()
1736        except:
[7cd87c2]1737            logging.error(sys.exc_value)
1738
[ef26c83]1739        return flag
[7cd87c2]1740
[c99a6c5]1741    def _is_modified(self, is_modified):
[acd0bda3]1742        """
[5062bbf]1743        return to self._is_modified
[acd0bda3]1744        """
[c99a6c5]1745        return is_modified
[7cd87c2]1746
[4a7ad5f]1747    def _reset_parameters_state(self, listtorestore, statelist):
[cfc0913]1748        """
[5062bbf]1749        Reset the parameters at the given state
[cfc0913]1750        """
[4a7ad5f]1751        if len(statelist) == 0 or len(listtorestore) == 0:
[6999659]1752            return
[ef26c83]1753        if len(statelist) != len(listtorestore):
[cfc0913]1754            return
[c99a6c5]1755
[cfc0913]1756        for j in range(len(listtorestore)):
1757            item_page = listtorestore[j]
1758            item_page_info = statelist[j]
1759            ##change the state of the check box for simple parameters
[ef26c83]1760            if item_page[0] != None:
[cfc0913]1761                item_page[0].SetValue(item_page_info[0])
[ef26c83]1762            if item_page[2] != None:
[cfc0913]1763                item_page[2].SetValue(item_page_info[2])
[2296316]1764                if item_page[2].__class__.__name__ == "ComboBox":
[ef26c83]1765                    if item_page_info[2] in self.model.fun_list:
1766                        fun_val = self.model.fun_list[item_page_info[2]]
1767                        self.model.setParam(item_page_info[1], fun_val)
1768            if item_page[3] != None:
[cfc0913]1769                ## show or hide text +/-
1770                if item_page_info[2]:
1771                    item_page[3].Show(True)
1772                else:
1773                    item_page[3].Hide()
[ef26c83]1774            if item_page[4] != None:
[cfc0913]1775                ## show of hide the text crtl for fitting error
1776                if item_page_info[4][0]:
1777                    item_page[4].Show(True)
1778                    item_page[4].SetValue(item_page_info[4][1])
1779                else:
1780                    item_page[3].Hide()
[ef26c83]1781            if item_page[5] != None:
[cfc0913]1782                ## show of hide the text crtl for fitting error
[2296316]1783                item_page[5].Show(item_page_info[5][0])
1784                item_page[5].SetValue(item_page_info[5][1])
[7cd87c2]1785
[ef26c83]1786            if item_page[6] != None:
[cfc0913]1787                ## show of hide the text crtl for fitting error
[2296316]1788                item_page[6].Show(item_page_info[6][0])
1789                item_page[6].SetValue(item_page_info[6][1])
[7cd87c2]1790
[2296316]1791    def _reset_strparam_state(self, listtorestore, statelist):
1792        """
1793        Reset the string parameters at the given state
1794        """
1795        if len(statelist) == 0:
1796            return
1797
1798        listtorestore = copy.deepcopy(statelist)
[7cd87c2]1799
[2296316]1800        for j in range(len(listtorestore)):
1801            item_page = listtorestore[j]
1802            item_page_info = statelist[j]
1803            ##change the state of the check box for simple parameters
[7cd87c2]1804
[ef26c83]1805            if item_page[0] != None:
[2296316]1806                item_page[0].SetValue(format_number(item_page_info[0], True))
1807
1808            if item_page[2] != None:
1809                param_name = item_page_info[1]
1810                value = item_page_info[2]
1811                selection = value
[ef26c83]1812                if value in self.model.fun_list:
[2296316]1813                    selection = self.model.fun_list[value]
1814                item_page[2].SetValue(selection)
1815                self.model.setParam(param_name, selection)
[7cd87c2]1816
[cfc0913]1817    def _copy_parameters_state(self, listtocopy, statelist):
1818        """
[ef26c83]1819        copy the state of button
[7cd87c2]1820
[5062bbf]1821        :param listtocopy: the list of check button to copy
1822        :param statelist: list of state object to store the current state
[7cd87c2]1823
[cfc0913]1824        """
[ef26c83]1825        if len(listtocopy) == 0:
[cfc0913]1826            return
[7cd87c2]1827
[cfc0913]1828        for item in listtocopy:
[7cd87c2]1829
[cfc0913]1830            checkbox_state = None
[ef26c83]1831            if item[0] != None:
1832                checkbox_state = item[0].GetValue()
[cfc0913]1833            parameter_name = item[1]
1834            parameter_value = None
[ef26c83]1835            if item[2] != None:
[cfc0913]1836                parameter_value = item[2].GetValue()
1837            static_text = None
[ef26c83]1838            if item[3] != None:
[cfc0913]1839                static_text = item[3].IsShown()
1840            error_value = None
1841            error_state = None
[ef26c83]1842            if item[4] != None:
[cfc0913]1843                error_value = item[4].GetValue()
1844                error_state = item[4].IsShown()
[7cd87c2]1845
[cfc0913]1846            min_value = None
1847            min_state = None
[ef26c83]1848            if item[5] != None:
[cfc0913]1849                min_value = item[5].GetValue()
1850                min_state = item[5].IsShown()
[7cd87c2]1851
[cfc0913]1852            max_value = None
1853            max_state = None
[ef26c83]1854            if item[6] != None:
[cfc0913]1855                max_value = item[6].GetValue()
1856                max_state = item[6].IsShown()
[ef26c83]1857            unit = None
1858            if item[7] != None:
[240b9966]1859                unit = item[7].GetLabel()
[7cd87c2]1860
[cfc0913]1861            statelist.append([checkbox_state, parameter_name, parameter_value,
[ef26c83]1862                              static_text, [error_state, error_value],
1863                              [min_state, min_value],
1864                              [max_state, max_value], unit])
[7cd87c2]1865
1866
[e3f6ef5]1867    def _draw_model(self, update_chisqr=True, source='model'):
[c77d859]1868        """
[5062bbf]1869        Method to draw or refresh a plotted model.
1870        The method will use the data member from the model page
1871        to build a call to the fitting perspective manager.
[7cd87c2]1872
[2296316]1873        :param chisqr: update chisqr value [bool]
[c77d859]1874        """
[7fd4afd]1875        wx.CallAfter(self._draw_model_after, update_chisqr, source)
[7cd87c2]1876
[7fd4afd]1877    def _draw_model_after(self, update_chisqr=True, source='model'):
1878        """
1879        Method to draw or refresh a plotted model.
1880        The method will use the data member from the model page
1881        to build a call to the fitting perspective manager.
[7cd87c2]1882
[7fd4afd]1883        :param chisqr: update chisqr value [bool]
1884        """
[6bbeacd4]1885        #if self.check_invalid_panel():
1886        #    return
[ef26c83]1887        if self.model != None:
1888            temp_smear = None
[fca9cbd9]1889            if hasattr(self, "enable_smearer"):
[7609f1a]1890                if not self.disable_smearer.GetValue():
[ef26c83]1891                    temp_smear = self.current_smearer
[f7ef313]1892            # compute weight for the current data
[b9a5f0e]1893            from sas.perspectives.fitting.utils import get_weight
[f7ef313]1894            flag = self.get_weight_flag()
1895            weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
[fa65e99]1896            toggle_mode_on = self.model_view.IsEnabled()
[01642fec]1897            is_2d = self._is_2D()
[ef26c83]1898            self._manager.draw_model(self.model,
[f72333f]1899                                    data=self.data,
[ef26c83]1900                                    smearer=temp_smear,
1901                                    qmin=float(self.qmin_x),
[484faf7]1902                                    qmax=float(self.qmax_x),
[66ff250]1903                                    page_id=self.uid,
[ef26c83]1904                                    toggle_mode_on=toggle_mode_on,
1905                                    state=self.state,
[01642fec]1906                                    enable2D=is_2d,
[55bb249c]1907                                    update_chisqr=update_chisqr,
[e3f6ef5]1908                                    source='model',
[55bb249c]1909                                    weight=weight)
[7cd87c2]1910
[a1b2471]1911    def _on_show_sld(self, event=None):
1912        """
1913        Plot SLD profile
1914        """
1915        # get profile data
[ef26c83]1916        x, y = self.model.getProfile()
[a1b2471]1917
[79492222]1918        from sas.plottools import Data1D as pf_data1d
1919        #from sas.perspectives.theory.profile_dialog import SLDPanel
1920        from sas.guiframe.local_perspectives.plotting.profile_dialog \
[da9ac4e6]1921        import SLDPanel
[be1ec9f]1922        sld_data = pf_data1d(x, y)
[a1b2471]1923        sld_data.name = 'SLD'
[fb59ed9]1924        sld_data.axes = self.sld_axes
[7cd87c2]1925        self.panel = SLDPanel(self, data=sld_data, axes=self.sld_axes, id= -1)
[ef26c83]1926        self.panel.ShowModal()
[7cd87c2]1927
[ef26c83]1928    def _set_multfactor_combobox(self, multiplicity=10):
[4523b68]1929        """
1930        Set comboBox for muitfactor of CoreMultiShellModel
[fb59ed9]1931        :param multiplicit: no. of multi-functionality
[4523b68]1932        """
[fb59ed9]1933        # build content of the combobox
[ef26c83]1934        for idx in range(0, multiplicity):
1935            self.multifactorbox.Append(str(idx), int(idx))
[4523b68]1936        self._hide_multfactor_combobox()
[7cd87c2]1937
[ef26c83]1938    def _show_multfactor_combobox(self):
[4523b68]1939        """
1940        Show the comboBox of muitfactor of CoreMultiShellModel
[ef26c83]1941        """
[4523b68]1942        if not self.mutifactor_text.IsShown():
1943            self.mutifactor_text.Show(True)
[fb59ed9]1944            self.mutifactor_text1.Show(True)
[4523b68]1945        if not self.multifactorbox.IsShown():
[ef26c83]1946            self.multifactorbox.Show(True)
[7cd87c2]1947
[ef26c83]1948    def _hide_multfactor_combobox(self):
[4523b68]1949        """
1950        Hide the comboBox of muitfactor of CoreMultiShellModel
[ef26c83]1951        """
[4523b68]1952        if self.mutifactor_text.IsShown():
1953            self.mutifactor_text.Hide()
[fb59ed9]1954            self.mutifactor_text1.Hide()
[4523b68]1955        if self.multifactorbox.IsShown():
[ef26c83]1956            self.multifactorbox.Hide()
[7cd87c2]1957
[ea5fa58]1958    def formfactor_combo_init(self):
1959        """
1960        First time calls _show_combox_helper
1961        """
1962        self._show_combox(None)
[7cd87c2]1963
[0b12abb5]1964    def _show_combox_helper(self):
[c77d859]1965        """
[0b12abb5]1966        Fill panel's combo box according to the type of model selected
[c77d859]1967        """
[ea5fa58]1968        custom_model = 'Customized Models'
1969        mod_cat = self.categorybox.GetStringSelection()
1970        self.structurebox.SetSelection(0)
1971        self.structurebox.Disable()
1972        self.formfactorbox.Clear()
1973        if mod_cat == None:
1974            return
1975        m_list = []
1976        try:
1977            if mod_cat == custom_model:
1978                for model in self.model_list_box[mod_cat]:
1979                    str_m = str(model).split(".")[0]
1980                    #self.model_box.Append(str_m)
1981                    m_list.append(self.model_dict[str_m])
1982            else:
[ae4c139]1983                cat_dic = self.master_category_dict[mod_cat]
1984                for (model, enabled) in cat_dic:
[ea5fa58]1985                    if enabled:
1986                        m_list.append(self.model_dict[model])
[ae4c139]1987                    #else:
1988                    #    msg = "This model is disabled by Category Manager."
1989                    #    wx.PostEvent(self.parent.parent,
1990                    #                 StatusEvent(status=msg, info="error"))
[ea5fa58]1991        except:
1992            msg = "%s\n" % (sys.exc_value)
[ae84427]1993            wx.PostEvent(self._manager.parent,
[ea5fa58]1994                         StatusEvent(status=msg, info="error"))
1995        self._populate_box(self.formfactorbox, m_list)
[7cd87c2]1996
1997    def _on_modify_cat(self, event=None):
1998        self._manager.parent.on_category_panel(event)
1999
[0b12abb5]2000    def _show_combox(self, event=None):
2001        """
2002        Show combox box associate with type of model selected
2003        """
[dbbdf11]2004        self.Show(False)
[0b12abb5]2005        self._show_combox_helper()
[77e23a2]2006        self._on_select_model(event=None)
[dbbdf11]2007        self.Show(True)
[3595309d]2008        self._save_typeOfmodel()
[3b605bb]2009        self.sizer4_4.Layout()
[9853ad0]2010        self.sizer4.Layout()
[3b605bb]2011        self.Layout()
2012        self.Refresh()
[7cd87c2]2013
[ea5fa58]2014    def _populate_box(self, combobox, list):
[c77d859]2015        """
[5062bbf]2016        fill combox box with dict item
[7cd87c2]2017
[5062bbf]2018        :param list: contains item to fill the combox
[c77d859]2019            item must model class
2020        """
[f74673c]2021        mlist = []
[ea5fa58]2022        for models in list:
[ef26c83]2023            model = models()
[c77d859]2024            name = model.__class__.__name__
[ef26c83]2025            if models.__name__ != "NoStructure":
[0a518e4c]2026                if hasattr(model, "name"):
2027                    name = model.name
[ef26c83]2028                mlist.append((name, models))
[7cd87c2]2029
[f74673c]2030        # Sort the models
[8df5faa]2031        mlist_sorted = sorted(mlist)
[f74673c]2032        for item in mlist_sorted:
[ef26c83]2033            combobox.Append(item[0], item[1])
[c77d859]2034        return 0
[7cd87c2]2035
[6e9976b]2036    def _onQrangeEnter(self, event):
2037        """
[5062bbf]2038        Check validity of value enter in the Q range field
[7cd87c2]2039
[6bbeacd4]2040        """
2041        tcrtl = event.GetEventObject()
2042        #Clear msg if previously shown.
2043        msg = ""
2044        wx.PostEvent(self.parent, StatusEvent(status=msg))
2045        # Flag to register when a parameter has changed.
[be1ec9f]2046        #is_modified = False
[6bbeacd4]2047        if tcrtl.GetValue().lstrip().rstrip() != "":
2048            try:
[be1ec9f]2049                float(tcrtl.GetValue())
[6bbeacd4]2050                tcrtl.SetBackgroundColour(wx.WHITE)
2051                # If qmin and qmax have been modified, update qmin and qmax
[f95301b]2052                if self._validate_qrange(self.qmin, self.qmax):
2053                    tempmin = float(self.qmin.GetValue())
[6bbeacd4]2054                    if tempmin != self.qmin_x:
2055                        self.qmin_x = tempmin
2056                    tempmax = float(self.qmax.GetValue())
2057                    if tempmax != self.qmax_x:
2058                        self.qmax_x = tempmax
2059                else:
2060                    tcrtl.SetBackgroundColour("pink")
[6fe5100]2061                    msg = "Model Error: wrong value entered: %s" % sys.exc_value
[6bbeacd4]2062                    wx.PostEvent(self.parent, StatusEvent(status=msg))
[ef26c83]2063                    return
[6bbeacd4]2064            except:
2065                tcrtl.SetBackgroundColour("pink")
[6fe5100]2066                msg = "Model Error: wrong value entered: %s" % sys.exc_value
[6bbeacd4]2067                wx.PostEvent(self.parent, StatusEvent(status=msg))
[ef26c83]2068                return
[6bbeacd4]2069            #Check if # of points for theory model are valid(>0).
2070            if self.npts != None:
2071                if check_float(self.npts):
2072                    temp_npts = float(self.npts.GetValue())
[ef26c83]2073                    if temp_npts != self.num_points:
[6bbeacd4]2074                        self.num_points = temp_npts
[be1ec9f]2075                        #is_modified = True
[6bbeacd4]2076                else:
[6fe5100]2077                    msg = "Cannot plot: No points in Q range!!!  "
[6bbeacd4]2078                    wx.PostEvent(self.parent, StatusEvent(status=msg))
2079        else:
[ef26c83]2080            tcrtl.SetBackgroundColour("pink")
[6fe5100]2081            msg = "Model Error: wrong value entered!!!"
[ef26c83]2082            wx.PostEvent(self.parent, StatusEvent(status=msg))
[6bbeacd4]2083        self.save_current_state()
2084        event = PageInfoEvent(page=self)
2085        wx.PostEvent(self.parent, event)
2086        self.state_change = False
2087        #Draw the model for a different range
[b582d6bc]2088        if not self.data.is_data:
2089            self.create_default_data()
[6bbeacd4]2090        self._draw_model()
[7cd87c2]2091
[6bbeacd4]2092    def _theory_qrange_enter(self, event):
2093        """
2094        Check validity of value enter in the Q range field
[6e9976b]2095        """
[7cd87c2]2096
[ef26c83]2097        tcrtl = event.GetEventObject()
[6e9976b]2098        #Clear msg if previously shown.
[ef26c83]2099        msg = ""
[ae84427]2100        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[6e9976b]2101        # Flag to register when a parameter has changed.
2102        is_modified = False
[ef26c83]2103        if tcrtl.GetValue().lstrip().rstrip() != "":
[6e9976b]2104            try:
2105                value = float(tcrtl.GetValue())
2106                tcrtl.SetBackgroundColour(wx.WHITE)
2107
2108                # If qmin and qmax have been modified, update qmin and qmax
[6bbeacd4]2109                if self._validate_qrange(self.theory_qmin, self.theory_qmax):
2110                    tempmin = float(self.theory_qmin.GetValue())
2111                    if tempmin != self.theory_qmin_x:
2112                        self.theory_qmin_x = tempmin
2113                    tempmax = float(self.theory_qmax.GetValue())
[6e9976b]2114                    if tempmax != self.qmax_x:
[6bbeacd4]2115                        self.theory_qmax_x = tempmax
[6e9976b]2116                else:
2117                    tcrtl.SetBackgroundColour("pink")
[6fe5100]2118                    msg = "Model Error: wrong value entered: %s" % sys.exc_value
[ef26c83]2119                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2120                    return
[6e9976b]2121            except:
2122                tcrtl.SetBackgroundColour("pink")
[6fe5100]2123                msg = "Model Error: wrong value entered: %s" % sys.exc_value
[ef26c83]2124                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2125                return
[6e9976b]2126            #Check if # of points for theory model are valid(>0).
[ef26c83]2127            if self.Npts_total.IsEditable():
[0cf97c5]2128                if check_float(self.Npts_total):
2129                    temp_npts = float(self.Npts_total.GetValue())
[ef26c83]2130                    if temp_npts != self.num_points:
[6e9976b]2131                        self.num_points = temp_npts
2132                        is_modified = True
2133                else:
[6fe5100]2134                    msg = "Cannot Plot: No points in Q range!!!  "
[ae84427]2135                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[6e9976b]2136        else:
[ef26c83]2137            tcrtl.SetBackgroundColour("pink")
[6fe5100]2138            msg = "Model Error: wrong value entered!!!"
[ae84427]2139            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[6e9976b]2140        self.save_current_state()
[ef26c83]2141        event = PageInfoEvent(page=self)
[6e9976b]2142        wx.PostEvent(self.parent, event)
[ef26c83]2143        self.state_change = False
[484faf7]2144        #Draw the model for a different range
[ecf26e1]2145        self.create_default_data()
[484faf7]2146        self._draw_model()
[7cd87c2]2147
[ef26c83]2148    def _on_select_model_helper(self):
[c77d859]2149        """
[5062bbf]2150        call back for model selection
[c77d859]2151        """
[376916c]2152        ## reset dictionary containing reference to dispersion
2153        self._disp_obj_dict = {}
[ef26c83]2154        self.disp_cb_dict = {}
[8960479]2155        self.temp_multi_functional = False
[ea5fa58]2156        f_id = self.formfactorbox.GetCurrentSelection()
2157        #For MAC
2158        form_factor = None
2159        if f_id >= 0:
2160            form_factor = self.formfactorbox.GetClientData(f_id)
[9170547]2161
[376916c]2162        if not form_factor in  self.model_list_box["multiplication"]:
[6dc9ad8]2163            self.structurebox.Hide()
[ef26c83]2164            self.text2.Hide()
[3b605bb]2165            self.structurebox.Disable()
2166            self.structurebox.SetSelection(0)
[c097f02]2167            self.text2.Disable()
[87fbc60]2168        else:
[6dc9ad8]2169            self.structurebox.Show()
2170            self.text2.Show()
[87fbc60]2171            self.structurebox.Enable()
[c097f02]2172            self.text2.Enable()
[7cd87c2]2173
[ef26c83]2174        if form_factor != None:
2175            # set multifactor for Mutifunctional models
[be1ec9f]2176            if form_factor().__class__ in \
2177                                        self.model_list_box["Multi-Functions"]:
[4523b68]2178                m_id = self.multifactorbox.GetCurrentSelection()
[fb59ed9]2179                multiplicity = form_factor().multiplicity_info[0]
2180                self.multifactorbox.Clear()
2181                self._set_multfactor_combobox(multiplicity)
2182                self._show_multfactor_combobox()
[ef26c83]2183                #ToDo:  this info should be called directly from the model
2184                text = form_factor().multiplicity_info[1]  # 'No. of Shells: '
[fb59ed9]2185
2186                self.mutifactor_text.SetLabel(text)
[ef26c83]2187                if m_id > multiplicity - 1:
[fb59ed9]2188                    # default value
2189                    m_id = 1
[7cd87c2]2190
[4523b68]2191                self.multi_factor = self.multifactorbox.GetClientData(m_id)
[ef26c83]2192                if self.multi_factor == None:
2193                    self.multi_factor = 0
[4523b68]2194                form_factor = form_factor(int(self.multi_factor))
[fb59ed9]2195                self.multifactorbox.SetSelection(m_id)
2196                # Check len of the text1 and max_multiplicity
2197                text = ''
[be1ec9f]2198                if form_factor.multiplicity_info[0] == \
2199                                        len(form_factor.multiplicity_info[2]):
[fb59ed9]2200                    text = form_factor.multiplicity_info[2][self.multi_factor]
2201                self.mutifactor_text1.SetLabel(text)
2202                # Check if model has  get sld profile.
2203                if len(form_factor.multiplicity_info[3]) > 0:
2204                    self.sld_axes = form_factor.multiplicity_info[3]
2205                    self.show_sld_button.Show(True)
2206                else:
2207                    self.sld_axes = ""
2208
[4523b68]2209            else:
2210                self._hide_multfactor_combobox()
[a1b2471]2211                self.show_sld_button.Hide()
[4523b68]2212                form_factor = form_factor()
2213                self.multi_factor = None
2214        else:
2215            self._hide_multfactor_combobox()
[a1b2471]2216            self.show_sld_button.Hide()
[ef26c83]2217            self.multi_factor = None
[7cd87c2]2218
[3b605bb]2219        s_id = self.structurebox.GetCurrentSelection()
[ef26c83]2220        struct_factor = self.structurebox.GetClientData(s_id)
[7cd87c2]2221
[ef26c83]2222        if  struct_factor != None:
[79492222]2223            from sas.models.MultiplicationModel import MultiplicationModel
[ef26c83]2224            self.model = MultiplicationModel(form_factor, struct_factor())
[8960479]2225            # multifunctional form factor
2226            if len(form_factor.non_fittable) > 0:
2227                self.temp_multi_functional = True
[c77d859]2228        else:
[fbf4bf8]2229            if form_factor != None:
[ef26c83]2230                self.model = form_factor
[fbf4bf8]2231            else:
2232                self.model = None
2233                return self.model
[318b5bbb]2234        # check if model has magnetic parameters
2235        if len(self.model.magnetic_params) > 0:
[7cd87c2]2236            self._has_magnetic = True
[318b5bbb]2237        else:
[7cd87c2]2238            self._has_magnetic = False
[cfc0913]2239        ## post state to fit panel
[ef26c83]2240        self.state.parameters = []
2241        self.state.model = self.model
[4523b68]2242        self.state.qmin = self.qmin_x
2243        self.state.multi_factor = self.multi_factor
[ef26c83]2244        self.disp_list = self.model.getDispParamList()
[f20767b]2245        self.state.disp_list = self.disp_list
[fadf925]2246        self.on_set_focus(None)
[ef26c83]2247        self.Layout()
[7cd87c2]2248
[85b3971]2249    def _validate_qrange(self, qmin_ctrl, qmax_ctrl):
2250        """
[5062bbf]2251        Verify that the Q range controls have valid values
2252        and that Qmin < Qmax.
[7cd87c2]2253
[5062bbf]2254        :param qmin_ctrl: text control for Qmin
2255        :param qmax_ctrl: text control for Qmax
[7cd87c2]2256
[ef26c83]2257        :return: True is the Q range is value, False otherwise
[7cd87c2]2258
[85b3971]2259        """
2260        qmin_validity = check_float(qmin_ctrl)
2261        qmax_validity = check_float(qmax_ctrl)
2262        if not (qmin_validity and qmax_validity):
2263            return False
2264        else:
2265            qmin = float(qmin_ctrl.GetValue())
2266            qmax = float(qmax_ctrl.GetValue())
[e0e22f2c]2267            if qmin < qmax:
[ef26c83]2268                #Make sure to set both colours white.
[85b3971]2269                qmin_ctrl.SetBackgroundColour(wx.WHITE)
2270                qmin_ctrl.Refresh()
2271                qmax_ctrl.SetBackgroundColour(wx.WHITE)
2272                qmax_ctrl.Refresh()
2273            else:
2274                qmin_ctrl.SetBackgroundColour("pink")
2275                qmin_ctrl.Refresh()
2276                qmax_ctrl.SetBackgroundColour("pink")
2277                qmax_ctrl.Refresh()
[ef26c83]2278                msg = "Invalid Q range: Q min must be smaller than Q max"
[ae84427]2279                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[85b3971]2280                return False
2281        return True
[7cd87c2]2282
[ef26c83]2283    def _validate_Npts(self):
[51a71a3]2284        """
[5062bbf]2285        Validate the number of points for fitting is more than 10 points.
2286        If valid, setvalues Npts_fit otherwise post msg.
[51a71a3]2287        """
2288        #default flag
2289        flag = True
[1a74523]2290        # Theory
2291        if self.data == None and self.enable2D:
2292            return flag
[166f2f8]2293        for data in self.data_list:
2294            # q value from qx and qy
[ef26c83]2295            radius = numpy.sqrt(data.qx_data * data.qx_data +
2296                                data.qy_data * data.qy_data)
[166f2f8]2297            #get unmasked index
2298            index_data = (float(self.qmin.GetValue()) <= radius) & \
2299                            (radius <= float(self.qmax.GetValue()))
[ef26c83]2300            index_data = (index_data) & (data.mask)
[166f2f8]2301            index_data = (index_data) & (numpy.isfinite(data.data))
[7609f1a]2302
[166f2f8]2303            if len(index_data[index_data]) < 10:
2304                # change the color pink.
2305                self.qmin.SetBackgroundColour("pink")
2306                self.qmin.Refresh()
2307                self.qmax.SetBackgroundColour("pink")
2308                self.qmax.Refresh()
[6fe5100]2309                msg = "Data Error: "
2310                msg += "Too few points in %s." % data.name
[ae84427]2311                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[166f2f8]2312                self.fitrange = False
2313                flag = False
2314            else:
[ef26c83]2315                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
[166f2f8]2316                self.fitrange = True
[7cd87c2]2317
[51a71a3]2318        return flag
[94078a8]2319
[ef26c83]2320    def _validate_Npts_1D(self):
[94078a8]2321        """
2322        Validate the number of points for fitting is more than 5 points.
2323        If valid, setvalues Npts_fit otherwise post msg.
2324        """
2325        #default flag
2326        flag = True
2327        # Theory
2328        if self.data == None:
2329            return flag
2330        for data in self.data_list:
2331            # q value from qx and qy
[ef26c83]2332            radius = data.x
[94078a8]2333            #get unmasked index
2334            index_data = (float(self.qmin.GetValue()) <= radius) & \
2335                            (radius <= float(self.qmax.GetValue()))
2336            index_data = (index_data) & (numpy.isfinite(data.y))
2337
2338            if len(index_data[index_data]) < 5:
2339                # change the color pink.
2340                self.qmin.SetBackgroundColour("pink")
2341                self.qmin.Refresh()
2342                self.qmax.SetBackgroundColour("pink")
2343                self.qmax.Refresh()
[6fe5100]2344                msg = "Data Error: "
2345                msg += "Too few points in %s." % data.name
[ae84427]2346                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[94078a8]2347                self.fitrange = False
2348                flag = False
2349            else:
[ef26c83]2350                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
[94078a8]2351                self.fitrange = True
[7cd87c2]2352
[94078a8]2353        return flag
[7cd87c2]2354
[ea5fa58]2355    def _check_value_enter(self, list, modified):
[c77d859]2356        """
[ea5fa58]2357        :param list: model parameter and panel info
2358        :Note: each item of the list should be as follow:
[5062bbf]2359            item=[check button state, parameter's name,
[ef26c83]2360                paramater's value, string="+/-",
2361                parameter's error of fit,
2362                parameter's minimum value,
2363                parrameter's maximum value ,
[5062bbf]2364                parameter's units]
[ef26c83]2365        """
2366        is_modified = modified
[ea5fa58]2367        if len(list) == 0:
[c99a6c5]2368            return is_modified
[ea5fa58]2369        for item in list:
[edd166b]2370            #skip angle parameters for 1D
[ef26c83]2371            if not self.enable2D:
[edd166b]2372                if item in self.orientation_params:
2373                    continue
2374            #try:
2375            name = str(item[1])
[7cd87c2]2376
[ef26c83]2377            if string.find(name, ".npts") == -1 and \
2378                                        string.find(name, ".nsigmas") == -1:
2379                ## check model parameters range
2380                param_min = None
2381                param_max = None
[7cd87c2]2382
[c985bef]2383                ## check minimun value
[ef26c83]2384                if item[5] != None and item[5] != "":
2385                    if item[5].GetValue().lstrip().rstrip() != "":
[edd166b]2386                        try:
2387                            param_min = float(item[5].GetValue())
[ef26c83]2388                            if not self._validate_qrange(item[5], item[2]):
[7975f2b]2389                                if numpy.isfinite(param_min):
2390                                    item[2].SetValue(format_number(param_min))
[7cd87c2]2391
[6e9976b]2392                            item[5].SetBackgroundColour(wx.WHITE)
2393                            item[2].SetBackgroundColour(wx.WHITE)
[7cd87c2]2394
[edd166b]2395                        except:
[6fe5100]2396                            msg = "Wrong fit parameter range entered"
[ae84427]2397                            wx.PostEvent(self._manager.parent,
[ef26c83]2398                                         StatusEvent(status=msg))
[edd166b]2399                            raise ValueError, msg
[c985bef]2400                        is_modified = True
[77e23a2]2401                ## check maximum value
[ef26c83]2402                if item[6] != None and item[6] != "":
2403                    if item[6].GetValue().lstrip().rstrip() != "":
2404                        try:
[edd166b]2405                            param_max = float(item[6].GetValue())
[ef26c83]2406                            if not self._validate_qrange(item[2], item[6]):
[7975f2b]2407                                if numpy.isfinite(param_max):
[ef26c83]2408                                    item[2].SetValue(format_number(param_max))
[7cd87c2]2409
[6e9976b]2410                            item[6].SetBackgroundColour(wx.WHITE)
2411                            item[2].SetBackgroundColour(wx.WHITE)
[edd166b]2412                        except:
2413                            msg = "Wrong Fit parameter range entered "
[ae84427]2414                            wx.PostEvent(self._manager.parent,
[ef26c83]2415                                         StatusEvent(status=msg))
[edd166b]2416                            raise ValueError, msg
[c985bef]2417                        is_modified = True
[7cd87c2]2418
[ef26c83]2419                if param_min != None and param_max != None:
[85b3971]2420                    if not self._validate_qrange(item[5], item[6]):
[ef26c83]2421                        msg = "Wrong Fit range entered for parameter "
2422                        msg += "name %s of model %s " % (name, self.model.name)
[ae84427]2423                        wx.PostEvent(self._manager.parent,
[ef26c83]2424                                     StatusEvent(status=msg))
[7cd87c2]2425
[ef26c83]2426                if name in self.model.details.keys():
[be1ec9f]2427                    self.model.details[name][1:3] = param_min, param_max
2428                    is_modified = True
[edd166b]2429                else:
[be1ec9f]2430                    self.model.details[name] = ["", param_min, param_max]
2431                    is_modified = True
[ef26c83]2432            try:
[2296316]2433                # Check if the textctr is enabled
2434                if item[2].IsEnabled():
[ef26c83]2435                    value = float(item[2].GetValue())
[2296316]2436                    item[2].SetBackgroundColour("white")
2437                    # If the value of the parameter has changed,
2438                    # +update the model and set the is_modified flag
2439                    if value != self.model.getParam(name) and \
2440                                                numpy.isfinite(value):
2441                        self.model.setParam(name, value)
[6e9976b]2442            except:
[7987962]2443                item[2].SetBackgroundColour("pink")
[6e9976b]2444                msg = "Wrong Fit parameter value entered "
[ae84427]2445                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[7cd87c2]2446
[ef26c83]2447        return is_modified
[7cd87c2]2448
[c77d859]2449    def _set_dipers_Param(self, event):
2450        """
[5062bbf]2451        respond to self.enable_disp and self.disable_disp radio box.
[ef26c83]2452        The dispersity object is reset inside the model into Gaussian.
2453        When the user select yes , this method display a combo box for
2454        more selection when the user selects No,the combo box disappears.
[5062bbf]2455        Redraw the model with the default dispersity (Gaussian)
[c77d859]2456        """
[4470b10]2457        ## On selction if no model exists.
[ef26c83]2458        if self.model == None:
[4470b10]2459            self.disable_disp.SetValue(True)
[ef26c83]2460            msg = "Please select a Model first..."
[4470b10]2461            wx.MessageBox(msg, 'Info')
[ef26c83]2462            wx.PostEvent(self._manager.parent,
2463                         StatusEvent(status="Polydispersion: %s" % msg))
[4470b10]2464            return
2465
[1c1436d]2466        self._reset_dispersity()
[7cd87c2]2467
[ef26c83]2468        if self.model == None:
[70c57ebf]2469            self.model_disp.Hide()
2470            self.sizer4_4.Clear(True)
2471            return
[b421b1a]2472
[1c1436d]2473        if self.enable_disp.GetValue():
2474            ## layout for model containing no dispersity parameters
[7cd87c2]2475
[ef26c83]2476            self.disp_list = self.model.getDispParamList()
[7cd87c2]2477
[ef26c83]2478            if len(self.disp_list) == 0 and len(self.disp_cb_dict) == 0:
2479                self._layout_sizer_noDipers()
[c77d859]2480            else:
[ef26c83]2481                ## set gaussian sizer
[1c1436d]2482                self._on_select_Disp(event=None)
2483        else:
2484            self.sizer4_4.Clear(True)
[7cd87c2]2485
[ef26c83]2486        ## post state to fit panel
[1c1436d]2487        self.save_current_state()
[ef26c83]2488        if event != None:
2489            event = PageInfoEvent(page=self)
[240b9966]2490            wx.PostEvent(self.parent, event)
[1467e1a6]2491        #draw the model with the current dispersity
[f20767b]2492        self._draw_model()
[f1aa385]2493        self.sizer4_4.Layout()
2494        self.sizer5.Layout()
[71f0373]2495        self.Layout()
[ef26c83]2496        self.Refresh()
[7cd87c2]2497
[c77d859]2498    def _layout_sizer_noDipers(self):
2499        """
[5062bbf]2500        Draw a sizer with no dispersity info
[c77d859]2501        """
[ef26c83]2502        ix = 0
2503        iy = 1
2504        self.fittable_param = []
2505        self.fixed_param = []
2506        self.orientation_params_disp = []
[7cd87c2]2507
[c77d859]2508        self.sizer4_4.Clear(True)
[01b6bd0]2509        text = "No polydispersity available for this model"
2510        model_disp = wx.StaticText(self, -1, text)
[ef26c83]2511        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
2512                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[dcf29d7]2513        self.sizer4_4.Layout()
[c77d859]2514        self.sizer4.Layout()
[7cd87c2]2515
[c77d859]2516    def _reset_dispersity(self):
2517        """
[5062bbf]2518        put gaussian dispersity into current model
[c77d859]2519        """
[ef26c83]2520        if len(self.param_toFit) > 0:
[1c1436d]2521            for item in self.fittable_param:
[513115c]2522                if item in self.param_toFit:
[1c1436d]2523                    self.param_toFit.remove(item)
[edd166b]2524
[1c1436d]2525            for item in self.orientation_params_disp:
[513115c]2526                if item in self.param_toFit:
[1c1436d]2527                    self.param_toFit.remove(item)
[7cd87c2]2528
[ef26c83]2529        self.fittable_param = []
2530        self.fixed_param = []
2531        self.orientation_params_disp = []
2532        self.values = {}
2533        self.weights = {}
[7cd87c2]2534
[79492222]2535        from sas.models.dispersion_models import GaussianDispersion
[ef26c83]2536        if len(self.disp_cb_dict) == 0:
[e2f7b92]2537            self.save_current_state()
[3b605bb]2538            self.sizer4_4.Clear(True)
2539            self.Layout()
[ef26c83]2540            return
2541        if (len(self.disp_cb_dict) > 0):
[f20767b]2542            for p in self.disp_cb_dict:
[ef26c83]2543                # The parameter was un-selected.
2544                # Go back to Gaussian model (with 0 pts)
[f20767b]2545                disp_model = GaussianDispersion()
[7cd87c2]2546
[ff8f99b]2547                self._disp_obj_dict[p] = disp_model
[ef26c83]2548                # Set the new model as the dispersion object
2549                # for the selected parameter
[f20767b]2550                try:
[ef26c83]2551                    self.model.set_dispersion(p, disp_model)
[f20767b]2552                except:
[7cd87c2]2553                    logging.error(sys.exc_value)
[7975f2b]2554
[240b9966]2555        ## save state into
2556        self.save_current_state()
[ef26c83]2557        self.Layout()
[c985bef]2558        self.Refresh()
[7cd87c2]2559
[ef26c83]2560    def _on_select_Disp(self, event):
[c77d859]2561        """
[5062bbf]2562        allow selecting different dispersion
2563        self.disp_list should change type later .now only gaussian
[c77d859]2564        """
[2296316]2565        self._set_sizer_dispersion()
2566
[6e9150d]2567        ## Redraw the model
[ef26c83]2568        self._draw_model()
[3a37fe0]2569        #self._undo.Enable(True)
[ef26c83]2570        event = PageInfoEvent(page=self)
[3b9e023]2571        wx.PostEvent(self.parent, event)
[7cd87c2]2572
[7975f2b]2573        self.sizer4_4.Layout()
2574        self.sizer4.Layout()
[36cb4d2f]2575        self.SetupScrolling()
[7cd87c2]2576
[ef26c83]2577    def _on_disp_func(self, event=None):
[c77d859]2578        """
[2296316]2579        Select a distribution function for the polydispersion
[7cd87c2]2580
[ef26c83]2581        :Param event: ComboBox event
[2296316]2582        """
2583        # get ready for new event
2584        if event != None:
2585            event.Skip()
2586        # Get event object
[ef26c83]2587        disp_box = event.GetEventObject()
[2296316]2588
2589        # Try to select a Distr. function
[ef26c83]2590        try:
[2296316]2591            disp_box.SetBackgroundColour("white")
2592            selection = disp_box.GetCurrentSelection()
2593            param_name = disp_box.Name.split('.')[0]
2594            disp_name = disp_box.GetValue()
[ef26c83]2595            dispersity = disp_box.GetClientData(selection)
[7cd87c2]2596
[2296316]2597            #disp_model =  GaussianDispersion()
2598            disp_model = dispersity()
2599            # Get param names to reset the values of the param
2600            name1 = param_name + ".width"
2601            name2 = param_name + ".npts"
2602            name3 = param_name + ".nsigmas"
2603            # Check Disp. function whether or not it is 'array'
2604            if disp_name.lower() == "array":
[ef26c83]2605                value2 = ""
2606                value3 = ""
[2296316]2607                value1 = self._set_array_disp(name=name1, disp=disp_model)
2608            else:
2609                self._del_array_values(name1)
2610                #self._reset_array_disp(param_name)
2611                self._disp_obj_dict[name1] = disp_model
2612                self.model.set_dispersion(param_name, disp_model)
[ef26c83]2613                self.state._disp_obj_dict[name1] = disp_model
[7cd87c2]2614
[ef26c83]2615                value1 = str(format_number(self.model.getParam(name1), True))
2616                value2 = str(format_number(self.model.getParam(name2)))
2617                value3 = str(format_number(self.model.getParam(name3)))
[2296316]2618            # Reset fittable polydispersin parameter value
[513115c]2619            for item in self.fittable_param:
[ef26c83]2620                if item[1] == name1:
2621                    item[2].SetValue(value1)
[2296316]2622                    item[5].SetValue("")
2623                    item[6].SetValue("")
2624                    # Disable for array
2625                    if disp_name.lower() == "array":
2626                        item[0].SetValue(False)
2627                        item[0].Disable()
2628                        item[2].Disable()
[ace0e33]2629                        item[3].Show(False)
2630                        item[4].Show(False)
[2296316]2631                        item[5].Disable()
2632                        item[6].Disable()
2633                    else:
2634                        item[0].Enable()
2635                        item[2].Enable()
2636                        item[5].Enable()
[ef26c83]2637                        item[6].Enable()
[2296316]2638                    break
2639            # Reset fixed polydispersion params
2640            for item in self.fixed_param:
2641                if item[1] == name2:
[7cd87c2]2642                    item[2].SetValue(value2)
[2296316]2643                    # Disable Npts for array
2644                    if disp_name.lower() == "array":
2645                        item[2].Disable()
2646                    else:
2647                        item[2].Enable()
2648                if item[1] == name3:
[ef26c83]2649                    item[2].SetValue(value3)
[2296316]2650                    # Disable Nsigs for array
2651                    if disp_name.lower() == "array":
2652                        item[2].Disable()
2653                    else:
2654                        item[2].Enable()
[7cd87c2]2655
[2296316]2656            # Make sure the check box updated when all checked
2657            if self.cb1.GetValue():
[fc1761b]2658                #self.select_all_param(None)
2659                self.get_all_checked_params()
[2296316]2660
2661            # update params
[ef26c83]2662            self._update_paramv_on_fit()
[2296316]2663            # draw
2664            self._draw_model()
2665            self.Refresh()
2666        except:
2667            # Error msg
2668            msg = "Error occurred:"
2669            msg += " Could not select the distribution function..."
2670            msg += " Please select another distribution function."
2671            disp_box.SetBackgroundColour("pink")
2672            # Focus on Fit button so that users can see the pinky box
2673            self.btFit.SetFocus()
[ae84427]2674            wx.PostEvent(self._manager.parent,
[2296316]2675                         StatusEvent(status=msg, info="error"))
[7cd87c2]2676
[2296316]2677    def _set_array_disp(self, name=None, disp=None):
2678        """
2679        Set array dispersion
[7cd87c2]2680
[2296316]2681        :param name: name of the parameter for the dispersion to be set
2682        :param disp: the polydisperion object
2683        """
[ef26c83]2684        # The user wants this parameter to be averaged.
[2296316]2685        # Pop up the file selection dialog.
2686        path = self._selectDlg()
2687        # Array data
2688        values = []
2689        weights = []
2690        # If nothing was selected, just return
2691        if path is None:
2692            self.disp_cb_dict[name].SetValue(False)
2693            #self.noDisper_rbox.SetValue(True)
2694            return
2695        self._default_save_location = os.path.dirname(path)
[ae84427]2696        if self._manager != None:
[7cd87c2]2697            self._manager.parent._default_save_location = \
[baf1ef3]2698                             self._default_save_location
[f20767b]2699
[ef26c83]2700        basename = os.path.basename(path)
2701        values, weights = self.read_file(path)
[7cd87c2]2702
[2296316]2703        # If any of the two arrays is empty, notify the user that we won't
2704        # proceed
[ef26c83]2705        if len(self.param_toFit) > 0:
[2296316]2706            if name in self.param_toFit:
2707                self.param_toFit.remove(name)
2708
2709        # Tell the user that we are about to apply the distribution
2710        msg = "Applying loaded %s distribution: %s" % (name, path)
[ae84427]2711        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[6b39d58]2712        self._set_array_disp_model(name=name, disp=disp,
2713                                    values=values, weights=weights)
2714        return basename
[7cd87c2]2715
[ef26c83]2716    def _set_array_disp_model(self, name=None, disp=None,
[6b39d58]2717                              values=[], weights=[]):
2718        """
2719        Set array dispersion model
[7cd87c2]2720
[6b39d58]2721        :param name: name of the parameter for the dispersion to be set
2722        :param disp: the polydisperion object
2723        """
[2296316]2724        disp.set_weights(values, weights)
2725        self._disp_obj_dict[name] = disp
2726        self.model.set_dispersion(name.split('.')[0], disp)
[ef26c83]2727        self.state._disp_obj_dict[name] = disp
[2296316]2728        self.values[name] = values
2729        self.weights[name] = weights
2730        # Store the object to make it persist outside the
2731        # scope of this method
2732        #TODO: refactor model to clean this up?
2733        self.state.values = {}
2734        self.state.weights = {}
2735        self.state.values = copy.deepcopy(self.values)
2736        self.state.weights = copy.deepcopy(self.weights)
2737
[ef26c83]2738        # Set the new model as the dispersion object for the
[2296316]2739        #selected parameter
2740        #self.model.set_dispersion(p, disp_model)
[ef26c83]2741        # Store a reference to the weights in the model object
[2296316]2742        #so that
2743        # it's not lost when we use the model within another thread.
[ef26c83]2744        self.state.model = self.model.clone()
[2296316]2745        self.model._persistency_dict[name.split('.')[0]] = \
2746                                        [values, weights]
2747        self.state.model._persistency_dict[name.split('.')[0]] = \
[ef26c83]2748                                        [values, weights]
[7cd87c2]2749
[ef26c83]2750    def _del_array_values(self, name=None):
[2296316]2751        """
2752        Reset array dispersion
[7cd87c2]2753
[ef26c83]2754        :param name: name of the parameter for the dispersion to be set
[2296316]2755        """
2756        # Try to delete values and weight of the names array dic if exists
2757        try:
2758            del self.values[name]
2759            del self.weights[name]
2760            # delete all other dic
2761            del self.state.values[name]
2762            del self.state.weights[name]
[ef26c83]2763            del self.model._persistency_dict[name.split('.')[0]]
[2296316]2764            del self.state.model._persistency_dict[name.split('.')[0]]
2765        except:
[7cd87c2]2766            logging.error(sys.exc_value)
2767
[7975f2b]2768    def _lay_out(self):
2769        """
[5062bbf]2770        returns self.Layout
[7cd87c2]2771
[5062bbf]2772        :Note: Mac seems to like this better when self.
2773            Layout is called after fitting.
[7975f2b]2774        """
2775        self._sleep4sec()
2776        self.Layout()
[ef26c83]2777        return
[7cd87c2]2778
[7975f2b]2779    def _sleep4sec(self):
2780        """
[12eac73]2781            sleep for 1 sec only applied on Mac
[ef26c83]2782            Note: This 1sec helps for Mac not to crash on self.
2783            Layout after self._draw_model
[7975f2b]2784        """
2785        if ON_MAC == True:
[12eac73]2786            time.sleep(1)
[7cd87c2]2787
[ef26c83]2788    def _find_polyfunc_selection(self, disp_func=None):
[2296316]2789        """
[ef26c83]2790        FInd Comboox selection from disp_func
[7cd87c2]2791
[2296316]2792        :param disp_function: dispersion distr. function
2793        """
2794        # List of the poly_model name in the combobox
[ef26c83]2795        list = ["RectangleDispersion", "ArrayDispersion",
2796                "LogNormalDispersion", "GaussianDispersion",
2797                "SchulzDispersion"]
[2296316]2798
2799        # Find the selection
2800        try:
2801            selection = list.index(disp_func.__class__.__name__)
2802            return selection
2803        except:
[ef26c83]2804            return 3
[7cd87c2]2805
[ef26c83]2806    def on_reset_clicked(self, event):
[904168e1]2807        """
[5062bbf]2808        On 'Reset' button  for Q range clicked
[904168e1]2809        """
[7609f1a]2810        flag = True
[904168e1]2811        ##For 3 different cases: Data2D, Data1D, and theory
[06aa2eeb]2812        if self.model == None:
[ef26c83]2813            msg = "Please select a model first..."
[06aa2eeb]2814            wx.MessageBox(msg, 'Info')
2815            flag = False
2816            return
[7cd87c2]2817
[06aa2eeb]2818        elif self.data.__class__.__name__ == "Data2D":
[ef26c83]2819            data_min = 0
2820            x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax))
2821            y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
[904168e1]2822            self.qmin_x = data_min
[7cd87c2]2823            self.qmax_x = math.sqrt(x * x + y * y)
[3b70cc7]2824            #self.data.mask = numpy.ones(len(self.data.data),dtype=bool)
[f72333f]2825            # check smearing
2826            if not self.disable_smearer.GetValue():
[ef26c83]2827                ## set smearing value whether or
2828                # not the data contain the smearing info
[f72333f]2829                if self.pinhole_smearer.GetValue():
2830                    flag = self.update_pinhole_smear()
2831                else:
2832                    flag = True
[7cd87c2]2833
[06aa2eeb]2834        elif self.data == None:
2835            self.qmin_x = _QMIN_DEFAULT
2836            self.qmax_x = _QMAX_DEFAULT
[ef26c83]2837            self.num_points = _NPTS_DEFAULT
[06aa2eeb]2838            self.state.npts = self.num_points
[7cd87c2]2839
[6318298]2840        elif self.data.__class__.__name__ != "Data2D":
[904168e1]2841            self.qmin_x = min(self.data.x)
2842            self.qmax_x = max(self.data.x)
[7609f1a]2843            # check smearing
2844            if not self.disable_smearer.GetValue():
[ef26c83]2845                ## set smearing value whether or
2846                # not the data contain the smearing info
[7609f1a]2847                if self.slit_smearer.GetValue():
2848                    flag = self.update_slit_smear()
2849                elif self.pinhole_smearer.GetValue():
2850                    flag = self.update_pinhole_smear()
2851                else:
2852                    flag = True
[904168e1]2853        else:
[06aa2eeb]2854            flag = False
[7cd87c2]2855
[7609f1a]2856        if flag == False:
[ef26c83]2857            msg = "Cannot Plot :Must enter a number!!!  "
[ae84427]2858            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
[51a71a3]2859        else:
2860            # set relative text ctrs.
[f95301b]2861            self.qmin.SetValue(str(self.qmin_x))
[51a71a3]2862            self.qmax.SetValue(str(self.qmax_x))
[bf5e985]2863            self.show_npts2fit()
[ef26c83]2864            # At this point, some button and variables satatus (disabled?)
2865            # should be checked such as color that should be reset to
2866            # white in case that it was pink.
[51a71a3]2867            self._onparamEnter_helper()
2868
[7609f1a]2869        self.save_current_state()
[904168e1]2870        self.state.qmin = self.qmin_x
2871        self.state.qmax = self.qmax_x
[7cd87c2]2872
[904168e1]2873        #reset the q range values
2874        self._reset_plotting_range(self.state)
2875        self._draw_model()
[7cd87c2]2876
[cb270ad2]2877    def select_log(self, event):
2878        """
2879        Log checked to generate log spaced points for theory model
2880        """
2881
[2296316]2882    def get_images(self):
2883        """
2884        Get the images of the plots corresponding this panel for report
[7cd87c2]2885
[2296316]2886        : return graphs: list of figures
[be1ec9f]2887        : Need Move to guiframe
[2296316]2888        """
2889        # set list of graphs
2890        graphs = []
2891        canvases = []
[1c6c1b7]2892        res_item = None
[2296316]2893        # call gui_manager
[ae84427]2894        gui_manager = self._manager.parent
[2296316]2895        # loops through the panels [dic]
[be1ec9f]2896        for _, item2 in gui_manager.plot_panels.iteritems():
[ef26c83]2897            data_title = self.data.group_id
[2296316]2898            # try to get all plots belonging to this control panel
[ef26c83]2899            try:
[1c6c1b7]2900                g_id = item2.group_id
2901                if g_id == data_title or \
2902                        str(g_id).count("res" + str(self.graph_id)) or \
2903                        str(g_id).count(str(self.uid)) > 0:
2904                    if str(g_id).count("res" + str(self.graph_id)) > 0:
2905                        res_item = [item2.figure, item2.canvas]
2906                    else:
2907                        # append to the list
2908                        graphs.append(item2.figure)
[7cd87c2]2909                        canvases.append(item2.canvas)
[ef26c83]2910            except:
2911                # Not for control panels
[7cd87c2]2912                logging.error(sys.exc_value)
[1c6c1b7]2913        # Make sure the resduals plot goes to the last
2914        if res_item != None:
2915            graphs.append(res_item[0])
2916            canvases.append(res_item[1])
[2296316]2917        # return the list of graphs
2918        return graphs, canvases
[904168e1]2919
[c1694f8]2920    def on_function_help_clicked(self, event):
[7ad6ff5]2921        """
[c1694f8]2922        Function called when 'Help' button is pressed next to model
[7cd87c2]2923        of interest.  This calls DocumentationWindow from
[c1694f8]2924        documentation_window.py. It will load the top level of the model
2925        help documenation sphinx generated html if no model is presented.
2926        If a model IS present then if documention for that model exists
2927        it will load to that  point otherwise again it will go to the top.
2928        For Wx2.8 and below is used (i.e. non-released through installer)
2929        a browser is loaded and the top of the model documentation only is
2930        accessible because webbrowser module does not pass anything after
2931        the # to the browser.
[7cd87c2]2932
[c1694f8]2933        :param evt: on Help Button pressed event
[7ad6ff5]2934        """
[c1694f8]2935
[7ad6ff5]2936        if self.model == None:
[74f6c7e]2937            name = 'index.html'
[ea5fa58]2938        else:
2939            name = self.formfactorbox.GetValue()
[c1694f8]2940
[7cd87c2]2941        if self.model != None:
2942            _docspath = 'user/models/model_functions.html#' + name
[c1694f8]2943            _doc_viewer = DocumentationWindow(self, -1, _docspath, name + "Help")
[ea5fa58]2944        else:
[c1694f8]2945            _doc_viewer = DocumentationWindow(self, -1, "index.html", \
2946                                                "General Help")
2947
2948
2949    def on_model_help_clicked(self, event):
2950        """
2951        Function called when 'Description' button is pressed next to model
2952        of interest.  This calls the Description embedded in the model. This
2953        should work with either Wx2.8 and lower or higher. If no model is
2954        selected it will give the message that a model must be chosen first
2955        in the box that would normally contain the description.  If a badly
2956        behaved model is encountered which has no description then it will
2957        give the message that none is available.
[7cd87c2]2958
[c1694f8]2959        :param evt: on Description Button pressed event
2960        """
2961
2962        if self.model == None:
2963            name = 'index.html'
2964        else:
2965            name = self.formfactorbox.GetValue()
2966
2967        msg = 'Model description:\n'
2968        info = "Info"
2969        if self.model != None:
2970#                frame.Destroy()
2971            if str(self.model.description).rstrip().lstrip() == '':
2972                msg += "Sorry, no information is available for this model."
[ea5fa58]2973            else:
[c1694f8]2974                msg += self.model.description + '\n'
2975            wx.MessageBox(msg, info)
2976        else:
2977            msg += "You must select a model to get information on this"
2978            wx.MessageBox(msg, info)
[ea5fa58]2979
[7cd87c2]2980    def _on_mag_help(self, event):
[318b5bbb]2981        """
[7cd87c2]2982        Bring up Magnetic Angle definition bmp image whenever the ? button
2983        is clicked. Calls DocumentationWindow with the path of the location
2984        within the documentation tree (after /doc/ ....". When using old
[c1694f8]2985        versions of Wx (i.e. before 2.9 and therefore not part of release
[4380c03]2986        versions distributed via installer) it brings up an image viewer
[7cd87c2]2987        box which allows the user to click through the rest of the images in
[4380c03]2988        the directory.  Not ideal but probably better than alternative which
[7cd87c2]2989        would bring up the entire discussion of how magnetic models work?
[4380c03]2990        Specially since it is not likely to be accessed.  The normal release
2991        versions bring up the normal image box.
[7cd87c2]2992
[4380c03]2993        :param evt: Triggers on clicking ? in Magnetic Angles? box
[318b5bbb]2994        """
[7cd87c2]2995
[4380c03]2996        _TreeLocation = "_images/M_angles_pic.bmp"
2997        _doc_viewer = DocumentationWindow(self, -1, \
[7cd87c2]2998             _TreeLocation, "Magnetic Angle Defintions")
2999
3000    def _on_mag_on(self, event):
[318b5bbb]3001        """
3002        Magnetic Parameters ON/OFF
3003        """
3004        button = event.GetEventObject()
3005
3006        if button.GetLabel().count('ON') > 0:
3007            self.magnetic_on = True
3008            button.SetLabel("Magnetic OFF")
3009            m_value = 1.0e-06
3010            for key in self.model.magnetic_params:
3011                if key.count('M0') > 0:
3012                    self.model.setParam(key, m_value)
3013                    m_value += 0.5e-06
3014        else:
3015            self.magnetic_on = False
3016            button.SetLabel("Magnetic ON")
3017            for key in self.model.magnetic_params:
3018                if key.count('M0') > 0:
3019                    #reset mag value to zero fo safety
3020                    self.model.setParam(key, 0.0)
[7cd87c2]3021
3022        self.Show(False)
[318b5bbb]3023        self.set_model_param_sizer(self.model)
3024        #self._set_sizer_dispersion()
3025        self.state.magnetic_on = self.magnetic_on
3026        self.SetupScrolling()
3027        self.Show(True)
[7cd87c2]3028
[2296316]3029    def on_pd_help_clicked(self, event):
3030        """
[7cd87c2]3031        Bring up Polydispersity Documentation whenever the ? button is clicked.
[74f6c7e]3032        Calls DocumentationWindow with the path of the location within the
[7cd87c2]3033        documentation tree (after /doc/ ....".  Note that when using old
3034        versions of Wx (before 2.9) and thus not the release version of
3035        istallers, the help comes up at the top level of the file as
[4380c03]3036        webbrowser does not pass anything past the # to the browser when it is
3037        running "file:///...."
[7cd87c2]3038
[74f6c7e]3039        :param evt: Triggers on clicking ? in polydispersity box
[2296316]3040        """
[7cd87c2]3041
[74f6c7e]3042        _TreeLocation = "user/perspectives/fitting/fitting_help.html"
3043        _TreeLocation += "#polydispersity-distributions"
3044        _doc_viewer = DocumentationWindow(self, -1, \
[7cd87c2]3045             _TreeLocation, "Polydispersity Help")
3046
[2296316]3047    def on_left_down(self, event):
3048        """
3049        Get key stroke event
3050        """
3051        # Figuring out key combo: Cmd for copy, Alt for paste
3052        if event.CmdDown() and event.ShiftDown():
[be1ec9f]3053            self.get_paste()
[2296316]3054        elif event.CmdDown():
[be1ec9f]3055            self.get_copy()
[2296316]3056        else:
3057            event.Skip()
3058            return
3059        # make event free
3060        event.Skip()
[7cd87c2]3061
[951a2f5]3062    def get_copy(self):
3063        """
3064        Get copy params to clipboard
3065        """
[ef26c83]3066        content = self.get_copy_params()
[951a2f5]3067        flag = self.set_clipboard(content)
[ef26c83]3068        self._copy_info(flag)
3069        return flag
[7cd87c2]3070
[ef26c83]3071    def get_copy_params(self):
[2296316]3072        """
3073        Get the string copies of the param names and values in the tap
[ef26c83]3074        """
[b9a5f0e]3075        content = 'sasview_parameter_values:'
[ef26c83]3076        # Do it if params exist
3077        if  self.parameters != []:
[7cd87c2]3078
[ef26c83]3079            # go through the parameters
[be1ec9f]3080            strings = self._get_copy_helper(self.parameters,
[2296316]3081                                           self.orientation_params)
[be1ec9f]3082            content += strings
[7cd87c2]3083
[2296316]3084            # go through the fittables
[be1ec9f]3085            strings = self._get_copy_helper(self.fittable_param,
[2296316]3086                                           self.orientation_params_disp)
[be1ec9f]3087            content += strings
[2296316]3088
3089            # go through the fixed params
[be1ec9f]3090            strings = self._get_copy_helper(self.fixed_param,
[2296316]3091                                           self.orientation_params_disp)
[be1ec9f]3092            content += strings
[7cd87c2]3093
[2296316]3094            # go through the str params
[be1ec9f]3095            strings = self._get_copy_helper(self.str_parameters,
[2296316]3096                                           self.orientation_params)
[be1ec9f]3097            content += strings
[951a2f5]3098            return content
3099        else:
3100            return False
[34dbaf4]3101
3102    def get_copy_excel(self):
3103        """
3104        Get copy params to clipboard
3105        """
3106        content = self.get_copy_params_excel()
3107        flag = self.set_clipboard(content)
3108        self._copy_info(flag)
3109        return flag
3110
3111    def get_copy_params_excel(self):
3112        """
3113        Get the string copies of the param names and values in the tap
3114        """
3115        content = ''
3116
3117        crlf = chr(13) + chr(10)
3118        tab = chr(9)
3119
3120        # Do it if params exist
3121        if  self.parameters != []:
3122
3123            for param in self.parameters:
3124                content += param[1] #parameter name
3125                content += tab
[7cd87c2]3126                content += param[1] + "_err"
[34dbaf4]3127                content += tab
3128
3129            content += crlf
3130
3131            #row of values and errors...
3132            for param in self.parameters:
3133                content += param[2].GetValue() #value
[7cd87c2]3134                content += tab
[34dbaf4]3135                content += param[4].GetValue() #error
[7cd87c2]3136                content += tab
[34dbaf4]3137
3138            return content
3139        else:
3140            return False
3141
3142
3143    def get_copy_latex(self):
3144        """
3145        Get copy params to clipboard
3146        """
3147        content = self.get_copy_params_latex()
3148        flag = self.set_clipboard(content)
3149        self._copy_info(flag)
3150        return flag
3151
3152    def get_copy_params_latex(self):
3153        """
3154        Get the string copies of the param names and values in the tap
3155        """
3156        content = '\\begin{table}'
3157        content += '\\begin{tabular}[h]'
3158
3159        crlf = chr(13) + chr(10)
3160        tab = chr(9)
3161
3162        # Do it if params exist
3163        if  self.parameters != []:
3164
3165            content += '{|'
3166            for param in self.parameters:
3167                content += 'l|l|'
3168            content += '}\hline'
3169            content += crlf
3170
3171            for index, param in enumerate(self.parameters):
[7cd87c2]3172                content += param[1].replace('_', '\_') #parameter name
[34dbaf4]3173                content += ' & '
[7cd87c2]3174                content += param[1].replace('_', '\_') + "\_err"
3175                if index < len(self.parameters) - 1:
[34dbaf4]3176                    content += ' & '
3177            content += '\\\\ \\hline'
3178            content += crlf
3179
3180            #row of values and errors...
3181            for index, param in enumerate(self.parameters):
3182                content += param[2].GetValue() #parameter value
3183                content += ' & '
3184                content += param[4].GetValue() #parameter error
[7cd87c2]3185                if index < len(self.parameters) - 1:
[34dbaf4]3186                    content += ' & '
3187            content += '\\\\ \\hline'
3188            content += crlf
3189
3190            content += '\\end{tabular}'
3191            content += '\\end{table}'
3192            return content
3193        else:
3194            return False
3195
3196
[ef26c83]3197    def set_clipboard(self, content=None):
[951a2f5]3198        """
3199        Put the string to the clipboard
[ef26c83]3200        """
[951a2f5]3201        if not content:
3202            return False
[2296316]3203        if wx.TheClipboard.Open():
3204            wx.TheClipboard.SetData(wx.TextDataObject(str(content)))
3205            wx.TheClipboard.Close()
[951a2f5]3206            return True
[2296316]3207        return None
[7cd87c2]3208
[2296316]3209    def _get_copy_helper(self, param, orient_param):
3210        """
3211        Helping get value and name of the params
[7cd87c2]3212
[2296316]3213        : param param:  parameters
3214        : param orient_param = oritational params
3215        : return content: strings [list] [name,value:....]
3216        """
3217        content = ''
3218        # go through the str params
[ef26c83]3219        for item in param:
[1ac202f]3220            # copy only the params shown
3221            if not item[2].IsShown():
3222                continue
[68a921f]3223            disfunc = ''
3224            try:
3225                if item[7].__class__.__name__ == 'ComboBox':
[706667b]3226                    disfunc = str(item[7].GetValue())
[68a921f]3227            except:
[7cd87c2]3228                logging.error(sys.exc_value)
3229
[2296316]3230            # 2D
[25c0def]3231            if self.data.__class__.__name__ == "Data2D":
[fc1761b]3232                try:
3233                    check = item[0].GetValue()
3234                except:
3235                    check = None
[2296316]3236                name = item[1]
3237                value = item[2].GetValue()
3238            # 1D
3239            else:
3240                ## for 1D all parameters except orientation
3241                if not item[1] in orient_param:
[fc1761b]3242                    try:
3243                        check = item[0].GetValue()
3244                    except:
3245                        check = None
[2296316]3246                    name = item[1]
3247                    value = item[2].GetValue()
[706667b]3248
[2296316]3249            # add to the content
[68a921f]3250            if disfunc != '':
[7cd87c2]3251
[68a921f]3252                disfunc = ',' + disfunc
[be1ec9f]3253            # Need to support array func for copy/paste
[ace0e33]3254            try:
3255                if disfunc.count('array') > 0:
3256                    disfunc += ','
3257                    for val in self.values[name]:
3258                        disfunc += ' ' + str(val)
3259                    disfunc += ','
3260                    for weight in self.weights[name]:
3261                        disfunc += ' ' + str(weight)
3262            except:
[7cd87c2]3263                logging.error(sys.exc_value)
[ef26c83]3264            content += name + ',' + str(check) + ',' + value + disfunc + ':'
[68a921f]3265
[2296316]3266        return content
[7cd87c2]3267
[ef26c83]3268    def get_clipboard(self):
[951a2f5]3269        """
3270        Get strings in the clipboard
3271        """
[ef26c83]3272        text = ""
3273        # Get text from the clip board
[951a2f5]3274        if wx.TheClipboard.Open():
[25c0def]3275            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
3276                data = wx.TextDataObject()
3277                # get wx dataobject
[2a66329]3278                success = wx.TheClipboard.GetData(data)
[25c0def]3279                # get text
[2a66329]3280                if success:
3281                    text = data.GetText()
3282                else:
3283                    text = ''
[25c0def]3284            # close clipboard
3285            wx.TheClipboard.Close()
[951a2f5]3286        return text
[7cd87c2]3287
[951a2f5]3288    def get_paste(self):
3289        """
3290        Paste params from the clipboard
3291        """
3292        text = self.get_clipboard()
[817991b]3293        flag = self.get_paste_params(text)
3294        self._copy_info(flag)
3295        return flag
[7cd87c2]3296
[ef26c83]3297    def get_paste_params(self, text=''):
[2296316]3298        """
3299        Get the string copies of the param names and values in the tap
[ef26c83]3300        """
3301        context = {}
3302        # put the text into dictionary
[2296316]3303        lines = text.split(':')
[b9a5f0e]3304        if lines[0] != 'sasview_parameter_values':
[977a965]3305            self._copy_info(False)
[2296316]3306            return False
3307        for line in lines[1:-1]:
3308            if len(line) != 0:
[25c0def]3309                item = line.split(',')
[fc1761b]3310                check = item[1]
[2296316]3311                name = item[0]
[fc1761b]3312                value = item[2]
[2296316]3313                # Transfer the text to content[dictionary]
[fc1761b]3314                context[name] = [check, value]
[68a921f]3315            # ToDo: PlugIn this poly disp function for pasting
[706667b]3316            try:
[fc1761b]3317                poly_func = item[3]
[ace0e33]3318                context[name].append(poly_func)
3319                try:
[ef26c83]3320                    # take the vals and weights for  array
[fc1761b]3321                    array_values = item[4].split(' ')
3322                    array_weights = item[5].split(' ')
[ace0e33]3323                    val = [float(a_val) for a_val in array_values[1:]]
3324                    weit = [float(a_weit) for a_weit in array_weights[1:]]
[7cd87c2]3325
[ace0e33]3326                    context[name].append(val)
3327                    context[name].append(weit)
3328                except:
3329                    raise
[706667b]3330            except:
[ace0e33]3331                poly_func = ''
3332                context[name].append(poly_func)
[706667b]3333
[ef26c83]3334        # Do it if params exist
[2296316]3335        if  self.parameters != []:
[ef26c83]3336            # go through the parameters
3337            self._get_paste_helper(self.parameters,
[2296316]3338                                   self.orientation_params, context)
3339
3340            # go through the fittables
[ef26c83]3341            self._get_paste_helper(self.fittable_param,
3342                                   self.orientation_params_disp,
[706667b]3343                                   context)
[2296316]3344
3345            # go through the fixed params
[ef26c83]3346            self._get_paste_helper(self.fixed_param,
[2296316]3347                                   self.orientation_params_disp, context)
[7cd87c2]3348
[2296316]3349            # go through the str params
[ef26c83]3350            self._get_paste_helper(self.str_parameters,
[2296316]3351                                   self.orientation_params, context)
[7cd87c2]3352
[817991b]3353            return True
3354        return None
[7cd87c2]3355
[2296316]3356    def _get_paste_helper(self, param, orient_param, content):
3357        """
3358        Helping set values of the params
[7cd87c2]3359
[2296316]3360        : param param:  parameters
3361        : param orient_param: oritational params
[ef26c83]3362        : param content: dictionary [ name, value: name1.value1,...]
[2296316]3363        """
3364        # go through the str params
[ef26c83]3365        for item in param:
[2296316]3366            # 2D
[25c0def]3367            if self.data.__class__.__name__ == "Data2D":
[2296316]3368                name = item[1]
3369                if name in content.keys():
[fc1761b]3370                    check = content[name][0]
3371                    pd = content[name][1]
[706667b]3372                    if name.count('.') > 0:
3373                        try:
[ace0e33]3374                            float(pd)
[706667b]3375                        except:
[01642fec]3376                            #continue
3377                            if not pd and pd != '':
3378                                continue
[ace0e33]3379                    item[2].SetValue(str(pd))
[01642fec]3380                    if item in self.fixed_param and pd == '':
3381                        # Only array func has pd == '' case.
3382                        item[2].Enable(False)
[706667b]3383                    if item[2].__class__.__name__ == "ComboBox":
[ef26c83]3384                        if content[name][1] in self.model.fun_list:
[fc1761b]3385                            fun_val = self.model.fun_list[content[name][1]]
[25c0def]3386                            self.model.setParam(name, fun_val)
[7cd87c2]3387
[fc1761b]3388                    value = content[name][1:]
[ace0e33]3389                    self._paste_poly_help(item, value)
[fc1761b]3390                    if check == 'True':
3391                        is_true = True
3392                    elif check == 'False':
3393                        is_true = False
3394                    else:
3395                        is_true = None
3396                    if is_true != None:
3397                        item[0].SetValue(is_true)
3398            # 1D
[2296316]3399            else:
3400                ## for 1D all parameters except orientation
3401                if not item[1] in orient_param:
3402                    name = item[1]
3403                    if name in content.keys():
[fc1761b]3404                        check = content[name][0]
[ef26c83]3405                        # Avoid changing combox content
[fc1761b]3406                        value = content[name][1:]
[ace0e33]3407                        pd = value[0]
[706667b]3408                        if name.count('.') > 0:
3409                            try:
[ace0e33]3410                                pd = float(pd)
[706667b]3411                            except:
[01642fec]3412                                #continue
3413                                if not pd and pd != '':
3414                                    continue
[ace0e33]3415                        item[2].SetValue(str(pd))
3416                        if item in self.fixed_param and pd == '':
3417                            # Only array func has pd == '' case.
3418                            item[2].Enable(False)
[d8c54ead]3419                        if item[2].__class__.__name__ == "ComboBox":
[ef26c83]3420                            if value[0] in self.model.fun_list:
[706667b]3421                                fun_val = self.model.fun_list[value[0]]
[25c0def]3422                                self.model.setParam(name, fun_val)
[d8c54ead]3423                                # save state
[ace0e33]3424                        self._paste_poly_help(item, value)
[fc1761b]3425                        if check == 'True':
3426                            is_true = True
3427                        elif check == 'False':
3428                            is_true = False
3429                        else:
3430                            is_true = None
3431                        if is_true != None:
3432                            item[0].SetValue(is_true)
[7cd87c2]3433
[ace0e33]3434    def _paste_poly_help(self, item, value):
3435        """
3436        Helps get paste for poly function
[7cd87c2]3437
[ace0e33]3438        :param item: Gui param items
3439        :param value: the values for parameter ctrols
3440        """
[6b39d58]3441        is_array = False
[ace0e33]3442        if len(value[1]) > 0:
3443            # Only for dispersion func.s
3444            try:
3445                item[7].SetValue(value[1])
3446                selection = item[7].GetCurrentSelection()
[01642fec]3447                name = item[7].Name
3448                param_name = name.split('.')[0]
[25c0def]3449                dispersity = item[7].GetClientData(selection)
[ace0e33]3450                disp_model = dispersity()
3451                # Only for array disp
3452                try:
3453                    pd_vals = numpy.array(value[2])
3454                    pd_weights = numpy.array(value[3])
3455                    if len(pd_vals) > 0 and len(pd_vals) > 0:
3456                        if len(pd_vals) == len(pd_weights):
[6b39d58]3457                            self._set_disp_array_cb(item=item)
[ef26c83]3458                            self._set_array_disp_model(name=name,
[6b39d58]3459                                                       disp=disp_model,
[ef26c83]3460                                                       values=pd_vals,
[6b39d58]3461                                                       weights=pd_weights)
3462                            is_array = True
[ace0e33]3463                except:
[7cd87c2]3464                    logging.error(sys.exc_value)
[6b39d58]3465                if not is_array:
3466                    self._disp_obj_dict[name] = disp_model
[ef26c83]3467                    self.model.set_dispersion(name,
[6b39d58]3468                                              disp_model)
3469                    self.state._disp_obj_dict[name] = \
3470                                              disp_model
3471                    self.model.set_dispersion(param_name, disp_model)
3472                    self.state.values = self.values
[ef26c83]3473                    self.state.weights = self.weights
[6b39d58]3474                    self.model._persistency_dict[param_name] = \
[ef26c83]3475                                            [self.state.values,
[25c0def]3476                                             self.state.weights]
[7cd87c2]3477
[ace0e33]3478            except:
[7cd87c2]3479                logging.error(sys.exc_value)
[ef26c83]3480                print "Error in BasePage._paste_poly_help: %s" % sys.exc_value
[7cd87c2]3481
[6b39d58]3482    def _set_disp_array_cb(self, item):
3483        """
3484        Set cb for array disp
3485        """
3486        item[0].SetValue(False)
3487        item[0].Enable(False)
3488        item[2].Enable(False)
3489        item[3].Show(False)
3490        item[4].Show(False)
3491        item[5].SetValue('')
3492        item[5].Enable(False)
3493        item[6].SetValue('')
3494        item[6].Enable(False)
[7cd87c2]3495
[25c0def]3496    def update_pinhole_smear(self):
3497        """
3498            Method to be called by sub-classes
[be1ec9f]3499            Moveit; This method doesn't belong here
[25c0def]3500        """
3501        print "BasicPage.update_pinhole_smear was called: skipping"
3502        return
[0b477f6]3503
[ea5fa58]3504
3505
3506
[df7a7e3]3507    def _read_category_info(self):
3508        """
3509        Reads the categories in from file
3510        """
3511
3512        # # ILL mod starts here - July 2012 kieranrcampbell@gmail.com
3513        self.master_category_dict = defaultdict(list)
3514        self.by_model_dict = defaultdict(list)
3515        self.model_enabled_dict = defaultdict(bool)
3516
3517        try:
[ea5fa58]3518            categorization_file = CategoryInstaller.get_user_file()
3519            if not os.path.isfile(categorization_file):
3520                categorization_file = CategoryInstaller.get_default_file()
[7cd87c2]3521            cat_file = open(categorization_file, 'rb')
[27b7acc]3522            self.master_category_dict = json.load(cat_file)
[df7a7e3]3523            self._regenerate_model_dict()
[657e52c]3524            cat_file.close()
[df7a7e3]3525
3526        except IOError:
[ea5fa58]3527            raise
[df7a7e3]3528            print 'Problem reading in category file.'
3529            print 'We even looked for it, made sure it was there.'
3530            print 'An existential crisis if there ever was one.'
3531
3532    def _regenerate_model_dict(self):
3533        """
[7cd87c2]3534        regenerates self.by_model_dict which has each model name as the
[df7a7e3]3535        key and the list of categories belonging to that model
3536        along with the enabled mapping
3537        """
3538
3539        self.by_model_dict = defaultdict(list)
3540        for category in self.master_category_dict:
[ea5fa58]3541            for (model, enabled) in self.master_category_dict[category]:
[df7a7e3]3542                self.by_model_dict[model].append(category)
3543                self.model_enabled_dict[model] = enabled
[7cd87c2]3544
[df7a7e3]3545    def _populate_listbox(self):
3546        """
3547        fills out the category list box
3548        """
[ea5fa58]3549        uncat_str = 'Customized Models'
[df7a7e3]3550        self._read_category_info()
3551
[ea5fa58]3552        self.categorybox.Clear()
[df7a7e3]3553        cat_list = sorted(self.master_category_dict.keys())
[ea5fa58]3554        if not uncat_str in cat_list:
3555            cat_list.append(uncat_str)
[7cd87c2]3556
[df7a7e3]3557        for category in cat_list:
3558            if category != '':
[ea5fa58]3559                self.categorybox.Append(category)
[df7a7e3]3560
[ea5fa58]3561        if self.categorybox.GetSelection() == wx.NOT_FOUND:
3562            self.categorybox.SetSelection(0)
[df7a7e3]3563        else:
[7cd87c2]3564            self.categorybox.SetSelection(\
[ea5fa58]3565                self.categorybox.GetSelection())
[df7a7e3]3566
[ea5fa58]3567        #self._on_change_cat(None)
[df7a7e3]3568
3569
3570    def _on_change_cat(self, event):
3571        """
3572        Callback for category change action
3573        """
3574        self.model_name = None
[ea5fa58]3575        category = self.categorybox.GetStringSelection()
[df7a7e3]3576        if category == None:
3577            return
3578        self.model_box.Clear()
3579
[ea5fa58]3580        if category == 'Customized Models':
3581            for model in self.model_list_box[category]:
[df7a7e3]3582                str_m = str(model).split(".")[0]
3583                self.model_box.Append(str_m)
3584
3585        else:
[7cd87c2]3586            for (model, enabled) in sorted(self.master_category_dict[category],
3587                                      key=lambda name: name[0]):
[df7a7e3]3588                if(enabled):
3589                    self.model_box.Append(model)
3590
[ea5fa58]3591
3592
3593
[0b477f6]3594    def _fill_model_sizer(self, sizer):
3595        """
3596        fill sizer containing model info
3597        """
3598        ##Add model function Details button in fitpanel.
3599        ##The following 3 lines are for Mac. Let JHC know before modifying...
3600        title = "Model"
3601        self.formfactorbox = None
3602        self.multifactorbox = None
3603        self.mbox_description = wx.StaticBox(self, -1, str(title))
3604        boxsizer1 = wx.StaticBoxSizer(self.mbox_description, wx.VERTICAL)
[ea5fa58]3605        sizer_cat = wx.BoxSizer(wx.HORIZONTAL)
[0b477f6]3606        self.mbox_description.SetForegroundColour(wx.RED)
3607        id = wx.NewId()
[c1694f8]3608        self.model_func = wx.Button(self, id, 'Help', size=(80, 23))
3609        self.model_func.Bind(wx.EVT_BUTTON, self.on_function_help_clicked, id=id)
3610        self.model_func.SetToolTipString("Full Model Function Help")
3611        id = wx.NewId()
3612        self.model_help = wx.Button(self, id, 'Description', size=(80, 23))
[0b477f6]3613        self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked, id=id)
[c1694f8]3614        self.model_help.SetToolTipString("Short Model Function Description")
[0b477f6]3615        id = wx.NewId()
3616        self.model_view = wx.Button(self, id, "Show 2D", size=(80, 23))
3617        self.model_view.Bind(wx.EVT_BUTTON, self._onModel2D, id=id)
3618        hint = "toggle view of model from 1D to 2D  or 2D to 1D"
3619        self.model_view.SetToolTipString(hint)
[7cd87c2]3620
[ea5fa58]3621        cat_set_box = wx.StaticBox(self, -1, 'Category')
3622        sizer_cat_box = wx.StaticBoxSizer(cat_set_box, wx.HORIZONTAL)
3623        sizer_cat_box.SetMinSize((200, 50))
3624        self.categorybox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
[7cd87c2]3625        self.categorybox.SetToolTip(wx.ToolTip("Select a Category/Type"))
[ea5fa58]3626        self._populate_listbox()
3627        wx.EVT_COMBOBOX(self.categorybox, -1, self._show_combox)
3628        #self.shape_rbutton = wx.RadioButton(self, -1, 'Shapes',
3629        #                                     style=wx.RB_GROUP)
3630        #self.shape_indep_rbutton = wx.RadioButton(self, -1,
3631        #                                          "Shape-Independent")
3632        #self.struct_rbutton = wx.RadioButton(self, -1, "Structure Factor ")
3633        #self.plugin_rbutton = wx.RadioButton(self, -1, "Uncategorized")
[7cd87c2]3634
[ea5fa58]3635        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3636        #                   id=self.shape_rbutton.GetId())
3637        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3638        #                    id=self.shape_indep_rbutton.GetId())
3639        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3640        #                    id=self.struct_rbutton.GetId())
3641        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3642        #                    id=self.plugin_rbutton.GetId())
3643        #MAC needs SetValue
[7cd87c2]3644
[ef67145]3645        show_cat_button = wx.Button(self, -1, "Modify")
3646        cat_tip = "Modify model categories \n"
3647        cat_tip += "(also accessible from the menu bar)."
[7cd87c2]3648        show_cat_button.SetToolTip(wx.ToolTip(cat_tip))
[ef67145]3649        show_cat_button.Bind(wx.EVT_BUTTON, self._on_modify_cat)
3650        sizer_cat_box.Add(self.categorybox, 1, wx.RIGHT, 3)
[7cd87c2]3651        sizer_cat_box.Add((10, 10))
[ef67145]3652        sizer_cat_box.Add(show_cat_button)
[ea5fa58]3653        #self.shape_rbutton.SetValue(True)
[7cd87c2]3654
[ea5fa58]3655        sizer_radiobutton = wx.GridSizer(2, 2, 5, 5)
[7cd87c2]3656
[ea5fa58]3657
3658        #sizer_radiobutton.Add(self.shape_rbutton)
3659        #sizer_radiobutton.Add(self.shape_indep_rbutton)
[7cd87c2]3660        sizer_radiobutton.Add((5, 5))
[c1694f8]3661        sizer_radiobutton.Add(self.model_view, 1, wx.RIGHT, 5)
[ea5fa58]3662        #sizer_radiobutton.Add(self.plugin_rbutton)
3663        #sizer_radiobutton.Add(self.struct_rbutton)
[c1694f8]3664#        sizer_radiobutton.Add((5,5))
[7cd87c2]3665        sizer_radiobutton.Add(self.model_help, 1, wx.RIGHT | wx.LEFT, 5)
[c1694f8]3666#        sizer_radiobutton.Add((5,5))
3667        sizer_radiobutton.Add(self.model_func, 1, wx.RIGHT, 5)
[ea5fa58]3668        sizer_cat.Add(sizer_cat_box, 1, wx.LEFT, 2.5)
3669        sizer_cat.Add(sizer_radiobutton)
[0b477f6]3670        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
3671        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
[7cd87c2]3672
[0b477f6]3673        self.text1 = wx.StaticText(self, -1, "")
[ea5fa58]3674        self.text2 = wx.StaticText(self, -1, "P(Q)*S(Q)")
[0b477f6]3675        self.mutifactor_text = wx.StaticText(self, -1, "No. of Shells: ")
3676        self.mutifactor_text1 = wx.StaticText(self, -1, "")
3677        self.show_sld_button = wx.Button(self, -1, "Show SLD Profile")
3678        self.show_sld_button.Bind(wx.EVT_BUTTON, self._on_show_sld)
3679
[ea5fa58]3680        self.formfactorbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
[7cd87c2]3681        self.formfactorbox.SetToolTip(wx.ToolTip("Select a Model"))
[ea5fa58]3682        if self.model != None:
3683            self.formfactorbox.SetValue(self.model.name)
[0b477f6]3684        self.structurebox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
3685        self.multifactorbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
3686        self.initialize_combox()
[ea5fa58]3687        wx.EVT_COMBOBOX(self.formfactorbox, -1, self._on_select_model)
[0b477f6]3688
3689        wx.EVT_COMBOBOX(self.structurebox, -1, self._on_select_model)
3690        wx.EVT_COMBOBOX(self.multifactorbox, -1, self._on_select_model)
3691        ## check model type to show sizer
3692        if self.model != None:
[ea5fa58]3693            print "_set_model_sizer_selection: disabled."
3694            #self._set_model_sizer_selection(self.model)
[7cd87c2]3695
[ea5fa58]3696        sizer_selection.Add(self.text1)
3697        sizer_selection.Add((10, 5))
3698        sizer_selection.Add(self.formfactorbox)
3699        sizer_selection.Add((5, 5))
3700        sizer_selection.Add(self.text2)
3701        sizer_selection.Add((5, 5))
3702        sizer_selection.Add(self.structurebox)
[7cd87c2]3703
[ea5fa58]3704        mutifactor_selection.Add((13, 5))
[0b477f6]3705        mutifactor_selection.Add(self.mutifactor_text)
3706        mutifactor_selection.Add(self.multifactorbox)
3707        mutifactor_selection.Add((5, 5))
3708        mutifactor_selection.Add(self.mutifactor_text1)
3709        mutifactor_selection.Add((10, 5))
3710        mutifactor_selection.Add(self.show_sld_button)
3711
[ea5fa58]3712        boxsizer1.Add(sizer_cat)
3713        boxsizer1.Add((10, 10))
3714        boxsizer1.Add(sizer_selection)
[0b477f6]3715        boxsizer1.Add((10, 10))
3716        boxsizer1.Add(mutifactor_selection)
[7cd87c2]3717
[0b477f6]3718        self._set_multfactor_combobox()
3719        self.multifactorbox.SetSelection(1)
3720        self.show_sld_button.Hide()
3721        sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3722        sizer.Layout()
[7cd87c2]3723
[fcd6c6a]3724    def on_smear_helper(self, update=False):
3725        """
3726        Help for onSmear if implemented
[7cd87c2]3727
[fcd6c6a]3728        :param update: force or not to update
3729        """
3730    def reset_page(self, state, first=False):
3731        """
3732        reset the state  if implemented
3733        """
3734    def onSmear(self, event):
3735        """
[7cd87c2]3736        Create a smear object if implemented
[fcd6c6a]3737        """
3738    def onPinholeSmear(self, event):
3739        """
3740        Create a custom pinhole smear object if implemented
3741        """
3742    def onSlitSmear(self, event):
3743        """
3744        Create a custom slit smear object if implemented
3745        """
3746    def update_slit_smear(self):
3747        """
3748        called by kill_focus on pinhole TextCntrl
3749        to update the changes if implemented
3750        """
3751    def select_param(self, event):
3752        """
3753        Select TextCtrl  checked if implemented
3754        """
3755    def set_data(self, data=None):
3756        """
3757        Sets data if implemented
3758        """
3759    def _is_2D(self):
3760        """
3761        Check if data_name is Data2D if implemented
3762        """
3763    def _on_select_model(self, event=None):
3764        """
3765        call back for model selection if implemented
3766        """
3767    def select_all_param(self, event):
3768        """
3769        set to true or false all checkBox if implemented
3770        """
3771    def get_weight_flag(self):
3772        """
3773        Get flag corresponding to a given weighting dI data if implemented
3774        """
3775    def _set_sizer_dispersion(self):
3776        """
3777        draw sizer for dispersity if implemented
3778        """
3779    def get_all_checked_params(self):
3780        """
3781        Found all parameters current check and add them to list of parameters
3782        to fit if implemented
3783        """
[bf5e985]3784    def show_npts2fit(self):
[fcd6c6a]3785        """
3786        setValue Npts for fitting if implemented
3787        """
3788    def _onModel2D(self, event):
3789        """
3790        toggle view of model from 1D to 2D  or 2D from 1D if implemented
[6fe5100]3791        """
Note: See TracBrowser for help on using the repository browser.