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

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 8416a02 was 6ed67db, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

better error handling for disperser objects

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