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

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 b78707b0 was acf8e4a5, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

reference BumpsFit? directly and remove fit engine selection layer

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