source: sasview/src/sas/sasgui/perspectives/fitting/basepage.py @ 24f6f4a

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 24f6f4a was e28f34d, checked in by mathieu, 8 years ago

Allow structure factors for custom models. Fixes #664

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