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

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

fixed compute update w/ no data set

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