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

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

Reenable processing of Npts and Nsigs for polydispersity

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