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

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 bc9a0e1 was c1694f8, checked in by butler, 10 years ago

Moved the rest of basepage help to the new method. Basically
MODELS
1) Added a button to separate description help from function details
help
2) Make function details help go to new documentation
3) Converted polydispersity help (hitting question mark) and mag angle
description help (question mark again) to use new documentation.

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