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

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 7cd87c2 was 7cd87c2, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

pylint fixes and remove except followed by pass

  • Property mode set to 100644
File size: 145.9 KB
Line 
1"""
2Base Page for fitting
3"""
4import sys
5import os
6import wx
7import numpy
8import time
9import copy
10import math
11import string
12import json
13import logging
14from collections import defaultdict
15from wx.lib.scrolledpanel import ScrolledPanel
16from sas.guiframe.panel_base import PanelBase
17from sas.guiframe.utils import format_number, check_float
18from sas.guiframe.events import PanelOnFocusEvent
19from sas.guiframe.events import StatusEvent
20from sas.guiframe.events import AppendBookmarkEvent
21from sas.guiframe.dataFitting import Data2D
22from sas.guiframe.dataFitting import Data1D
23from sas.guiframe.dataFitting import check_data_validity
24from sas.guiframe.gui_style import GUIFRAME_ID
25from sas.dataloader.data_info import Detector
26from sas.dataloader.data_info import Source
27from sas.perspectives.fitting.pagestate import PageState
28from sas.guiframe.CategoryInstaller import CategoryInstaller
29from sas.guiframe.documentation_window import DocumentationWindow
30
31
32(PageInfoEvent, EVT_PAGE_INFO) = wx.lib.newevent.NewEvent()
33(PreviousStateEvent, EVT_PREVIOUS_STATE) = wx.lib.newevent.NewEvent()
34(NextStateEvent, EVT_NEXT_STATE) = wx.lib.newevent.NewEvent()
35
36_BOX_WIDTH = 76
37_QMIN_DEFAULT = 0.0005
38_QMAX_DEFAULT = 0.5
39_NPTS_DEFAULT = 50
40#Control panel width
41if sys.platform.count("win32") > 0:
42    PANEL_WIDTH = 450
43    FONT_VARIANT = 0
44    ON_MAC = False
45else:
46    PANEL_WIDTH = 500
47    FONT_VARIANT = 1
48    ON_MAC = True
49
50
51class BasicPage(ScrolledPanel, PanelBase):
52    """
53    This class provide general structure of  fitpanel page
54    """
55    ## Internal name for the AUI manager
56    window_name = "Fit Page"
57    ## Title to appear on top of the window
58    window_caption = "Fit Page "
59
60    def __init__(self, parent, color='blue', **kwargs):
61        """
62        """
63        ScrolledPanel.__init__(self, parent, **kwargs)
64        PanelBase.__init__(self, parent)
65        self.SetupScrolling()
66        #Set window's font size
67        self.SetWindowVariant(variant=FONT_VARIANT)
68
69        self.SetBackgroundColour(color)
70        ## parent of the page
71        self.parent = parent
72        ## manager is the fitting plugin
73        ## owner of the page (fitting plugin)
74        self.event_owner = None
75        ## current model
76        self.model = None
77        self.m_name = None
78        self.index_model = None
79        self.panel = None
80        ## data
81        self.data = None
82        #list of available data
83        self.data_list = []
84        self.mask = None
85        self.uid = wx.NewId()
86        self.graph_id = None
87        #Q range for data set
88        self.qmin_data_set = numpy.inf
89        self.qmax_data_set = None
90        self.npts_data_set = 0
91        ## Q range
92        self.qmin = None
93        self.qmax = None
94        self.qmax_x = _QMAX_DEFAULT
95        self.qmin_x = _QMIN_DEFAULT
96        self.npts_x = _NPTS_DEFAULT
97        ## total number of point: float
98        self.npts = None
99        self.num_points = None
100        ## default fitengine type
101        self.engine_type = 'bumps'
102        ## smear default
103        self.current_smearer = None
104        ## 2D smear accuracy default
105        self.smear2d_accuracy = 'Low'
106        ## slit smear:
107        self.dxl = None
108        self.dxw = None
109        ## pinhole smear
110        self.dx_min = None
111        self.dx_max = None
112        ##semar attrbs
113        self.enable_smearer = None
114        self.disable_smearer = None
115        self.pinhole_smearer = None
116        self.slit_smearer = None
117        ##weigth attrbs
118        self.dI_noweight = None
119        self.dI_didata = None
120        self.dI_sqrdata = None
121        self.dI_idata = None
122        ##other attrbs
123        self.dq_l = None
124        self.dq_r = None
125        self.tcChi = None
126        self.disp_box = None
127        self.model_disp = None
128        self.Npts_fit = None
129        self.Npts_total = None
130        self.theory_qmin = None
131        self.theory_qmax = None
132        self.theory_qmin_x = None
133        self.theory_qmax_x = None
134        self.cb1 = None
135        self.btEditMask = None
136        self.btFit = None
137        self.sld_axes = None
138        self.multi_factor = None
139
140        self.disp_cb_dict = {}
141
142        #self.state = PageState(parent=parent)
143        ## dictionary containing list of models
144        self.model_list_box = {}
145
146        ## Data member to store the dispersion object created
147        self._disp_obj_dict = {}
148        ## selected parameters to apply dispersion
149        self.disp_cb_dict = {}
150        ## smearer object
151        self.enable2D = False
152        self._has_magnetic = False
153        self.magnetic_on = False
154        self.is_mac = ON_MAC
155        self.formfactorbox = None
156        self.structurebox = None
157        self.categorybox = None
158        ##list of model parameters. each item must have same length
159        ## each item related to a given parameters
160        ##[cb state, name, value, "+/-", error of fit, min, max , units]
161        self.parameters = []
162        # non-fittable parameter whose value is astring
163        self.str_parameters = []
164        ## list of parameters to fit , must be like self.parameters
165        self.param_toFit = []
166        ## list of looking like parameters but with non fittable parameters info
167        self.fixed_param = []
168        ## list of looking like parameters but with  fittable parameters info
169        self.fittable_param = []
170        ##list of dispersion parameters
171        self.disp_list = []
172        self.disp_name = ""
173
174        ## list of orientation parameters
175        self.orientation_params = []
176        self.orientation_params_disp = []
177#       Self.model should ALWAYS be None here.  It was set to none above in
178#       this long init setting.  no obvious function call in between setting
179#       and this - commenting out on 4/8/2014 by PDB.  Remove once clear
180#       it is pointless.
181#        if self.model != None:
182#            self.disp_list = self.model.getDispParamList()
183        self.temp_multi_functional = False
184        ##enable model 2D draw
185        self.enable2D = False
186        ## check that the fit range is correct to plot the model again
187        self.fitrange = True
188        ## Create memento to save the current state
189        self.state = PageState(parent=self.parent,
190                               model=self.model, data=self.data)
191        ## flag to determine if state has change
192        self.state_change = False
193        ## save customized array
194        self.values = []
195        self.weights = []
196        ## retrieve saved state
197        self.number_saved_state = 0
198        ## dictionary of saved state
199        self.saved_states = {}
200        ## Create context menu for page
201        self.popUpMenu = wx.Menu()
202
203        id = wx.NewId()
204        self._keep = wx.MenuItem(self.popUpMenu, id, "Add bookmark",
205                                 " Keep the panel status to recall it later")
206        self.popUpMenu.AppendItem(self._keep)
207        self._keep.Enable(False)
208        self._set_bookmark_flag(False)
209        self._set_save_flag(False)
210        wx.EVT_MENU(self, id, self.on_bookmark)
211        self.popUpMenu.AppendSeparator()
212
213        ## Default locations
214        self._default_save_location = os.getcwd()
215        ## save initial state on context menu
216        #self.onSave(event=None)
217        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
218
219        # bind key event
220        self.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
221
222        ## create the basic structure of the panel with empty sizer
223        self.define_page_structure()
224        ## drawing Initial dispersion parameters sizer
225        self.set_dispers_sizer()
226
227        ## layout
228        self.set_layout()
229
230    def set_index_model(self, index):
231        """
232        Index related to this page
233        """
234        self.index_model = index
235
236    def create_default_data(self):
237        """
238        Given the user selection, creates a 1D or 2D data
239        Only when the page is on theory mode.
240        """
241        if not hasattr(self, "model_view"):
242            return
243        toggle_mode_on = self.model_view.IsEnabled()
244        if toggle_mode_on:
245            if self.enable2D and not check_data_validity(self.data):
246                self._create_default_2d_data()
247            else:
248                if self.pointsbox.GetValue():
249                    self._create_log_1d_data()
250                else:
251                    self._create_default_1d_data()
252
253            if self.model != None:
254                if not self.data.is_data:
255                    self._manager.page_finder[self.uid].set_fit_data(data=\
256                                                                [self.data])
257            self.on_smear_helper(update=True)
258            self.state.enable_smearer = self.enable_smearer.GetValue()
259            self.state.disable_smearer = self.disable_smearer.GetValue()
260            self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
261            self.state.slit_smearer = self.slit_smearer.GetValue()
262
263    def _create_default_1d_data(self):
264        """
265        Create default data for fitting perspective
266        Only when the page is on theory mode.
267        :warning: This data is never plotted.
268
269        """
270        x = numpy.linspace(start=self.qmin_x, stop=self.qmax_x,
271                           num=self.npts_x, endpoint=True)
272        self.data = Data1D(x=x)
273        self.data.xaxis('\\rm{Q}', "A^{-1}")
274        self.data.yaxis('\\rm{Intensity}', "cm^{-1}")
275        self.data.is_data = False
276        self.data.id = str(self.uid) + " data"
277        self.data.group_id = str(self.uid) + " Model1D"
278
279    def _create_log_1d_data(self):
280        """
281        Create log-spaced data for fitting perspective
282        Only when the page is on theory mode.
283        :warning: This data is never plotted.
284
285        """
286        if self.qmin_x >= 1.e-10:
287            qmin = numpy.log10(self.qmin_x)
288        else:
289            qmin = -10.
290
291        if self.qmax_x <= 1.e10:
292            qmax = numpy.log10(self.qmax_x)
293        else:
294            qmax = 10.
295
296        x = numpy.logspace(start=qmin, stop=qmax,
297                           num=self.npts_x, endpoint=True, base=10.0)
298        self.data = Data1D(x=x)
299        self.data.xaxis('\\rm{Q}', "A^{-1}")
300        self.data.yaxis('\\rm{Intensity}', "cm^{-1}")
301        self.data.is_data = False
302        self.data.id = str(self.uid) + " data"
303        self.data.group_id = str(self.uid) + " Model1D"
304
305    def _create_default_2d_data(self):
306        """
307        Create 2D data by default
308        Only when the page is on theory mode.
309        :warning: This data is never plotted.
310        """
311        self.data = Data2D()
312        qmax = self.qmax_x / math.sqrt(2)
313        self.data.xaxis('\\rm{Q_{x}}', 'A^{-1}')
314        self.data.yaxis('\\rm{Q_{y}}', 'A^{-1}')
315        self.data.is_data = False
316        self.data.id = str(self.uid) + " data"
317        self.data.group_id = str(self.uid) + " Model2D"
318        ## Default values
319        self.data.detector.append(Detector())
320        index = len(self.data.detector) - 1
321        self.data.detector[index].distance = 8000   # mm
322        self.data.source.wavelength = 6             # A
323        self.data.detector[index].pixel_size.x = 5  # mm
324        self.data.detector[index].pixel_size.y = 5  # mm
325        self.data.detector[index].beam_center.x = qmax
326        self.data.detector[index].beam_center.y = qmax
327        ## create x_bins and y_bins of the model 2D
328        #pixel_width_x = self.data.detector[index].pixel_size.x
329        #pixel_width_y = self.data.detector[index].pixel_size.y
330        #center_x = self.data.detector[index].beam_center.x/pixel_width_x
331        #center_y = self.data.detector[index].beam_center.y/pixel_width_y
332        # theory default: assume the beam
333        #center is located at the center of sqr detector
334        xmax = qmax
335        xmin = -qmax
336        ymax = qmax
337        ymin = -qmax
338        qstep = self.npts_x
339
340        x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)
341        y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True)
342        ## use data info instead
343        new_x = numpy.tile(x, (len(y), 1))
344        new_y = numpy.tile(y, (len(x), 1))
345        new_y = new_y.swapaxes(0, 1)
346        # all data reuire now in 1d array
347        qx_data = new_x.flatten()
348        qy_data = new_y.flatten()
349        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
350        # set all True (standing for unmasked) as default
351        mask = numpy.ones(len(qx_data), dtype=bool)
352        # calculate the range of qx and qy: this way,
353        # it is a little more independent
354        #x_size = xmax - xmin
355        #y_size = ymax - ymin
356        # store x and y bin centers in q space
357        x_bins = x
358        y_bins = y
359        # bin size: x- & y-directions
360        #xstep = x_size / len(x_bins - 1)
361        #ystep = y_size / len(y_bins - 1)
362
363        self.data.source = Source()
364        self.data.data = numpy.ones(len(mask))
365        self.data.err_data = numpy.ones(len(mask))
366        self.data.qx_data = qx_data
367        self.data.qy_data = qy_data
368        self.data.q_data = q_data
369        self.data.mask = mask
370        self.data.x_bins = x_bins
371        self.data.y_bins = y_bins
372        # max and min taking account of the bin sizes
373        self.data.xmin = xmin
374        self.data.xmax = xmax
375        self.data.ymin = ymin
376        self.data.ymax = ymax
377
378    def on_set_focus(self, event):
379        """
380        On Set Focus, update guimanger and menu
381        """
382        if self._manager is not None:
383            wx.PostEvent(self._manager.parent, PanelOnFocusEvent(panel=self))
384            self.on_tap_focus()
385
386    def on_tap_focus(self):
387        """
388        Update menu1 on cliking the page tap
389        """
390        if self._manager.menu1 != None:
391            chain_menu = self._manager.menu1.FindItemById(\
392                                                   self._manager.id_reset_flag)
393            chain_menu.Enable(self.batch_on)
394            sim_menu = self._manager.menu1.FindItemById(self._manager.id_simfit)
395            flag = self.data.is_data\
396                            and (self.model != None)
397            sim_menu.Enable(not self.batch_on and flag)
398            batch_menu = \
399                    self._manager.menu1.FindItemById(self._manager.id_batchfit)
400            batch_menu.Enable(self.batch_on and flag)
401
402    class ModelTextCtrl(wx.TextCtrl):
403        """
404        Text control for model and fit parameters.
405        Binds the appropriate events for user interactions.
406        Default callback methods can be overwritten on initialization
407
408        :param kill_focus_callback: callback method for EVT_KILL_FOCUS event
409        :param set_focus_callback:  callback method for EVT_SET_FOCUS event
410        :param mouse_up_callback:   callback method for EVT_LEFT_UP event
411        :param text_enter_callback: callback method for EVT_TEXT_ENTER event
412
413        """
414        ## Set to True when the mouse is clicked while whole string is selected
415        full_selection = False
416        ## Call back for EVT_SET_FOCUS events
417        _on_set_focus_callback = None
418
419        def __init__(self, parent, id= -1,
420                     value=wx.EmptyString,
421                     pos=wx.DefaultPosition,
422                     size=wx.DefaultSize,
423                     style=0,
424                     validator=wx.DefaultValidator,
425                     name=wx.TextCtrlNameStr,
426                     kill_focus_callback=None,
427                     set_focus_callback=None,
428                     mouse_up_callback=None,
429                     text_enter_callback=None):
430
431            wx.TextCtrl.__init__(self, parent, id, value, pos,
432                                  size, style, validator, name)
433
434            # Bind appropriate events
435            self._on_set_focus_callback = parent.onSetFocus \
436                      if set_focus_callback is None else set_focus_callback
437            self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
438            self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus \
439                      if kill_focus_callback is None else kill_focus_callback)
440            self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter \
441                      if text_enter_callback is None else text_enter_callback)
442            if not ON_MAC:
443                self.Bind(wx.EVT_LEFT_UP, self._highlight_text \
444                          if mouse_up_callback is None else mouse_up_callback)
445
446        def _on_set_focus(self, event):
447            """
448            Catch when the text control is set in focus to highlight the whole
449            text if necessary
450
451            :param event: mouse event
452
453            """
454            event.Skip()
455            self.full_selection = True
456            return self._on_set_focus_callback(event)
457
458        def _highlight_text(self, event):
459            """
460            Highlight text of a TextCtrl only of no text has be selected
461
462            :param event: mouse event
463
464            """
465            # Make sure the mouse event is available to other listeners
466            event.Skip()
467            control = event.GetEventObject()
468            if self.full_selection:
469                self.full_selection = False
470                # Check that we have a TextCtrl
471                if issubclass(control.__class__, wx.TextCtrl):
472                    # Check whether text has been selected,
473                    # if not, select the whole string
474                    (start, end) = control.GetSelection()
475                    if start == end:
476                        control.SetSelection(-1, -1)
477
478        def _silent_kill_focus(self, event):
479            """
480            Save the state of the page
481            """
482
483            event.Skip()
484            #pass
485
486    def set_page_info(self, page_info):
487        """
488        set some page important information at once
489        """
490#       THIS METHOD/FUNCTION NO LONGE APPEARS TO BE CALLED.  Started up program
491#       and started new fit window and PR and Invariant and a fit in fitting
492#       but never entered this routine which should be an initialization
493#       routine.  Leave for a while but probably something to clean up at
494#       some point?
495#
496#       PDB April 13 2014
497#
498        ##window_name
499        self.window_name = page_info.window_name
500        ##window_caption
501        self.window_caption = page_info.window_caption
502        ## manager is the fitting plugin
503        self._manager = page_info.manager
504        ## owner of the page (fitting plugin)
505        self.event_owner = page_info.event_owner
506        ## current model
507        self.model = page_info.model
508        ## data
509        self.data = page_info.data
510        ## dictionary containing list of models
511        self.model_list_box = page_info.model_list_box
512        ## Data member to store the dispersion object created
513        self.populate_box(model_dict=self.model_list_box)
514
515    def onContextMenu(self, event):
516        """
517        Retrieve the state selected state
518        """
519        # Skipping the save state functionality for release 0.9.0
520        #return
521
522        pos = event.GetPosition()
523        pos = self.ScreenToClient(pos)
524
525        self.PopupMenu(self.popUpMenu, pos)
526
527    def onUndo(self, event):
528        """
529        Cancel the previous action
530        """
531        event = PreviousStateEvent(page=self)
532        wx.PostEvent(self.parent, event)
533
534    def onRedo(self, event):
535        """
536        Restore the previous action cancelled
537        """
538        event = NextStateEvent(page=self)
539        wx.PostEvent(self.parent, event)
540
541    def define_page_structure(self):
542        """
543        Create empty sizer for a panel
544        """
545        self.vbox = wx.BoxSizer(wx.VERTICAL)
546        self.sizer0 = wx.BoxSizer(wx.VERTICAL)
547        self.sizer1 = wx.BoxSizer(wx.VERTICAL)
548        self.sizer2 = wx.BoxSizer(wx.VERTICAL)
549        self.sizer3 = wx.BoxSizer(wx.VERTICAL)
550        self.sizer4 = wx.BoxSizer(wx.VERTICAL)
551        self.sizer5 = wx.BoxSizer(wx.VERTICAL)
552        self.sizer6 = wx.BoxSizer(wx.VERTICAL)
553
554        self.sizer0.SetMinSize((PANEL_WIDTH, -1))
555        self.sizer1.SetMinSize((PANEL_WIDTH, -1))
556        self.sizer2.SetMinSize((PANEL_WIDTH, -1))
557        self.sizer3.SetMinSize((PANEL_WIDTH, -1))
558        self.sizer4.SetMinSize((PANEL_WIDTH, -1))
559        self.sizer5.SetMinSize((PANEL_WIDTH, -1))
560        self.sizer6.SetMinSize((PANEL_WIDTH, -1))
561
562        self.vbox.Add(self.sizer0)
563        self.vbox.Add(self.sizer1)
564        self.vbox.Add(self.sizer2)
565        self.vbox.Add(self.sizer3)
566        self.vbox.Add(self.sizer4)
567        self.vbox.Add(self.sizer5)
568        self.vbox.Add(self.sizer6)
569
570    def set_layout(self):
571        """
572        layout
573        """
574        self.vbox.Layout()
575        self.vbox.Fit(self)
576        self.SetSizer(self.vbox)
577        self.Centre()
578
579    def set_owner(self, owner):
580        """
581        set owner of fitpage
582
583        :param owner: the class responsible of plotting
584
585        """
586        self.event_owner = owner
587        self.state.event_owner = owner
588
589    def get_state(self):
590        """
591        """
592        return self.state
593
594    def get_data(self):
595        """
596        return the current data
597        """
598        return self.data
599
600    def get_data_list(self):
601        """
602        return the current data
603        """
604        return self.data_list
605
606    def set_manager(self, manager):
607        """
608        set panel manager
609
610        :param manager: instance of plugin fitting
611
612        """
613        self._manager = manager
614        self.state.manager = manager
615
616    def populate_box(self, model_dict):
617        """
618        Store list of model
619
620        :param model_dict: dictionary containing list of models
621
622        """
623        self.model_list_box = model_dict
624        self.state.model_list_box = self.model_list_box
625        self.initialize_combox()
626
627    def set_model_dictionary(self, model_dict):
628        """
629        Store a dictionary linking model name -> model object
630
631        :param model_dict: dictionary containing list of models
632        """
633        self.model_dict = model_dict
634
635    def initialize_combox(self):
636        """
637        put default value in the combobox
638        """
639        ## fill combox box
640        if self.model_list_box is None:
641            return
642        if len(self.model_list_box) > 0:
643        ## This is obsolete code since form factor box is no longer static.
644        ## It is now set dynamically through _show_combox and _show_combos_helper
645        ## These are called for first time by formfactor_combo_init
646        ## itself called from fitpanel only.  If we find that I'm wrong and
647        ## we DO need to initialize somehow here - do it by a call to
648        ## formfactor_combo_init
649        ## self.formfator_combo_init()
650        ## BUT NOT HERE -- make it last line of this
651        ## method so that structure box is populated before _show_comboox_helper
652        ## is called.  Otherwise wx will complain mightily:-)
653        ##
654        ## Also change the name to initiatlize_structurebox along with changes
655        ## to other combobox methods (_populate_listbox --> _populate_categorybox
656        ## etc )
657        ##
658        ##     PDB 4/26/2014
659#            self._populate_box(self.formfactorbox,
660#                               self.model_list_box["Shapes"])
661            self._populate_box(self.structurebox,
662                                self.model_list_box["Structure Factors"])
663            self.structurebox.Insert("None", 0, None)
664            self.structurebox.SetSelection(0)
665            self.structurebox.Hide()
666            self.text2.Hide()
667            self.structurebox.Disable()
668            self.text2.Disable()
669
670            if self.model.__class__ in self.model_list_box["P(Q)*S(Q)"]:
671                self.structurebox.Show()
672                self.text2.Show()
673                self.structurebox.Enable()
674                self.text2.Enable()
675
676
677    def set_dispers_sizer(self):
678        """
679        fill sizer containing dispersity info
680        """
681        self.sizer4.Clear(True)
682        name = "Polydispersity and Orientational Distribution"
683        box_description = wx.StaticBox(self, -1, name)
684        box_description.SetForegroundColour(wx.BLUE)
685        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
686        #----------------------------------------------------
687        self.disable_disp = wx.RadioButton(self, -1, 'Off', (10, 10),
688                                            style=wx.RB_GROUP)
689        self.enable_disp = wx.RadioButton(self, -1, 'On', (10, 30))
690        # best size for MAC and PC
691        if ON_MAC:
692            size_q = (30, 20)
693        else:
694            size_q = (20, 15)
695        self.disp_help_bt = wx.Button(self, wx.NewId(), '?',
696                                      style=wx.BU_EXACTFIT,
697                                      size=size_q)
698        self.disp_help_bt.Bind(wx.EVT_BUTTON,
699                        self.on_pd_help_clicked, id=self.disp_help_bt.GetId())
700        self.disp_help_bt.SetToolTipString("Helps for Polydispersion.")
701
702        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
703                     id=self.disable_disp.GetId())
704        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
705                   id=self.enable_disp.GetId())
706        #MAC needs SetValue
707        self.disable_disp.SetValue(True)
708        sizer_dispersion = wx.BoxSizer(wx.HORIZONTAL)
709        sizer_dispersion.Add((20, 20))
710        name = ""  # Polydispersity and \nOrientational Distribution "
711        sizer_dispersion.Add(wx.StaticText(self, -1, name))
712        sizer_dispersion.Add(self.enable_disp)
713        sizer_dispersion.Add((20, 20))
714        sizer_dispersion.Add(self.disable_disp)
715        sizer_dispersion.Add((25, 20))
716        sizer_dispersion.Add(self.disp_help_bt)
717
718        ## fill a sizer for dispersion
719        boxsizer1.Add(sizer_dispersion, 0,
720                wx.TOP | wx.BOTTOM | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE,
721                border=5)
722        self.sizer4_4 = wx.GridBagSizer(6, 5)
723
724        boxsizer1.Add(self.sizer4_4)
725        #-----------------------------------------------------
726        self.sizer4.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
727        self.sizer4_4.Layout()
728        self.sizer4.Layout()
729        self.Layout()
730
731        self.Refresh()
732        ## saving the state of enable dispersity button
733        self.state.enable_disp = self.enable_disp.GetValue()
734        self.state.disable_disp = self.disable_disp.GetValue()
735        self.SetupScrolling()
736
737    def onResetModel(self, event):
738        """
739        Reset model state
740        """
741        menu = event.GetEventObject()
742        ## post help message for the selected model
743        msg = menu.GetHelpString(event.GetId())
744        msg += " reloaded"
745        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
746        self.Show(False)
747        name = menu.GetLabel(event.GetId())
748        self._on_select_model_helper()
749        if self.model != None:
750            self.m_name = self.model.name
751        if name in self.saved_states.keys():
752            previous_state = self.saved_states[name]
753            ## reset state of checkbox,textcrtl  and  regular parameters value
754
755            self.reset_page(previous_state)
756        self.state.m_name = self.m_name
757        self.Show(True)
758
759    def on_preview(self, event):
760        """
761        Report the current fit results
762        """
763        # Get plot image from plotpanel
764        images, canvases = self.get_images()
765        # get the report dialog
766        self.state.report(images, canvases)
767
768    def on_save(self, event):
769        """
770        Save the current state into file
771        """
772        self.save_current_state()
773        new_state = self.state.clone()
774        # Ask the user the location of the file to write to.
775        path = None
776        if self.parent != None:
777            self._default_save_location = \
778                        self._manager.parent._default_save_location
779        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location,
780                                        self.window_caption, "*.fitv", wx.SAVE)
781
782        if dlg.ShowModal() == wx.ID_OK:
783            path = dlg.GetPath()
784            self._default_save_location = os.path.dirname(path)
785            self._manager.parent._default_save_location = \
786                                 self._default_save_location
787        else:
788            return None
789        # MAC always needs the extension for saving
790        extens = ".fitv"
791        # Make sure the ext included in the file name
792        fName = os.path.splitext(path)[0] + extens
793        #the manager write the state into file
794        self._manager.save_fit_state(filepath=fName, fitstate=new_state)
795        return new_state
796
797    def on_copy(self, event):
798        """
799        Copy Parameter values to the clipboad
800        """
801        if event != None:
802            event.Skip()
803        # It seems MAC needs wxCallAfter
804        if event.GetId() == GUIFRAME_ID.COPYEX_ID:
805            print "copy excel"
806            wx.CallAfter(self.get_copy_excel)
807        elif event.GetId() == GUIFRAME_ID.COPYLAT_ID:
808            print "copy latex"
809            wx.CallAfter(self.get_copy_latex)
810        else:
811            wx.CallAfter(self.get_copy)
812
813
814    def on_paste(self, event):
815        """
816        Paste Parameter values to the panel if possible
817        """
818        #if event != None:
819        #    event.Skip()
820        # It seems MAC needs wxCallAfter for the setvalues
821        # for multiple textctrl items, otherwise it tends to crash once a while
822        wx.CallAfter(self.get_paste)
823        # messages depending on the flag
824        #self._copy_info(True)
825
826    def _copy_info(self, flag):
827        """
828        Send event dpemding on flag
829
830        : Param flag: flag that distinguish event
831        """
832        # messages depending on the flag
833        if flag == None:
834            msg = " Parameter values are copied to the clipboard..."
835            infor = 'warning'
836        elif flag:
837            msg = " Parameter values are pasted from the clipboard..."
838            infor = "warning"
839        else:
840            msg = "Error occurred: "
841            msg += "No valid parameter values to paste from the clipboard..."
842            infor = "warning"
843        # inform msg to wx
844        wx.PostEvent(self._manager.parent,
845                    StatusEvent(status=msg, info=infor))
846
847    def _get_time_stamp(self):
848        """
849        return time and date stings
850        """
851        # date and time
852        year, month, day, hour, minute, second, _, _, _ = time.localtime()
853        current_time = str(hour) + ":" + str(minute) + ":" + str(second)
854        current_date = str(month) + "/" + str(day) + "/" + str(year)
855        return current_time, current_date
856
857    def on_bookmark(self, event):
858        """
859        save history of the data and model
860        """
861        if self.model == None:
862            msg = "Can not bookmark; Please select Data and Model first..."
863            wx.MessageBox(msg, 'Info')
864            return
865        self.save_current_state()
866        new_state = self.state.clone()
867        ##Add model state on context menu
868        self.number_saved_state += 1
869        current_time, current_date = self._get_time_stamp()
870        #name= self.model.name+"[%g]"%self.number_saved_state
871        name = "Fitting: %g]" % self.number_saved_state
872        name += self.model.__class__.__name__
873        name += "bookmarked at %s on %s" % (current_time, current_date)
874        self.saved_states[name] = new_state
875
876        ## Add item in the context menu
877        msg = "Model saved at %s on %s" % (current_time, current_date)
878        ## post help message for the selected model
879        msg += " Saved! right click on this page to retrieve this model"
880        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
881
882        id = wx.NewId()
883        self.popUpMenu.Append(id, name, str(msg))
884        wx.EVT_MENU(self, id, self.onResetModel)
885        wx.PostEvent(self._manager.parent,
886                     AppendBookmarkEvent(title=name,
887                                         hint=str(msg),
888                                         handler=self._back_to_bookmark))
889
890    def _back_to_bookmark(self, event):
891        """
892        Back to bookmark
893        """
894        self._manager.on_perspective(event)
895        self.onResetModel(event)
896        self._draw_model()
897
898    def onSetFocus(self, evt):
899        """
900        highlight the current textcrtl and hide the error text control shown
901        after fitting
902        """
903        return
904
905    def read_file(self, path):
906        """
907        Read two columns file
908
909        :param path: the path to the file to read
910
911        """
912        try:
913            if path == None:
914                wx.PostEvent(self._manager.parent,
915                            StatusEvent(status=\
916                            " Selected Distribution was not loaded: %s" % path))
917                return None, None
918            input_f = open(path, 'r')
919            buff = input_f.read()
920            lines = buff.split('\n')
921            input_f.close()
922            angles = []
923            weights = []
924            for line in lines:
925                toks = line.split()
926                try:
927                    angle = float(toks[0])
928                    weight = float(toks[1])
929                    angles.append(angle)
930                    weights.append(weight)
931                except:
932                    # Skip non-data lines
933                    logging.error(sys.exc_value)
934            return numpy.array(angles), numpy.array(weights)
935        except:
936            raise
937
938    def createMemento(self):
939        """
940        return the current state of the page
941        """
942        return self.state.clone()
943
944    def save_current_state(self):
945        """
946        Store current state
947        """
948        self.state.engine_type = copy.deepcopy(self.engine_type)
949        ## save model option
950        if self.model != None:
951            self.disp_list = self.model.getDispParamList()
952            self.state.disp_list = copy.deepcopy(self.disp_list)
953            self.state.model = self.model.clone()
954
955            #model combobox: complex code because of mac's silent error
956            if self.structurebox != None:
957                if self.structurebox.IsShown():
958                    self.state.structurecombobox = 'None'
959                    s_select = self.structurebox.GetSelection()
960                    if s_select > 0:
961                        self.state.structurecombobox = self.structurebox.\
962                        GetString(s_select)
963            if self.formfactorbox != None:
964                f_select = self.formfactorbox.GetSelection()
965                if f_select > 0:
966                    self.state.formfactorcombobox = self.formfactorbox.\
967                    GetString(f_select)
968        if self.categorybox != None:
969            cb_select = self.categorybox.GetSelection()
970            if cb_select > 0:
971                self.state.categorycombobox = self.categorybox.\
972                GetString(cb_select)
973
974        self.state.enable2D = copy.deepcopy(self.enable2D)
975        self.state.values = copy.deepcopy(self.values)
976        self.state.weights = copy.deepcopy(self.weights)
977        ## save data
978        self.state.data = copy.deepcopy(self.data)
979        self.state.qmax_x = self.qmax_x
980        self.state.qmin_x = self.qmin_x
981        self.state.dI_noweight = copy.deepcopy(self.dI_noweight.GetValue())
982        self.state.dI_didata = copy.deepcopy(self.dI_didata.GetValue())
983        self.state.dI_sqrdata = copy.deepcopy(self.dI_sqrdata.GetValue())
984        self.state.dI_idata = copy.deepcopy(self.dI_idata.GetValue())
985        self.state.dq_l = self.dq_l
986        self.state.dq_r = self.dq_r
987        if hasattr(self, "enable_disp"):
988            self.state.enable_disp = self.enable_disp.GetValue()
989            self.state.disable_disp = self.disable_disp.GetValue()
990
991        self.state.smearer = copy.deepcopy(self.current_smearer)
992        if hasattr(self, "enable_smearer"):
993            self.state.enable_smearer = \
994                                copy.deepcopy(self.enable_smearer.GetValue())
995            self.state.disable_smearer = \
996                                copy.deepcopy(self.disable_smearer.GetValue())
997
998        self.state.pinhole_smearer = \
999                                copy.deepcopy(self.pinhole_smearer.GetValue())
1000        self.state.dx_max = copy.deepcopy(self.dx_max)
1001        self.state.dx_min = copy.deepcopy(self.dx_min)
1002        self.state.dxl = copy.deepcopy(self.dxl)
1003        self.state.dxw = copy.deepcopy(self.dxw)
1004        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())
1005
1006        if len(self._disp_obj_dict) > 0:
1007            for k, v in self._disp_obj_dict.iteritems():
1008                self.state._disp_obj_dict[k] = v
1009
1010            self.state.values = copy.deepcopy(self.values)
1011            self.state.weights = copy.deepcopy(self.weights)
1012        ## save plotting range
1013        self._save_plotting_range()
1014
1015        self.state.orientation_params = []
1016        self.state.orientation_params_disp = []
1017        self.state.parameters = []
1018        self.state.fittable_param = []
1019        self.state.fixed_param = []
1020        self.state.str_parameters = []
1021
1022        ## save checkbutton state and txtcrtl values
1023        self._copy_parameters_state(self.str_parameters,
1024                                    self.state.str_parameters)
1025        self._copy_parameters_state(self.orientation_params,
1026                                     self.state.orientation_params)
1027        self._copy_parameters_state(self.orientation_params_disp,
1028                                     self.state.orientation_params_disp)
1029
1030        self._copy_parameters_state(self.parameters, self.state.parameters)
1031        self._copy_parameters_state(self.fittable_param,
1032                                     self.state.fittable_param)
1033        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
1034        #save chisqr
1035        self.state.tcChi = self.tcChi.GetValue()
1036
1037    def save_current_state_fit(self):
1038        """
1039        Store current state for fit_page
1040        """
1041        ## save model option
1042        if self.model != None:
1043            self.disp_list = self.model.getDispParamList()
1044            self.state.disp_list = copy.deepcopy(self.disp_list)
1045            self.state.model = self.model.clone()
1046        if hasattr(self, "engine_type"):
1047            self.state.engine_type = copy.deepcopy(self.engine_type)
1048
1049        self.state.enable2D = copy.deepcopy(self.enable2D)
1050        self.state.values = copy.deepcopy(self.values)
1051        self.state.weights = copy.deepcopy(self.weights)
1052        ## save data
1053        self.state.data = copy.deepcopy(self.data)
1054
1055        if hasattr(self, "enable_disp"):
1056            self.state.enable_disp = self.enable_disp.GetValue()
1057            self.state.disable_disp = self.disable_disp.GetValue()
1058
1059        self.state.smearer = copy.deepcopy(self.current_smearer)
1060        if hasattr(self, "enable_smearer"):
1061            self.state.enable_smearer = \
1062                                copy.deepcopy(self.enable_smearer.GetValue())
1063            self.state.disable_smearer = \
1064                                copy.deepcopy(self.disable_smearer.GetValue())
1065
1066        self.state.pinhole_smearer = \
1067                                copy.deepcopy(self.pinhole_smearer.GetValue())
1068        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())
1069        self.state.dI_noweight = copy.deepcopy(self.dI_noweight.GetValue())
1070        self.state.dI_didata = copy.deepcopy(self.dI_didata.GetValue())
1071        self.state.dI_sqrdata = copy.deepcopy(self.dI_sqrdata.GetValue())
1072        self.state.dI_idata = copy.deepcopy(self.dI_idata.GetValue())
1073        if hasattr(self, "disp_box") and self.disp_box != None:
1074            self.state.disp_box = self.disp_box.GetCurrentSelection()
1075
1076            if len(self.disp_cb_dict) > 0:
1077                for k, v in self.disp_cb_dict.iteritems():
1078                    if v == None:
1079                        self.state.disp_cb_dict[k] = v
1080                    else:
1081                        try:
1082                            self.state.disp_cb_dict[k] = v.GetValue()
1083                        except:
1084                            self.state.disp_cb_dict[k] = None
1085            if len(self._disp_obj_dict) > 0:
1086                for k, v in self._disp_obj_dict.iteritems():
1087                    self.state._disp_obj_dict[k] = v
1088
1089            self.state.values = copy.deepcopy(self.values)
1090            self.state.weights = copy.deepcopy(self.weights)
1091
1092        ## save plotting range
1093        self._save_plotting_range()
1094
1095        ## save checkbutton state and txtcrtl values
1096        self._copy_parameters_state(self.orientation_params,
1097                                     self.state.orientation_params)
1098        self._copy_parameters_state(self.orientation_params_disp,
1099                                     self.state.orientation_params_disp)
1100        self._copy_parameters_state(self.parameters, self.state.parameters)
1101        self._copy_parameters_state(self.fittable_param,
1102                                             self.state.fittable_param)
1103        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
1104
1105    def check_invalid_panel(self):
1106        """
1107        check if the user can already perform some action with this panel
1108        """
1109        if self.data is None:
1110            self.disable_smearer.SetValue(True)
1111            self.disable_disp.SetValue(True)
1112            msg = "Please load Data and select Model to start..."
1113            wx.MessageBox(msg, 'Info')
1114            return  True
1115
1116    def set_model_state(self, state):
1117        """
1118        reset page given a model state
1119        """
1120        self.disp_cb_dict = state.disp_cb_dict
1121        self.disp_list = state.disp_list
1122
1123        ## set the state of the radio box
1124        #self.shape_rbutton.SetValue(state.shape_rbutton)
1125        #self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
1126        #self.struct_rbutton.SetValue(state.struct_rbutton)
1127        #self.plugin_rbutton.SetValue(state.plugin_rbutton)
1128
1129        ## fill model combobox
1130        self._show_combox_helper()
1131        #select the current model
1132        try:
1133            # to support older version
1134            category_pos = int(state.categorycombobox)
1135        except:
1136            category_pos = 0
1137            for ind_cat in range(self.categorybox.GetCount()):
1138                if self.categorycombobox.GetString(ind_form) == \
1139                                        state.categorycombobox:
1140                    category_pos = int(ind_cat)
1141                    break
1142
1143        self.categorybox.Select(category_pos)
1144        try:
1145            # to support older version
1146            formfactor_pos = int(state.formfactorcombobox)
1147        except:
1148            formfactor_pos = 0
1149            for ind_form in range(self.formfactorbox.GetCount()):
1150                if self.formfactorbox.GetString(ind_form) == \
1151                                        state.formfactorcombobox:
1152                    formfactor_pos = int(ind_form)
1153                    break
1154
1155        self.formfactorbox.Select(formfactor_pos)
1156
1157        try:
1158            # to support older version
1159            structfactor_pos = int(state.structurecombobox)
1160        except:
1161            structfactor_pos = 0
1162            for ind_struct in range(self.structurebox.GetCount()):
1163                if self.structurebox.GetString(ind_struct) == \
1164                                        state.structurecombobox:
1165                    structfactor_pos = int(ind_struct)
1166                    break
1167
1168        self.structurebox.SetSelection(structfactor_pos)
1169
1170        if state.multi_factor != None:
1171            self.multifactorbox.SetSelection(state.multi_factor)
1172
1173        ## reset state of checkbox,textcrtl  and  regular parameters value
1174        self._reset_parameters_state(self.orientation_params_disp,
1175                                     state.orientation_params_disp)
1176        self._reset_parameters_state(self.orientation_params,
1177                                     state.orientation_params)
1178        self._reset_parameters_state(self.str_parameters,
1179                                     state.str_parameters)
1180        self._reset_parameters_state(self.parameters, state.parameters)
1181        ## display dispersion info layer
1182        self.enable_disp.SetValue(state.enable_disp)
1183        self.disable_disp.SetValue(state.disable_disp)
1184
1185        if hasattr(self, "disp_box") and self.disp_box != None:
1186            self.disp_box.SetSelection(state.disp_box)
1187            n = self.disp_box.GetCurrentSelection()
1188            dispersity = self.disp_box.GetClientData(n)
1189            name = dispersity.__name__
1190
1191            self._set_dipers_Param(event=None)
1192
1193            if name == "ArrayDispersion":
1194
1195                for item in self.disp_cb_dict.keys():
1196
1197                    if hasattr(self.disp_cb_dict[item], "SetValue"):
1198                        self.disp_cb_dict[item].SetValue(\
1199                                                    state.disp_cb_dict[item])
1200                        # Create the dispersion objects
1201                        from sas.models.dispersion_models import ArrayDispersion
1202                        disp_model = ArrayDispersion()
1203                        if hasattr(state, "values") and \
1204                                 self.disp_cb_dict[item].GetValue() == True:
1205                            if len(state.values) > 0:
1206                                self.values = state.values
1207                                self.weights = state.weights
1208                                disp_model.set_weights(self.values,
1209                                                       state.weights)
1210                            else:
1211                                self._reset_dispersity()
1212
1213                        self._disp_obj_dict[item] = disp_model
1214                        # Set the new model as the dispersion object
1215                        #for the selected parameter
1216                        self.model.set_dispersion(item, disp_model)
1217
1218                        self.model._persistency_dict[item] = \
1219                                                [state.values, state.weights]
1220
1221            else:
1222                keys = self.model.getParamList()
1223                for item in keys:
1224                    if item in self.disp_list and \
1225                        not item in self.model.details:
1226                        self.model.details[item] = ["", None, None]
1227                self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1228                self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1229        ## smearing info  restore
1230        if hasattr(self, "enable_smearer"):
1231            ## set smearing value whether or not the data
1232            #contain the smearing info
1233            self.enable_smearer.SetValue(state.enable_smearer)
1234            self.disable_smearer.SetValue(state.disable_smearer)
1235            self.onSmear(event=None)
1236        self.pinhole_smearer.SetValue(state.pinhole_smearer)
1237        self.slit_smearer.SetValue(state.slit_smearer)
1238
1239        self.dI_noweight.SetValue(state.dI_noweight)
1240        self.dI_didata.SetValue(state.dI_didata)
1241        self.dI_sqrdata.SetValue(state.dI_sqrdata)
1242        self.dI_idata.SetValue(state.dI_idata)
1243
1244        ## we have two more options for smearing
1245        if self.pinhole_smearer.GetValue():
1246            self.onPinholeSmear(event=None)
1247        elif self.slit_smearer.GetValue():
1248            self.onSlitSmear(event=None)
1249
1250        ## reset state of checkbox,textcrtl  and dispersity parameters value
1251        self._reset_parameters_state(self.fittable_param, state.fittable_param)
1252        self._reset_parameters_state(self.fixed_param, state.fixed_param)
1253
1254        ## draw the model with previous parameters value
1255        self._onparamEnter_helper()
1256        self.select_param(event=None)
1257        #Save state_fit
1258        self.save_current_state_fit()
1259        self._lay_out()
1260        self.Refresh()
1261
1262    def reset_page_helper(self, state):
1263        """
1264        Use page_state and change the state of existing page
1265
1266        :precondition: the page is already drawn or created
1267
1268        :postcondition: the state of the underlying data change as well as the
1269            state of the graphic interface
1270        """
1271        if state == None:
1272            return
1273        # set data, etc. from the state
1274        # reset page between theory and fitting from bookmarking
1275        #if state.data == None:
1276        #    data = None
1277        #else:
1278        data = state.data
1279
1280        if data == None:
1281            data_min = state.qmin
1282            data_max = state.qmax
1283            self.qmin_x = data_min
1284            self.qmax_x = data_max
1285            self.qmin.SetValue(str(data_min))
1286            self.qmax.SetValue(str(data_max))
1287
1288            self.state.data = data
1289            self.state.qmin = self.qmin_x
1290            self.state.qmax = self.qmax_x
1291        else:
1292            self.set_data(data)
1293
1294        self.enable2D = state.enable2D
1295        try:
1296            self.magnetic_on = state.magnetic_on
1297        except:
1298            # Backward compatibility (for older state files)
1299            self.magnetic_on = False
1300        self.engine_type = state.engine_type
1301
1302        self.disp_cb_dict = state.disp_cb_dict
1303        self.disp_list = state.disp_list
1304
1305        ## set the state of the radio box
1306        #self.shape_rbutton.SetValue(state.shape_rbutton)
1307        #self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
1308        #self.struct_rbutton.SetValue(state.struct_rbutton)
1309        #self.plugin_rbutton.SetValue(state.plugin_rbutton)
1310
1311        ## fill model combobox
1312        self._show_combox_helper()
1313        #select the current model
1314        try:
1315            # to support older version
1316            category_pos = int(state.categorycombobox)
1317        except:
1318            category_pos = 0
1319            for ind_cat in range(self.categorybox.GetCount()):
1320                if self.categorybox.GetString(ind_cat) == \
1321                                        state.categorycombobox:
1322                    category_pos = int(ind_cat)
1323                    break
1324
1325        self.categorybox.Select(category_pos)
1326        self._show_combox(None)
1327        try:
1328            # to support older version
1329            formfactor_pos = int(state.formfactorcombobox)
1330        except:
1331            formfactor_pos = 0
1332            for ind_form in range(self.formfactorbox.GetCount()):
1333                if self.formfactorbox.GetString(ind_form) == \
1334                                                    (state.formfactorcombobox):
1335                    formfactor_pos = int(ind_form)
1336                    break
1337
1338        self.formfactorbox.Select(formfactor_pos)
1339
1340        try:
1341            # to support older version
1342            structfactor_pos = int(state.structurecombobox)
1343        except:
1344            structfactor_pos = 0
1345            for ind_struct in range(self.structurebox.GetCount()):
1346                if self.structurebox.GetString(ind_struct) == \
1347                                                    (state.structurecombobox):
1348                    structfactor_pos = int(ind_struct)
1349                    break
1350
1351        self.structurebox.SetSelection(structfactor_pos)
1352
1353        if state.multi_factor != None:
1354            self.multifactorbox.SetSelection(state.multi_factor)
1355
1356        #reset the fitting engine type
1357        self.engine_type = state.engine_type
1358        #draw the pnael according to the new model parameter
1359        self._on_select_model(event=None)
1360
1361        # take care of 2D button
1362        if data == None and self.model_view.IsEnabled():
1363            if self.enable2D:
1364                self.model_view.SetLabel("2D Mode")
1365            else:
1366                self.model_view.SetLabel("1D Mode")
1367        # else:
1368
1369        if self._manager != None and self.engine_type != None:
1370            self._manager._on_change_engine(engine=self.engine_type)
1371        ## set the select all check box to the a given state
1372        self.cb1.SetValue(state.cb1)
1373
1374        ## reset state of checkbox,textcrtl  and  regular parameters value
1375        self._reset_parameters_state(self.orientation_params_disp,
1376                                     state.orientation_params_disp)
1377        self._reset_parameters_state(self.orientation_params,
1378                                     state.orientation_params)
1379        self._reset_parameters_state(self.str_parameters,
1380                                     state.str_parameters)
1381        self._reset_parameters_state(self.parameters, state.parameters)
1382        ## display dispersion info layer
1383        self.enable_disp.SetValue(state.enable_disp)
1384        self.disable_disp.SetValue(state.disable_disp)
1385        # If the polydispersion is ON
1386        if state.enable_disp:
1387            # reset dispersion according the state
1388            self._set_dipers_Param(event=None)
1389            self._reset_page_disp_helper(state)
1390        ##plotting range restore
1391        self._reset_plotting_range(state)
1392        ## smearing info  restore
1393        if hasattr(self, "enable_smearer"):
1394            ## set smearing value whether or not the data
1395            #contain the smearing info
1396            self.enable_smearer.SetValue(state.enable_smearer)
1397            self.disable_smearer.SetValue(state.disable_smearer)
1398            self.onSmear(event=None)
1399        self.pinhole_smearer.SetValue(state.pinhole_smearer)
1400        self.slit_smearer.SetValue(state.slit_smearer)
1401        try:
1402            self.dI_noweight.SetValue(state.dI_noweight)
1403            self.dI_didata.SetValue(state.dI_didata)
1404            self.dI_sqrdata.SetValue(state.dI_sqrdata)
1405            self.dI_idata.SetValue(state.dI_idata)
1406        except:
1407            # to support older state file formats
1408            self.dI_noweight.SetValue(False)
1409            self.dI_didata.SetValue(True)
1410            self.dI_sqrdata.SetValue(False)
1411            self.dI_idata.SetValue(False)
1412
1413        ## we have two more options for smearing
1414        if self.pinhole_smearer.GetValue():
1415            self.dx_min = state.dx_min
1416            self.dx_max = state.dx_max
1417            if self.dx_min != None:
1418                self.smear_pinhole_min.SetValue(str(self.dx_min))
1419            if self.dx_max != None:
1420                self.smear_pinhole_max.SetValue(str(self.dx_max))
1421            self.onPinholeSmear(event=None)
1422        elif self.slit_smearer.GetValue():
1423            self.dxl = state.dxl
1424            self.dxw = state.dxw
1425            if self.dxl != None:
1426                self.smear_slit_height.SetValue(str(self.dxl))
1427            if self.dxw != None:
1428                self.smear_slit_width.SetValue(str(self.dxw))
1429            else:
1430                self.smear_slit_width.SetValue('')
1431            self.onSlitSmear(event=None)
1432
1433        ## reset state of checkbox,textcrtl  and dispersity parameters value
1434        self._reset_parameters_state(self.fittable_param, state.fittable_param)
1435        self._reset_parameters_state(self.fixed_param, state.fixed_param)
1436
1437        ## draw the model with previous parameters value
1438        self._onparamEnter_helper()
1439        #reset the value of chisqr when not consistent with the value computed
1440        self.tcChi.SetValue(str(self.state.tcChi))
1441        ## reset context menu items
1442        self._reset_context_menu()
1443
1444        ## set the value of the current state to the state given as parameter
1445        self.state = state.clone()
1446        self.state.m_name = self.m_name
1447
1448    def _reset_page_disp_helper(self, state):
1449        """
1450        Help to rest page for dispersions
1451        """
1452        keys = self.model.getParamList()
1453        for item in keys:
1454            if item in self.disp_list and \
1455                not item in self.model.details:
1456                self.model.details[item] = ["", None, None]
1457        #for k,v in self.state.disp_cb_dict.iteritems():
1458        self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1459        self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1460        self.values = copy.deepcopy(state.values)
1461        self.weights = copy.deepcopy(state.weights)
1462
1463        for key, disp in state._disp_obj_dict.iteritems():
1464            # From saved file, disp_model can not be sent in model obj.
1465            # it will be sent as a string here, then converted to model object.
1466            if disp.__class__.__name__ == 'str':
1467                disp_model = None
1468                com_str = "from sas.models.dispersion_models "
1469                com_str += "import %s as disp_func \ndisp_model = disp_func()"
1470                exec com_str % disp
1471            else:
1472                disp_model = disp
1473            self._disp_obj_dict[key] = disp_model
1474            param_name = key.split('.')[0]
1475            # Try to set dispersion only when available
1476            # for eg., pass the orient. angles for 1D Cal
1477            try:
1478                self.model.set_dispersion(param_name, disp_model)
1479                self.model._persistency_dict[key] = \
1480                                 [state.values, state.weights]
1481            except:
1482                logging.error(sys.exc_value)
1483            selection = self._find_polyfunc_selection(disp_model)
1484            for list in self.fittable_param:
1485                if list[1] == key and list[7] != None:
1486                    list[7].SetSelection(selection)
1487                    # For the array disp_model, set the values and weights
1488                    if selection == 1:
1489                        disp_model.set_weights(self.values[key],
1490                                               self.weights[key])
1491                        try:
1492                            # Diables all fittable params for array
1493                            list[0].SetValue(False)
1494                            list[0].Disable()
1495                            list[2].Disable()
1496                            list[5].Disable()
1497                            list[6].Disable()
1498                        except:
1499                            logging.error(sys.exc_value)
1500            # For array, disable all fixed params
1501            if selection == 1:
1502                for item in self.fixed_param:
1503                    if item[1].split(".")[0] == key.split(".")[0]:
1504                        # try it and pass it for the orientation for 1D
1505                        try:
1506                            item[2].Disable()
1507                        except:
1508                            logging.error(sys.exc_value)
1509
1510        # Make sure the check box updated when all checked
1511        if self.cb1.GetValue():
1512            self.select_all_param(None)
1513
1514    def _selectDlg(self):
1515        """
1516        open a dialog file to selected the customized dispersity
1517        """
1518        if self.parent != None:
1519            self._default_save_location = \
1520                        self._manager.parent.get_save_location()
1521        dlg = wx.FileDialog(self, "Choose a weight file",
1522                                self._default_save_location, "",
1523                                "*.*", wx.OPEN)
1524        path = None
1525        if dlg.ShowModal() == wx.ID_OK:
1526            path = dlg.GetPath()
1527        dlg.Destroy()
1528        return path
1529
1530    def _reset_context_menu(self):
1531        """
1532        reset the context menu
1533        """
1534        for name, _ in self.state.saved_states.iteritems():
1535            self.number_saved_state += 1
1536            ## Add item in the context menu
1537            id = wx.NewId()
1538            msg = 'Save model and state %g' % self.number_saved_state
1539            self.popUpMenu.Append(id, name, msg)
1540            wx.EVT_MENU(self, id, self.onResetModel)
1541
1542    def _reset_plotting_range(self, state):
1543        """
1544        Reset the plotting range to a given state
1545        """
1546        self.qmin.SetValue(str(state.qmin))
1547        self.qmax.SetValue(str(state.qmax))
1548
1549    def _save_typeOfmodel(self):
1550        """
1551        save radiobutton containing the type model that can be selected
1552        """
1553        #self.state.shape_rbutton = self.shape_rbutton.GetValue()
1554        #self.state.shape_indep_rbutton = self.shape_indep_rbutton.GetValue()
1555        #self.state.struct_rbutton = self.struct_rbutton.GetValue()
1556        #self.state.plugin_rbutton = self.plugin_rbutton.GetValue()
1557        self.state.structurecombobox = self.structurebox.GetLabel()
1558        self.state.formfactorcombobox = self.formfactorbox.GetLabel()
1559        self.state.categorycombobox = self.categorybox.GetLabel()
1560
1561        ## post state to fit panel
1562        event = PageInfoEvent(page=self)
1563        wx.PostEvent(self.parent, event)
1564
1565    def _save_plotting_range(self):
1566        """
1567        save the state of plotting range
1568        """
1569        self.state.qmin = self.qmin_x
1570        self.state.qmax = self.qmax_x
1571        self.state.npts = self.npts_x
1572
1573    def _onparamEnter_helper(self):
1574        """
1575        check if values entered by the user are changed and valid to replot
1576        model
1577        """
1578        # Flag to register when a parameter has changed.
1579        is_modified = False
1580        self.fitrange = True
1581        is_2Ddata = False
1582        #self._undo.Enable(True)
1583        # check if 2d data
1584        if self.data.__class__.__name__ == "Data2D":
1585            is_2Ddata = True
1586        if self.model != None:
1587            try:
1588                is_modified = self._check_value_enter(self.fittable_param,
1589                                                     is_modified)
1590                is_modified = self._check_value_enter(self.fixed_param,
1591                                                      is_modified)
1592                is_modified = self._check_value_enter(self.parameters,
1593                                                      is_modified)
1594            except:
1595                logging.error(sys.exc_value)
1596
1597            # Here we should check whether the boundaries have been modified.
1598            # If qmin and qmax have been modified, update qmin and qmax and
1599            # set the is_modified flag to True
1600            if self._validate_qrange(self.qmin, self.qmax):
1601                tempmin = float(self.qmin.GetValue())
1602                if tempmin != self.qmin_x:
1603                    self.qmin_x = tempmin
1604                    is_modified = True
1605                tempmax = float(self.qmax.GetValue())
1606                if tempmax != self.qmax_x:
1607                    self.qmax_x = tempmax
1608                    is_modified = True
1609
1610                if is_2Ddata:
1611                    # set mask
1612                    is_modified = self._validate_Npts()
1613
1614            else:
1615                self.fitrange = False
1616
1617            if not self.data.is_data:
1618                is_modified = True
1619
1620            ## if any value is modify draw model with new value
1621            if not self.fitrange:
1622                #self.btFit.Disable()
1623                if is_2Ddata:
1624                    self.btEditMask.Disable()
1625            else:
1626                if is_2Ddata and self.data.is_data and not self.batch_on:
1627                    self.btEditMask.Enable(True)
1628            if is_modified and self.fitrange:
1629                # Theory case: need to get npts value to draw
1630                self.npts_x = float(self.Npts_total.GetValue())
1631                self.create_default_data()
1632                self.state_change = True
1633                self._draw_model()
1634                self.Refresh()
1635        return is_modified
1636
1637    def _update_paramv_on_fit(self):
1638        """
1639        make sure that update param values just before the fitting
1640        """
1641        #flag for qmin qmax check values
1642        flag = True
1643        self.fitrange = True
1644        is_modified = False
1645
1646        #wx.PostEvent(self._manager.parent, StatusEvent(status=" \
1647        #updating ... ",type="update"))
1648
1649        ##So make sure that update param values on_Fit.
1650        #self._undo.Enable(True)
1651        if self.model != None:
1652            if self.Npts_total.GetValue() != self.Npts_fit.GetValue():
1653                if not self.data.is_data:
1654                    self._manager.page_finder[self.uid].set_fit_data(data=\
1655                                                                [self.data])
1656            ##Check the values
1657            self._check_value_enter(self.fittable_param, is_modified)
1658            self._check_value_enter(self.fixed_param, is_modified)
1659            self._check_value_enter(self.parameters, is_modified)
1660
1661            # If qmin and qmax have been modified, update qmin and qmax and
1662            # Here we should check whether the boundaries have been modified.
1663            # If qmin and qmax have been modified, update qmin and qmax and
1664            # set the is_modified flag to True
1665            self.fitrange = self._validate_qrange(self.qmin, self.qmax)
1666            if self.fitrange:
1667                tempmin = float(self.qmin.GetValue())
1668                if tempmin != self.qmin_x:
1669                    self.qmin_x = tempmin
1670                tempmax = float(self.qmax.GetValue())
1671                if tempmax != self.qmax_x:
1672                    self.qmax_x = tempmax
1673                if tempmax == tempmin:
1674                    flag = False
1675                temp_smearer = None
1676                if not self.disable_smearer.GetValue():
1677                    temp_smearer = self.current_smearer
1678                    if self.slit_smearer.GetValue():
1679                        flag = self.update_slit_smear()
1680                    elif self.pinhole_smearer.GetValue():
1681                        flag = self.update_pinhole_smear()
1682                    else:
1683                        self._manager.set_smearer(smearer=temp_smearer,
1684                                                  uid=self.uid,
1685                                                  fid=self.data.id,
1686                                                  qmin=float(self.qmin_x),
1687                                                  qmax=float(self.qmax_x),
1688                            enable_smearer=not self.disable_smearer.GetValue(),
1689                                                      draw=False)
1690                elif not self._is_2D():
1691                    self._manager.set_smearer(smearer=temp_smearer,
1692                                              qmin=float(self.qmin_x),
1693                                              uid=self.uid,
1694                                              fid=self.data.id,
1695                                              qmax=float(self.qmax_x),
1696                            enable_smearer=not self.disable_smearer.GetValue(),
1697                                                 draw=False)
1698                    if self.data != None:
1699                        index_data = ((self.qmin_x <= self.data.x) & \
1700                                      (self.data.x <= self.qmax_x))
1701                        val = str(len(self.data.x[index_data == True]))
1702                        self.Npts_fit.SetValue(val)
1703                    else:
1704                        # No data in the panel
1705                        try:
1706                            self.npts_x = float(self.Npts_total.GetValue())
1707                        except:
1708                            flag = False
1709                            return flag
1710                    flag = True
1711                if self._is_2D():
1712                    # only 2D case set mask
1713                    flag = self._validate_Npts()
1714                    if not flag:
1715                        return flag
1716            else:
1717                flag = False
1718        else:
1719            flag = False
1720
1721        #For invalid q range, disable the mask editor and fit button, vs.
1722        if not self.fitrange:
1723            if self._is_2D():
1724                self.btEditMask.Disable()
1725        else:
1726            if self._is_2D() and  self.data.is_data and not self.batch_on:
1727                self.btEditMask.Enable(True)
1728
1729        if not flag:
1730            msg = "Cannot Plot or Fit :Must select a "
1731            msg += " model or Fitting range is not valid!!!  "
1732            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1733
1734        try:
1735            self.save_current_state()
1736        except:
1737            logging.error(sys.exc_value)
1738
1739        return flag
1740
1741    def _is_modified(self, is_modified):
1742        """
1743        return to self._is_modified
1744        """
1745        return is_modified
1746
1747    def _reset_parameters_state(self, listtorestore, statelist):
1748        """
1749        Reset the parameters at the given state
1750        """
1751        if len(statelist) == 0 or len(listtorestore) == 0:
1752            return
1753        if len(statelist) != len(listtorestore):
1754            return
1755
1756        for j in range(len(listtorestore)):
1757            item_page = listtorestore[j]
1758            item_page_info = statelist[j]
1759            ##change the state of the check box for simple parameters
1760            if item_page[0] != None:
1761                item_page[0].SetValue(item_page_info[0])
1762            if item_page[2] != None:
1763                item_page[2].SetValue(item_page_info[2])
1764                if item_page[2].__class__.__name__ == "ComboBox":
1765                    if item_page_info[2] in self.model.fun_list:
1766                        fun_val = self.model.fun_list[item_page_info[2]]
1767                        self.model.setParam(item_page_info[1], fun_val)
1768            if item_page[3] != None:
1769                ## show or hide text +/-
1770                if item_page_info[2]:
1771                    item_page[3].Show(True)
1772                else:
1773                    item_page[3].Hide()
1774            if item_page[4] != None:
1775                ## show of hide the text crtl for fitting error
1776                if item_page_info[4][0]:
1777                    item_page[4].Show(True)
1778                    item_page[4].SetValue(item_page_info[4][1])
1779                else:
1780                    item_page[3].Hide()
1781            if item_page[5] != None:
1782                ## show of hide the text crtl for fitting error
1783                item_page[5].Show(item_page_info[5][0])
1784                item_page[5].SetValue(item_page_info[5][1])
1785
1786            if item_page[6] != None:
1787                ## show of hide the text crtl for fitting error
1788                item_page[6].Show(item_page_info[6][0])
1789                item_page[6].SetValue(item_page_info[6][1])
1790
1791    def _reset_strparam_state(self, listtorestore, statelist):
1792        """
1793        Reset the string parameters at the given state
1794        """
1795        if len(statelist) == 0:
1796            return
1797
1798        listtorestore = copy.deepcopy(statelist)
1799
1800        for j in range(len(listtorestore)):
1801            item_page = listtorestore[j]
1802            item_page_info = statelist[j]
1803            ##change the state of the check box for simple parameters
1804
1805            if item_page[0] != None:
1806                item_page[0].SetValue(format_number(item_page_info[0], True))
1807
1808            if item_page[2] != None:
1809                param_name = item_page_info[1]
1810                value = item_page_info[2]
1811                selection = value
1812                if value in self.model.fun_list:
1813                    selection = self.model.fun_list[value]
1814                item_page[2].SetValue(selection)
1815                self.model.setParam(param_name, selection)
1816
1817    def _copy_parameters_state(self, listtocopy, statelist):
1818        """
1819        copy the state of button
1820
1821        :param listtocopy: the list of check button to copy
1822        :param statelist: list of state object to store the current state
1823
1824        """
1825        if len(listtocopy) == 0:
1826            return
1827
1828        for item in listtocopy:
1829
1830            checkbox_state = None
1831            if item[0] != None:
1832                checkbox_state = item[0].GetValue()
1833            parameter_name = item[1]
1834            parameter_value = None
1835            if item[2] != None:
1836                parameter_value = item[2].GetValue()
1837            static_text = None
1838            if item[3] != None:
1839                static_text = item[3].IsShown()
1840            error_value = None
1841            error_state = None
1842            if item[4] != None:
1843                error_value = item[4].GetValue()
1844                error_state = item[4].IsShown()
1845
1846            min_value = None
1847            min_state = None
1848            if item[5] != None:
1849                min_value = item[5].GetValue()
1850                min_state = item[5].IsShown()
1851
1852            max_value = None
1853            max_state = None
1854            if item[6] != None:
1855                max_value = item[6].GetValue()
1856                max_state = item[6].IsShown()
1857            unit = None
1858            if item[7] != None:
1859                unit = item[7].GetLabel()
1860
1861            statelist.append([checkbox_state, parameter_name, parameter_value,
1862                              static_text, [error_state, error_value],
1863                              [min_state, min_value],
1864                              [max_state, max_value], unit])
1865
1866
1867    def _draw_model(self, update_chisqr=True, source='model'):
1868        """
1869        Method to draw or refresh a plotted model.
1870        The method will use the data member from the model page
1871        to build a call to the fitting perspective manager.
1872
1873        :param chisqr: update chisqr value [bool]
1874        """
1875        wx.CallAfter(self._draw_model_after, update_chisqr, source)
1876
1877    def _draw_model_after(self, update_chisqr=True, source='model'):
1878        """
1879        Method to draw or refresh a plotted model.
1880        The method will use the data member from the model page
1881        to build a call to the fitting perspective manager.
1882
1883        :param chisqr: update chisqr value [bool]
1884        """
1885        #if self.check_invalid_panel():
1886        #    return
1887        if self.model != None:
1888            temp_smear = None
1889            if hasattr(self, "enable_smearer"):
1890                if not self.disable_smearer.GetValue():
1891                    temp_smear = self.current_smearer
1892            # compute weight for the current data
1893            from sas.perspectives.fitting.utils import get_weight
1894            flag = self.get_weight_flag()
1895            weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
1896            toggle_mode_on = self.model_view.IsEnabled()
1897            is_2d = self._is_2D()
1898            self._manager.draw_model(self.model,
1899                                    data=self.data,
1900                                    smearer=temp_smear,
1901                                    qmin=float(self.qmin_x),
1902                                    qmax=float(self.qmax_x),
1903                                    page_id=self.uid,
1904                                    toggle_mode_on=toggle_mode_on,
1905                                    state=self.state,
1906                                    enable2D=is_2d,
1907                                    update_chisqr=update_chisqr,
1908                                    source='model',
1909                                    weight=weight)
1910
1911    def _on_show_sld(self, event=None):
1912        """
1913        Plot SLD profile
1914        """
1915        # get profile data
1916        x, y = self.model.getProfile()
1917
1918        from sas.plottools import Data1D as pf_data1d
1919        #from sas.perspectives.theory.profile_dialog import SLDPanel
1920        from sas.guiframe.local_perspectives.plotting.profile_dialog \
1921        import SLDPanel
1922        sld_data = pf_data1d(x, y)
1923        sld_data.name = 'SLD'
1924        sld_data.axes = self.sld_axes
1925        self.panel = SLDPanel(self, data=sld_data, axes=self.sld_axes, id= -1)
1926        self.panel.ShowModal()
1927
1928    def _set_multfactor_combobox(self, multiplicity=10):
1929        """
1930        Set comboBox for muitfactor of CoreMultiShellModel
1931        :param multiplicit: no. of multi-functionality
1932        """
1933        # build content of the combobox
1934        for idx in range(0, multiplicity):
1935            self.multifactorbox.Append(str(idx), int(idx))
1936        self._hide_multfactor_combobox()
1937
1938    def _show_multfactor_combobox(self):
1939        """
1940        Show the comboBox of muitfactor of CoreMultiShellModel
1941        """
1942        if not self.mutifactor_text.IsShown():
1943            self.mutifactor_text.Show(True)
1944            self.mutifactor_text1.Show(True)
1945        if not self.multifactorbox.IsShown():
1946            self.multifactorbox.Show(True)
1947
1948    def _hide_multfactor_combobox(self):
1949        """
1950        Hide the comboBox of muitfactor of CoreMultiShellModel
1951        """
1952        if self.mutifactor_text.IsShown():
1953            self.mutifactor_text.Hide()
1954            self.mutifactor_text1.Hide()
1955        if self.multifactorbox.IsShown():
1956            self.multifactorbox.Hide()
1957
1958    def formfactor_combo_init(self):
1959        """
1960        First time calls _show_combox_helper
1961        """
1962        self._show_combox(None)
1963
1964    def _show_combox_helper(self):
1965        """
1966        Fill panel's combo box according to the type of model selected
1967        """
1968        custom_model = 'Customized Models'
1969        mod_cat = self.categorybox.GetStringSelection()
1970        self.structurebox.SetSelection(0)
1971        self.structurebox.Disable()
1972        self.formfactorbox.Clear()
1973        if mod_cat == None:
1974            return
1975        m_list = []
1976        try:
1977            if mod_cat == custom_model:
1978                for model in self.model_list_box[mod_cat]:
1979                    str_m = str(model).split(".")[0]
1980                    #self.model_box.Append(str_m)
1981                    m_list.append(self.model_dict[str_m])
1982            else:
1983                cat_dic = self.master_category_dict[mod_cat]
1984                for (model, enabled) in cat_dic:
1985                    if enabled:
1986                        m_list.append(self.model_dict[model])
1987                    #else:
1988                    #    msg = "This model is disabled by Category Manager."
1989                    #    wx.PostEvent(self.parent.parent,
1990                    #                 StatusEvent(status=msg, info="error"))
1991        except:
1992            msg = "%s\n" % (sys.exc_value)
1993            wx.PostEvent(self._manager.parent,
1994                         StatusEvent(status=msg, info="error"))
1995        self._populate_box(self.formfactorbox, m_list)
1996
1997    def _on_modify_cat(self, event=None):
1998        self._manager.parent.on_category_panel(event)
1999
2000    def _show_combox(self, event=None):
2001        """
2002        Show combox box associate with type of model selected
2003        """
2004        self.Show(False)
2005        self._show_combox_helper()
2006        self._on_select_model(event=None)
2007        self.Show(True)
2008        self._save_typeOfmodel()
2009        self.sizer4_4.Layout()
2010        self.sizer4.Layout()
2011        self.Layout()
2012        self.Refresh()
2013
2014    def _populate_box(self, combobox, list):
2015        """
2016        fill combox box with dict item
2017
2018        :param list: contains item to fill the combox
2019            item must model class
2020        """
2021        mlist = []
2022        for models in list:
2023            model = models()
2024            name = model.__class__.__name__
2025            if models.__name__ != "NoStructure":
2026                if hasattr(model, "name"):
2027                    name = model.name
2028                mlist.append((name, models))
2029
2030        # Sort the models
2031        mlist_sorted = sorted(mlist)
2032        for item in mlist_sorted:
2033            combobox.Append(item[0], item[1])
2034        return 0
2035
2036    def _onQrangeEnter(self, event):
2037        """
2038        Check validity of value enter in the Q range field
2039
2040        """
2041        tcrtl = event.GetEventObject()
2042        #Clear msg if previously shown.
2043        msg = ""
2044        wx.PostEvent(self.parent, StatusEvent(status=msg))
2045        # Flag to register when a parameter has changed.
2046        #is_modified = False
2047        if tcrtl.GetValue().lstrip().rstrip() != "":
2048            try:
2049                float(tcrtl.GetValue())
2050                tcrtl.SetBackgroundColour(wx.WHITE)
2051                # If qmin and qmax have been modified, update qmin and qmax
2052                if self._validate_qrange(self.qmin, self.qmax):
2053                    tempmin = float(self.qmin.GetValue())
2054                    if tempmin != self.qmin_x:
2055                        self.qmin_x = tempmin
2056                    tempmax = float(self.qmax.GetValue())
2057                    if tempmax != self.qmax_x:
2058                        self.qmax_x = tempmax
2059                else:
2060                    tcrtl.SetBackgroundColour("pink")
2061                    msg = "Model Error: wrong value entered: %s" % sys.exc_value
2062                    wx.PostEvent(self.parent, StatusEvent(status=msg))
2063                    return
2064            except:
2065                tcrtl.SetBackgroundColour("pink")
2066                msg = "Model Error: wrong value entered: %s" % sys.exc_value
2067                wx.PostEvent(self.parent, StatusEvent(status=msg))
2068                return
2069            #Check if # of points for theory model are valid(>0).
2070            if self.npts != None:
2071                if check_float(self.npts):
2072                    temp_npts = float(self.npts.GetValue())
2073                    if temp_npts != self.num_points:
2074                        self.num_points = temp_npts
2075                        #is_modified = True
2076                else:
2077                    msg = "Cannot plot: No points in Q range!!!  "
2078                    wx.PostEvent(self.parent, StatusEvent(status=msg))
2079        else:
2080            tcrtl.SetBackgroundColour("pink")
2081            msg = "Model Error: wrong value entered!!!"
2082            wx.PostEvent(self.parent, StatusEvent(status=msg))
2083        self.save_current_state()
2084        event = PageInfoEvent(page=self)
2085        wx.PostEvent(self.parent, event)
2086        self.state_change = False
2087        #Draw the model for a different range
2088        if not self.data.is_data:
2089            self.create_default_data()
2090        self._draw_model()
2091
2092    def _theory_qrange_enter(self, event):
2093        """
2094        Check validity of value enter in the Q range field
2095        """
2096
2097        tcrtl = event.GetEventObject()
2098        #Clear msg if previously shown.
2099        msg = ""
2100        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2101        # Flag to register when a parameter has changed.
2102        is_modified = False
2103        if tcrtl.GetValue().lstrip().rstrip() != "":
2104            try:
2105                value = float(tcrtl.GetValue())
2106                tcrtl.SetBackgroundColour(wx.WHITE)
2107
2108                # If qmin and qmax have been modified, update qmin and qmax
2109                if self._validate_qrange(self.theory_qmin, self.theory_qmax):
2110                    tempmin = float(self.theory_qmin.GetValue())
2111                    if tempmin != self.theory_qmin_x:
2112                        self.theory_qmin_x = tempmin
2113                    tempmax = float(self.theory_qmax.GetValue())
2114                    if tempmax != self.qmax_x:
2115                        self.theory_qmax_x = tempmax
2116                else:
2117                    tcrtl.SetBackgroundColour("pink")
2118                    msg = "Model Error: wrong value entered: %s" % sys.exc_value
2119                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2120                    return
2121            except:
2122                tcrtl.SetBackgroundColour("pink")
2123                msg = "Model Error: wrong value entered: %s" % sys.exc_value
2124                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2125                return
2126            #Check if # of points for theory model are valid(>0).
2127            if self.Npts_total.IsEditable():
2128                if check_float(self.Npts_total):
2129                    temp_npts = float(self.Npts_total.GetValue())
2130                    if temp_npts != self.num_points:
2131                        self.num_points = temp_npts
2132                        is_modified = True
2133                else:
2134                    msg = "Cannot Plot: No points in Q range!!!  "
2135                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2136        else:
2137            tcrtl.SetBackgroundColour("pink")
2138            msg = "Model Error: wrong value entered!!!"
2139            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2140        self.save_current_state()
2141        event = PageInfoEvent(page=self)
2142        wx.PostEvent(self.parent, event)
2143        self.state_change = False
2144        #Draw the model for a different range
2145        self.create_default_data()
2146        self._draw_model()
2147
2148    def _on_select_model_helper(self):
2149        """
2150        call back for model selection
2151        """
2152        ## reset dictionary containing reference to dispersion
2153        self._disp_obj_dict = {}
2154        self.disp_cb_dict = {}
2155        self.temp_multi_functional = False
2156        f_id = self.formfactorbox.GetCurrentSelection()
2157        #For MAC
2158        form_factor = None
2159        if f_id >= 0:
2160            form_factor = self.formfactorbox.GetClientData(f_id)
2161
2162        if not form_factor in  self.model_list_box["multiplication"]:
2163            self.structurebox.Hide()
2164            self.text2.Hide()
2165            self.structurebox.Disable()
2166            self.structurebox.SetSelection(0)
2167            self.text2.Disable()
2168        else:
2169            self.structurebox.Show()
2170            self.text2.Show()
2171            self.structurebox.Enable()
2172            self.text2.Enable()
2173
2174        if form_factor != None:
2175            # set multifactor for Mutifunctional models
2176            if form_factor().__class__ in \
2177                                        self.model_list_box["Multi-Functions"]:
2178                m_id = self.multifactorbox.GetCurrentSelection()
2179                multiplicity = form_factor().multiplicity_info[0]
2180                self.multifactorbox.Clear()
2181                self._set_multfactor_combobox(multiplicity)
2182                self._show_multfactor_combobox()
2183                #ToDo:  this info should be called directly from the model
2184                text = form_factor().multiplicity_info[1]  # 'No. of Shells: '
2185
2186                self.mutifactor_text.SetLabel(text)
2187                if m_id > multiplicity - 1:
2188                    # default value
2189                    m_id = 1
2190
2191                self.multi_factor = self.multifactorbox.GetClientData(m_id)
2192                if self.multi_factor == None:
2193                    self.multi_factor = 0
2194                form_factor = form_factor(int(self.multi_factor))
2195                self.multifactorbox.SetSelection(m_id)
2196                # Check len of the text1 and max_multiplicity
2197                text = ''
2198                if form_factor.multiplicity_info[0] == \
2199                                        len(form_factor.multiplicity_info[2]):
2200                    text = form_factor.multiplicity_info[2][self.multi_factor]
2201                self.mutifactor_text1.SetLabel(text)
2202                # Check if model has  get sld profile.
2203                if len(form_factor.multiplicity_info[3]) > 0:
2204                    self.sld_axes = form_factor.multiplicity_info[3]
2205                    self.show_sld_button.Show(True)
2206                else:
2207                    self.sld_axes = ""
2208
2209            else:
2210                self._hide_multfactor_combobox()
2211                self.show_sld_button.Hide()
2212                form_factor = form_factor()
2213                self.multi_factor = None
2214        else:
2215            self._hide_multfactor_combobox()
2216            self.show_sld_button.Hide()
2217            self.multi_factor = None
2218
2219        s_id = self.structurebox.GetCurrentSelection()
2220        struct_factor = self.structurebox.GetClientData(s_id)
2221
2222        if  struct_factor != None:
2223            from sas.models.MultiplicationModel import MultiplicationModel
2224            self.model = MultiplicationModel(form_factor, struct_factor())
2225            # multifunctional form factor
2226            if len(form_factor.non_fittable) > 0:
2227                self.temp_multi_functional = True
2228        else:
2229            if form_factor != None:
2230                self.model = form_factor
2231            else:
2232                self.model = None
2233                return self.model
2234        # check if model has magnetic parameters
2235        if len(self.model.magnetic_params) > 0:
2236            self._has_magnetic = True
2237        else:
2238            self._has_magnetic = False
2239        ## post state to fit panel
2240        self.state.parameters = []
2241        self.state.model = self.model
2242        self.state.qmin = self.qmin_x
2243        self.state.multi_factor = self.multi_factor
2244        self.disp_list = self.model.getDispParamList()
2245        self.state.disp_list = self.disp_list
2246        self.on_set_focus(None)
2247        self.Layout()
2248
2249    def _validate_qrange(self, qmin_ctrl, qmax_ctrl):
2250        """
2251        Verify that the Q range controls have valid values
2252        and that Qmin < Qmax.
2253
2254        :param qmin_ctrl: text control for Qmin
2255        :param qmax_ctrl: text control for Qmax
2256
2257        :return: True is the Q range is value, False otherwise
2258
2259        """
2260        qmin_validity = check_float(qmin_ctrl)
2261        qmax_validity = check_float(qmax_ctrl)
2262        if not (qmin_validity and qmax_validity):
2263            return False
2264        else:
2265            qmin = float(qmin_ctrl.GetValue())
2266            qmax = float(qmax_ctrl.GetValue())
2267            if qmin < qmax:
2268                #Make sure to set both colours white.
2269                qmin_ctrl.SetBackgroundColour(wx.WHITE)
2270                qmin_ctrl.Refresh()
2271                qmax_ctrl.SetBackgroundColour(wx.WHITE)
2272                qmax_ctrl.Refresh()
2273            else:
2274                qmin_ctrl.SetBackgroundColour("pink")
2275                qmin_ctrl.Refresh()
2276                qmax_ctrl.SetBackgroundColour("pink")
2277                qmax_ctrl.Refresh()
2278                msg = "Invalid Q range: Q min must be smaller than Q max"
2279                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2280                return False
2281        return True
2282
2283    def _validate_Npts(self):
2284        """
2285        Validate the number of points for fitting is more than 10 points.
2286        If valid, setvalues Npts_fit otherwise post msg.
2287        """
2288        #default flag
2289        flag = True
2290        # Theory
2291        if self.data == None and self.enable2D:
2292            return flag
2293        for data in self.data_list:
2294            # q value from qx and qy
2295            radius = numpy.sqrt(data.qx_data * data.qx_data +
2296                                data.qy_data * data.qy_data)
2297            #get unmasked index
2298            index_data = (float(self.qmin.GetValue()) <= radius) & \
2299                            (radius <= float(self.qmax.GetValue()))
2300            index_data = (index_data) & (data.mask)
2301            index_data = (index_data) & (numpy.isfinite(data.data))
2302
2303            if len(index_data[index_data]) < 10:
2304                # change the color pink.
2305                self.qmin.SetBackgroundColour("pink")
2306                self.qmin.Refresh()
2307                self.qmax.SetBackgroundColour("pink")
2308                self.qmax.Refresh()
2309                msg = "Data Error: "
2310                msg += "Too few points in %s." % data.name
2311                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2312                self.fitrange = False
2313                flag = False
2314            else:
2315                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
2316                self.fitrange = True
2317
2318        return flag
2319
2320    def _validate_Npts_1D(self):
2321        """
2322        Validate the number of points for fitting is more than 5 points.
2323        If valid, setvalues Npts_fit otherwise post msg.
2324        """
2325        #default flag
2326        flag = True
2327        # Theory
2328        if self.data == None:
2329            return flag
2330        for data in self.data_list:
2331            # q value from qx and qy
2332            radius = data.x
2333            #get unmasked index
2334            index_data = (float(self.qmin.GetValue()) <= radius) & \
2335                            (radius <= float(self.qmax.GetValue()))
2336            index_data = (index_data) & (numpy.isfinite(data.y))
2337
2338            if len(index_data[index_data]) < 5:
2339                # change the color pink.
2340                self.qmin.SetBackgroundColour("pink")
2341                self.qmin.Refresh()
2342                self.qmax.SetBackgroundColour("pink")
2343                self.qmax.Refresh()
2344                msg = "Data Error: "
2345                msg += "Too few points in %s." % data.name
2346                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2347                self.fitrange = False
2348                flag = False
2349            else:
2350                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
2351                self.fitrange = True
2352
2353        return flag
2354
2355    def _check_value_enter(self, list, modified):
2356        """
2357        :param list: model parameter and panel info
2358        :Note: each item of the list should be as follow:
2359            item=[check button state, parameter's name,
2360                paramater's value, string="+/-",
2361                parameter's error of fit,
2362                parameter's minimum value,
2363                parrameter's maximum value ,
2364                parameter's units]
2365        """
2366        is_modified = modified
2367        if len(list) == 0:
2368            return is_modified
2369        for item in list:
2370            #skip angle parameters for 1D
2371            if not self.enable2D:
2372                if item in self.orientation_params:
2373                    continue
2374            #try:
2375            name = str(item[1])
2376
2377            if string.find(name, ".npts") == -1 and \
2378                                        string.find(name, ".nsigmas") == -1:
2379                ## check model parameters range
2380                param_min = None
2381                param_max = None
2382
2383                ## check minimun value
2384                if item[5] != None and item[5] != "":
2385                    if item[5].GetValue().lstrip().rstrip() != "":
2386                        try:
2387                            param_min = float(item[5].GetValue())
2388                            if not self._validate_qrange(item[5], item[2]):
2389                                if numpy.isfinite(param_min):
2390                                    item[2].SetValue(format_number(param_min))
2391
2392                            item[5].SetBackgroundColour(wx.WHITE)
2393                            item[2].SetBackgroundColour(wx.WHITE)
2394
2395                        except:
2396                            msg = "Wrong fit parameter range entered"
2397                            wx.PostEvent(self._manager.parent,
2398                                         StatusEvent(status=msg))
2399                            raise ValueError, msg
2400                        is_modified = True
2401                ## check maximum value
2402                if item[6] != None and item[6] != "":
2403                    if item[6].GetValue().lstrip().rstrip() != "":
2404                        try:
2405                            param_max = float(item[6].GetValue())
2406                            if not self._validate_qrange(item[2], item[6]):
2407                                if numpy.isfinite(param_max):
2408                                    item[2].SetValue(format_number(param_max))
2409
2410                            item[6].SetBackgroundColour(wx.WHITE)
2411                            item[2].SetBackgroundColour(wx.WHITE)
2412                        except:
2413                            msg = "Wrong Fit parameter range entered "
2414                            wx.PostEvent(self._manager.parent,
2415                                         StatusEvent(status=msg))
2416                            raise ValueError, msg
2417                        is_modified = True
2418
2419                if param_min != None and param_max != None:
2420                    if not self._validate_qrange(item[5], item[6]):
2421                        msg = "Wrong Fit range entered for parameter "
2422                        msg += "name %s of model %s " % (name, self.model.name)
2423                        wx.PostEvent(self._manager.parent,
2424                                     StatusEvent(status=msg))
2425
2426                if name in self.model.details.keys():
2427                    self.model.details[name][1:3] = param_min, param_max
2428                    is_modified = True
2429                else:
2430                    self.model.details[name] = ["", param_min, param_max]
2431                    is_modified = True
2432            try:
2433                # Check if the textctr is enabled
2434                if item[2].IsEnabled():
2435                    value = float(item[2].GetValue())
2436                    item[2].SetBackgroundColour("white")
2437                    # If the value of the parameter has changed,
2438                    # +update the model and set the is_modified flag
2439                    if value != self.model.getParam(name) and \
2440                                                numpy.isfinite(value):
2441                        self.model.setParam(name, value)
2442            except:
2443                item[2].SetBackgroundColour("pink")
2444                msg = "Wrong Fit parameter value entered "
2445                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2446
2447        return is_modified
2448
2449    def _set_dipers_Param(self, event):
2450        """
2451        respond to self.enable_disp and self.disable_disp radio box.
2452        The dispersity object is reset inside the model into Gaussian.
2453        When the user select yes , this method display a combo box for
2454        more selection when the user selects No,the combo box disappears.
2455        Redraw the model with the default dispersity (Gaussian)
2456        """
2457        ## On selction if no model exists.
2458        if self.model == None:
2459            self.disable_disp.SetValue(True)
2460            msg = "Please select a Model first..."
2461            wx.MessageBox(msg, 'Info')
2462            wx.PostEvent(self._manager.parent,
2463                         StatusEvent(status="Polydispersion: %s" % msg))
2464            return
2465
2466        self._reset_dispersity()
2467
2468        if self.model == None:
2469            self.model_disp.Hide()
2470            self.sizer4_4.Clear(True)
2471            return
2472
2473        if self.enable_disp.GetValue():
2474            ## layout for model containing no dispersity parameters
2475
2476            self.disp_list = self.model.getDispParamList()
2477
2478            if len(self.disp_list) == 0 and len(self.disp_cb_dict) == 0:
2479                self._layout_sizer_noDipers()
2480            else:
2481                ## set gaussian sizer
2482                self._on_select_Disp(event=None)
2483        else:
2484            self.sizer4_4.Clear(True)
2485
2486        ## post state to fit panel
2487        self.save_current_state()
2488        if event != None:
2489            event = PageInfoEvent(page=self)
2490            wx.PostEvent(self.parent, event)
2491        #draw the model with the current dispersity
2492        self._draw_model()
2493        self.sizer4_4.Layout()
2494        self.sizer5.Layout()
2495        self.Layout()
2496        self.Refresh()
2497
2498    def _layout_sizer_noDipers(self):
2499        """
2500        Draw a sizer with no dispersity info
2501        """
2502        ix = 0
2503        iy = 1
2504        self.fittable_param = []
2505        self.fixed_param = []
2506        self.orientation_params_disp = []
2507
2508        self.sizer4_4.Clear(True)
2509        text = "No polydispersity available for this model"
2510        model_disp = wx.StaticText(self, -1, text)
2511        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
2512                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
2513        self.sizer4_4.Layout()
2514        self.sizer4.Layout()
2515
2516    def _reset_dispersity(self):
2517        """
2518        put gaussian dispersity into current model
2519        """
2520        if len(self.param_toFit) > 0:
2521            for item in self.fittable_param:
2522                if item in self.param_toFit:
2523                    self.param_toFit.remove(item)
2524
2525            for item in self.orientation_params_disp:
2526                if item in self.param_toFit:
2527                    self.param_toFit.remove(item)
2528
2529        self.fittable_param = []
2530        self.fixed_param = []
2531        self.orientation_params_disp = []
2532        self.values = {}
2533        self.weights = {}
2534
2535        from sas.models.dispersion_models import GaussianDispersion
2536        if len(self.disp_cb_dict) == 0:
2537            self.save_current_state()
2538            self.sizer4_4.Clear(True)
2539            self.Layout()
2540            return
2541        if (len(self.disp_cb_dict) > 0):
2542            for p in self.disp_cb_dict:
2543                # The parameter was un-selected.
2544                # Go back to Gaussian model (with 0 pts)
2545                disp_model = GaussianDispersion()
2546
2547                self._disp_obj_dict[p] = disp_model
2548                # Set the new model as the dispersion object
2549                # for the selected parameter
2550                try:
2551                    self.model.set_dispersion(p, disp_model)
2552                except:
2553                    logging.error(sys.exc_value)
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            logging.error(sys.exc_value)
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                logging.error(sys.exc_value)
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                logging.error(sys.exc_value)
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                logging.error(sys.exc_value)
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                    logging.error(sys.exc_value)
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                logging.error(sys.exc_value)
3480                print "Error in BasePage._paste_poly_help: %s" % sys.exc_value
3481
3482    def _set_disp_array_cb(self, item):
3483        """
3484        Set cb for array disp
3485        """
3486        item[0].SetValue(False)
3487        item[0].Enable(False)
3488        item[2].Enable(False)
3489        item[3].Show(False)
3490        item[4].Show(False)
3491        item[5].SetValue('')
3492        item[5].Enable(False)
3493        item[6].SetValue('')
3494        item[6].Enable(False)
3495
3496    def update_pinhole_smear(self):
3497        """
3498            Method to be called by sub-classes
3499            Moveit; This method doesn't belong here
3500        """
3501        print "BasicPage.update_pinhole_smear was called: skipping"
3502        return
3503
3504
3505
3506
3507    def _read_category_info(self):
3508        """
3509        Reads the categories in from file
3510        """
3511
3512        # # ILL mod starts here - July 2012 kieranrcampbell@gmail.com
3513        self.master_category_dict = defaultdict(list)
3514        self.by_model_dict = defaultdict(list)
3515        self.model_enabled_dict = defaultdict(bool)
3516
3517        try:
3518            categorization_file = CategoryInstaller.get_user_file()
3519            if not os.path.isfile(categorization_file):
3520                categorization_file = CategoryInstaller.get_default_file()
3521            cat_file = open(categorization_file, 'rb')
3522            self.master_category_dict = json.load(cat_file)
3523            self._regenerate_model_dict()
3524            cat_file.close()
3525
3526        except IOError:
3527            raise
3528            print 'Problem reading in category file.'
3529            print 'We even looked for it, made sure it was there.'
3530            print 'An existential crisis if there ever was one.'
3531
3532    def _regenerate_model_dict(self):
3533        """
3534        regenerates self.by_model_dict which has each model name as the
3535        key and the list of categories belonging to that model
3536        along with the enabled mapping
3537        """
3538
3539        self.by_model_dict = defaultdict(list)
3540        for category in self.master_category_dict:
3541            for (model, enabled) in self.master_category_dict[category]:
3542                self.by_model_dict[model].append(category)
3543                self.model_enabled_dict[model] = enabled
3544
3545    def _populate_listbox(self):
3546        """
3547        fills out the category list box
3548        """
3549        uncat_str = 'Customized Models'
3550        self._read_category_info()
3551
3552        self.categorybox.Clear()
3553        cat_list = sorted(self.master_category_dict.keys())
3554        if not uncat_str in cat_list:
3555            cat_list.append(uncat_str)
3556
3557        for category in cat_list:
3558            if category != '':
3559                self.categorybox.Append(category)
3560
3561        if self.categorybox.GetSelection() == wx.NOT_FOUND:
3562            self.categorybox.SetSelection(0)
3563        else:
3564            self.categorybox.SetSelection(\
3565                self.categorybox.GetSelection())
3566
3567        #self._on_change_cat(None)
3568
3569
3570    def _on_change_cat(self, event):
3571        """
3572        Callback for category change action
3573        """
3574        self.model_name = None
3575        category = self.categorybox.GetStringSelection()
3576        if category == None:
3577            return
3578        self.model_box.Clear()
3579
3580        if category == 'Customized Models':
3581            for model in self.model_list_box[category]:
3582                str_m = str(model).split(".")[0]
3583                self.model_box.Append(str_m)
3584
3585        else:
3586            for (model, enabled) in sorted(self.master_category_dict[category],
3587                                      key=lambda name: name[0]):
3588                if(enabled):
3589                    self.model_box.Append(model)
3590
3591
3592
3593
3594    def _fill_model_sizer(self, sizer):
3595        """
3596        fill sizer containing model info
3597        """
3598        ##Add model function Details button in fitpanel.
3599        ##The following 3 lines are for Mac. Let JHC know before modifying...
3600        title = "Model"
3601        self.formfactorbox = None
3602        self.multifactorbox = None
3603        self.mbox_description = wx.StaticBox(self, -1, str(title))
3604        boxsizer1 = wx.StaticBoxSizer(self.mbox_description, wx.VERTICAL)
3605        sizer_cat = wx.BoxSizer(wx.HORIZONTAL)
3606        self.mbox_description.SetForegroundColour(wx.RED)
3607        id = wx.NewId()
3608        self.model_func = wx.Button(self, id, 'Help', size=(80, 23))
3609        self.model_func.Bind(wx.EVT_BUTTON, self.on_function_help_clicked, id=id)
3610        self.model_func.SetToolTipString("Full Model Function Help")
3611        id = wx.NewId()
3612        self.model_help = wx.Button(self, id, 'Description', size=(80, 23))
3613        self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked, id=id)
3614        self.model_help.SetToolTipString("Short Model Function Description")
3615        id = wx.NewId()
3616        self.model_view = wx.Button(self, id, "Show 2D", size=(80, 23))
3617        self.model_view.Bind(wx.EVT_BUTTON, self._onModel2D, id=id)
3618        hint = "toggle view of model from 1D to 2D  or 2D to 1D"
3619        self.model_view.SetToolTipString(hint)
3620
3621        cat_set_box = wx.StaticBox(self, -1, 'Category')
3622        sizer_cat_box = wx.StaticBoxSizer(cat_set_box, wx.HORIZONTAL)
3623        sizer_cat_box.SetMinSize((200, 50))
3624        self.categorybox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
3625        self.categorybox.SetToolTip(wx.ToolTip("Select a Category/Type"))
3626        self._populate_listbox()
3627        wx.EVT_COMBOBOX(self.categorybox, -1, self._show_combox)
3628        #self.shape_rbutton = wx.RadioButton(self, -1, 'Shapes',
3629        #                                     style=wx.RB_GROUP)
3630        #self.shape_indep_rbutton = wx.RadioButton(self, -1,
3631        #                                          "Shape-Independent")
3632        #self.struct_rbutton = wx.RadioButton(self, -1, "Structure Factor ")
3633        #self.plugin_rbutton = wx.RadioButton(self, -1, "Uncategorized")
3634
3635        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3636        #                   id=self.shape_rbutton.GetId())
3637        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3638        #                    id=self.shape_indep_rbutton.GetId())
3639        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3640        #                    id=self.struct_rbutton.GetId())
3641        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3642        #                    id=self.plugin_rbutton.GetId())
3643        #MAC needs SetValue
3644
3645        show_cat_button = wx.Button(self, -1, "Modify")
3646        cat_tip = "Modify model categories \n"
3647        cat_tip += "(also accessible from the menu bar)."
3648        show_cat_button.SetToolTip(wx.ToolTip(cat_tip))
3649        show_cat_button.Bind(wx.EVT_BUTTON, self._on_modify_cat)
3650        sizer_cat_box.Add(self.categorybox, 1, wx.RIGHT, 3)
3651        sizer_cat_box.Add((10, 10))
3652        sizer_cat_box.Add(show_cat_button)
3653        #self.shape_rbutton.SetValue(True)
3654
3655        sizer_radiobutton = wx.GridSizer(2, 2, 5, 5)
3656
3657
3658        #sizer_radiobutton.Add(self.shape_rbutton)
3659        #sizer_radiobutton.Add(self.shape_indep_rbutton)
3660        sizer_radiobutton.Add((5, 5))
3661        sizer_radiobutton.Add(self.model_view, 1, wx.RIGHT, 5)
3662        #sizer_radiobutton.Add(self.plugin_rbutton)
3663        #sizer_radiobutton.Add(self.struct_rbutton)
3664#        sizer_radiobutton.Add((5,5))
3665        sizer_radiobutton.Add(self.model_help, 1, wx.RIGHT | wx.LEFT, 5)
3666#        sizer_radiobutton.Add((5,5))
3667        sizer_radiobutton.Add(self.model_func, 1, wx.RIGHT, 5)
3668        sizer_cat.Add(sizer_cat_box, 1, wx.LEFT, 2.5)
3669        sizer_cat.Add(sizer_radiobutton)
3670        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
3671        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
3672
3673        self.text1 = wx.StaticText(self, -1, "")
3674        self.text2 = wx.StaticText(self, -1, "P(Q)*S(Q)")
3675        self.mutifactor_text = wx.StaticText(self, -1, "No. of Shells: ")
3676        self.mutifactor_text1 = wx.StaticText(self, -1, "")
3677        self.show_sld_button = wx.Button(self, -1, "Show SLD Profile")
3678        self.show_sld_button.Bind(wx.EVT_BUTTON, self._on_show_sld)
3679
3680        self.formfactorbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
3681        self.formfactorbox.SetToolTip(wx.ToolTip("Select a Model"))
3682        if self.model != None:
3683            self.formfactorbox.SetValue(self.model.name)
3684        self.structurebox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
3685        self.multifactorbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
3686        self.initialize_combox()
3687        wx.EVT_COMBOBOX(self.formfactorbox, -1, self._on_select_model)
3688
3689        wx.EVT_COMBOBOX(self.structurebox, -1, self._on_select_model)
3690        wx.EVT_COMBOBOX(self.multifactorbox, -1, self._on_select_model)
3691        ## check model type to show sizer
3692        if self.model != None:
3693            print "_set_model_sizer_selection: disabled."
3694            #self._set_model_sizer_selection(self.model)
3695
3696        sizer_selection.Add(self.text1)
3697        sizer_selection.Add((10, 5))
3698        sizer_selection.Add(self.formfactorbox)
3699        sizer_selection.Add((5, 5))
3700        sizer_selection.Add(self.text2)
3701        sizer_selection.Add((5, 5))
3702        sizer_selection.Add(self.structurebox)
3703
3704        mutifactor_selection.Add((13, 5))
3705        mutifactor_selection.Add(self.mutifactor_text)
3706        mutifactor_selection.Add(self.multifactorbox)
3707        mutifactor_selection.Add((5, 5))
3708        mutifactor_selection.Add(self.mutifactor_text1)
3709        mutifactor_selection.Add((10, 5))
3710        mutifactor_selection.Add(self.show_sld_button)
3711
3712        boxsizer1.Add(sizer_cat)
3713        boxsizer1.Add((10, 10))
3714        boxsizer1.Add(sizer_selection)
3715        boxsizer1.Add((10, 10))
3716        boxsizer1.Add(mutifactor_selection)
3717
3718        self._set_multfactor_combobox()
3719        self.multifactorbox.SetSelection(1)
3720        self.show_sld_button.Hide()
3721        sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3722        sizer.Layout()
3723
3724    def on_smear_helper(self, update=False):
3725        """
3726        Help for onSmear if implemented
3727
3728        :param update: force or not to update
3729        """
3730    def reset_page(self, state, first=False):
3731        """
3732        reset the state  if implemented
3733        """
3734    def onSmear(self, event):
3735        """
3736        Create a smear object if implemented
3737        """
3738    def onPinholeSmear(self, event):
3739        """
3740        Create a custom pinhole smear object if implemented
3741        """
3742    def onSlitSmear(self, event):
3743        """
3744        Create a custom slit smear object if implemented
3745        """
3746    def update_slit_smear(self):
3747        """
3748        called by kill_focus on pinhole TextCntrl
3749        to update the changes if implemented
3750        """
3751    def select_param(self, event):
3752        """
3753        Select TextCtrl  checked if implemented
3754        """
3755    def set_data(self, data=None):
3756        """
3757        Sets data if implemented
3758        """
3759    def _is_2D(self):
3760        """
3761        Check if data_name is Data2D if implemented
3762        """
3763    def _on_select_model(self, event=None):
3764        """
3765        call back for model selection if implemented
3766        """
3767    def select_all_param(self, event):
3768        """
3769        set to true or false all checkBox if implemented
3770        """
3771    def get_weight_flag(self):
3772        """
3773        Get flag corresponding to a given weighting dI data if implemented
3774        """
3775    def _set_sizer_dispersion(self):
3776        """
3777        draw sizer for dispersity if implemented
3778        """
3779    def get_all_checked_params(self):
3780        """
3781        Found all parameters current check and add them to list of parameters
3782        to fit if implemented
3783        """
3784    def show_npts2fit(self):
3785        """
3786        setValue Npts for fitting if implemented
3787        """
3788    def _onModel2D(self, event):
3789        """
3790        toggle view of model from 1D to 2D  or 2D from 1D if implemented
3791        """
Note: See TracBrowser for help on using the repository browser.