source: sasview/fittingview/src/sans/perspectives/fitting/basepage.py @ f71625f

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 f71625f was f71625f, checked in by Jae Cho <jhjcho@…>, 12 years ago

more pylint clean ups 4

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