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

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 dcdca68 was 08959b8, checked in by krzywon, 9 years ago

Commiting the MultiplicationModel? back to source and moving it from sas.models to sas.sascalc.fitting

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