source: sasview/src/sas/sasgui/perspectives/fitting/basepage.py @ ba8d326

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since ba8d326 was ba8d326, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

code cleanup

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