source: sasview/src/sas/sasgui/perspectives/fitting/basepage.py @ 17531f8

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 17531f8 was 4ed199e, checked in by wojciech, 8 years ago

Removing an extra Iq evaluation when model is loaded

  • Property mode set to 100644
File size: 139.4 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            self._set_dipers_Param(event=None)
1032
1033            if name == "ArrayDispersion":
1034
1035                for item in self.disp_cb_dict.keys():
1036
1037                    if hasattr(self.disp_cb_dict[item], "SetValue"):
1038                        self.disp_cb_dict[item].SetValue(\
1039                                                    state.disp_cb_dict[item])
1040                        # Create the dispersion objects
1041                        disp_model = POLYDISPERSITY_MODELS['array']()
1042                        if hasattr(state, "values") and \
1043                                 self.disp_cb_dict[item].GetValue() == True:
1044                            if len(state.values) > 0:
1045                                self.values = state.values
1046                                self.weights = state.weights
1047                                disp_model.set_weights(self.values,
1048                                                       state.weights)
1049                            else:
1050                                self._reset_dispersity()
1051
1052                        self._disp_obj_dict[item] = disp_model
1053                        # Set the new model as the dispersion object
1054                        #for the selected parameter
1055                        self.model.set_dispersion(item, disp_model)
1056
1057                        self.model._persistency_dict[item] = \
1058                                                [state.values, state.weights]
1059
1060            else:
1061                keys = self.model.getParamList()
1062                for item in keys:
1063                    if item in self.disp_list and \
1064                        not item in self.model.details:
1065                        self.model.details[item] = ["", None, None]
1066                self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1067                self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1068        ## smearing info  restore
1069        if hasattr(self, "enable_smearer"):
1070            ## set smearing value whether or not the data
1071            #contain the smearing info
1072            self.enable_smearer.SetValue(state.enable_smearer)
1073            self.disable_smearer.SetValue(state.disable_smearer)
1074            self.onSmear(event=None)
1075        self.pinhole_smearer.SetValue(state.pinhole_smearer)
1076        self.slit_smearer.SetValue(state.slit_smearer)
1077
1078        self.dI_noweight.SetValue(state.dI_noweight)
1079        self.dI_didata.SetValue(state.dI_didata)
1080        self.dI_sqrdata.SetValue(state.dI_sqrdata)
1081        self.dI_idata.SetValue(state.dI_idata)
1082
1083        ## we have two more options for smearing
1084        if self.pinhole_smearer.GetValue():
1085            self.onPinholeSmear(event=None)
1086        elif self.slit_smearer.GetValue():
1087            self.onSlitSmear(event=None)
1088
1089        ## reset state of checkbox,textcrtl  and dispersity parameters value
1090        self._reset_parameters_state(self.fittable_param, state.fittable_param)
1091        self._reset_parameters_state(self.fixed_param, state.fixed_param)
1092
1093        ## draw the model with previous parameters value
1094        self._onparamEnter_helper()
1095        self.select_param(event=None)
1096        #Save state_fit
1097        self.save_current_state_fit()
1098        self._lay_out()
1099        self.Refresh()
1100
1101    def reset_page_helper(self, state):
1102        """
1103        Use page_state and change the state of existing page
1104
1105        :precondition: the page is already drawn or created
1106
1107        :postcondition: the state of the underlying data change as well as the
1108            state of the graphic interface
1109        """
1110        if state == None:
1111            return
1112        # set data, etc. from the state
1113        # reset page between theory and fitting from bookmarking
1114        data = state.data
1115
1116        if data == None:
1117            data_min = state.qmin
1118            data_max = state.qmax
1119            self.qmin_x = data_min
1120            self.qmax_x = data_max
1121            self.qmin.SetValue(str(data_min))
1122            self.qmax.SetValue(str(data_max))
1123
1124            self.state.data = data
1125            self.state.qmin = self.qmin_x
1126            self.state.qmax = self.qmax_x
1127        else:
1128            self.set_data(data)
1129
1130        self.enable2D = state.enable2D
1131        try:
1132            self.magnetic_on = state.magnetic_on
1133        except:
1134            # Backward compatibility (for older state files)
1135            self.magnetic_on = False
1136
1137        self.disp_cb_dict = state.disp_cb_dict
1138        self.disp_list = state.disp_list
1139
1140        ## fill model combobox
1141        self._show_combox_helper()
1142        #select the current model
1143        try:
1144            # to support older version
1145            category_pos = int(state.categorycombobox)
1146        except:
1147            category_pos = 0
1148            for ind_cat in range(self.categorybox.GetCount()):
1149                if self.categorybox.GetString(ind_cat) == \
1150                                        state.categorycombobox:
1151                    category_pos = int(ind_cat)
1152                    break
1153
1154        self.categorybox.Select(category_pos)
1155        self._show_combox(None)
1156        try:
1157            # to support older version
1158            formfactor_pos = int(state.formfactorcombobox)
1159        except:
1160            formfactor_pos = 0
1161            for ind_form in range(self.formfactorbox.GetCount()):
1162                if self.formfactorbox.GetString(ind_form) == \
1163                                                    (state.formfactorcombobox):
1164                    formfactor_pos = int(ind_form)
1165                    break
1166
1167        self.formfactorbox.Select(formfactor_pos)
1168
1169        try:
1170            # to support older version
1171            structfactor_pos = int(state.structurecombobox)
1172        except:
1173            structfactor_pos = 0
1174            for ind_struct in range(self.structurebox.GetCount()):
1175                if self.structurebox.GetString(ind_struct) == \
1176                                                    (state.structurecombobox):
1177                    structfactor_pos = int(ind_struct)
1178                    break
1179
1180        self.structurebox.SetSelection(structfactor_pos)
1181
1182        if state.multi_factor != None:
1183            self.multifactorbox.SetSelection(state.multi_factor)
1184
1185        #draw the panel according to the new model parameter
1186        self._on_select_model(event=None)
1187
1188        # take care of 2D button
1189        if data == None and self.model_view.IsEnabled():
1190            if self.enable2D:
1191                self.model_view.SetLabel("2D Mode")
1192            else:
1193                self.model_view.SetLabel("1D Mode")
1194
1195        ## reset state of checkbox,textcrtl  and  regular parameters value
1196        self._reset_parameters_state(self.orientation_params_disp,
1197                                     state.orientation_params_disp)
1198        self._reset_parameters_state(self.orientation_params,
1199                                     state.orientation_params)
1200        self._reset_parameters_state(self.str_parameters,
1201                                     state.str_parameters)
1202        self._reset_parameters_state(self.parameters, state.parameters)
1203        ## display dispersion info layer
1204        self.enable_disp.SetValue(state.enable_disp)
1205        self.disable_disp.SetValue(state.disable_disp)
1206        # If the polydispersion is ON
1207        if state.enable_disp:
1208            # reset dispersion according the state
1209            self._set_dipers_Param(event=None)
1210            self._reset_page_disp_helper(state)
1211        ##plotting range restore
1212        self._reset_plotting_range(state)
1213        ## smearing info  restore
1214        if hasattr(self, "enable_smearer"):
1215            ## set smearing value whether or not the data
1216            #contain the smearing info
1217            self.enable_smearer.SetValue(state.enable_smearer)
1218            self.disable_smearer.SetValue(state.disable_smearer)
1219            self.onSmear(event=None)
1220        self.pinhole_smearer.SetValue(state.pinhole_smearer)
1221        self.slit_smearer.SetValue(state.slit_smearer)
1222        try:
1223            self.dI_noweight.SetValue(state.dI_noweight)
1224            self.dI_didata.SetValue(state.dI_didata)
1225            self.dI_sqrdata.SetValue(state.dI_sqrdata)
1226            self.dI_idata.SetValue(state.dI_idata)
1227        except:
1228            # to support older state file formats
1229            self.dI_noweight.SetValue(False)
1230            self.dI_didata.SetValue(True)
1231            self.dI_sqrdata.SetValue(False)
1232            self.dI_idata.SetValue(False)
1233
1234        ## we have two more options for smearing
1235        if self.pinhole_smearer.GetValue():
1236            self.dx_min = state.dx_min
1237            self.dx_max = state.dx_max
1238            if self.dx_min != None:
1239                self.smear_pinhole_min.SetValue(str(self.dx_min))
1240            if self.dx_max != None:
1241                self.smear_pinhole_max.SetValue(str(self.dx_max))
1242            self.onPinholeSmear(event=None)
1243        elif self.slit_smearer.GetValue():
1244            self.dxl = state.dxl
1245            self.dxw = state.dxw
1246            if self.dxl != None:
1247                self.smear_slit_height.SetValue(str(self.dxl))
1248            if self.dxw != None:
1249                self.smear_slit_width.SetValue(str(self.dxw))
1250            else:
1251                self.smear_slit_width.SetValue('')
1252            self.onSlitSmear(event=None)
1253
1254        ## reset state of checkbox,textcrtl  and dispersity parameters value
1255        self._reset_parameters_state(self.fittable_param, state.fittable_param)
1256        self._reset_parameters_state(self.fixed_param, state.fixed_param)
1257
1258        ## draw the model with previous parameters value
1259        self._onparamEnter_helper()
1260        #reset the value of chisqr when not consistent with the value computed
1261        self.tcChi.SetValue(str(self.state.tcChi))
1262        ## reset context menu items
1263        self._reset_context_menu()
1264
1265        ## set the value of the current state to the state given as parameter
1266        self.state = state.clone()
1267        self.state.m_name = self.m_name
1268
1269    def _reset_page_disp_helper(self, state):
1270        """
1271        Help to rest page for dispersions
1272        """
1273        keys = self.model.getParamList()
1274        for item in keys:
1275            if item in self.disp_list and \
1276                not item in self.model.details:
1277                self.model.details[item] = ["", None, None]
1278        #for k,v in self.state.disp_cb_dict.iteritems():
1279        self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1280        self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1281        self.values = copy.deepcopy(state.values)
1282        self.weights = copy.deepcopy(state.weights)
1283
1284        for key, disp_type in state._disp_obj_dict.iteritems():
1285            #disp_model = disp
1286            disp_model = POLYDISPERSITY_MODELS[disp_type]()
1287            self._disp_obj_dict[key] = disp_model
1288            param_name = key.split('.')[0]
1289            # Try to set dispersion only when available
1290            # for eg., pass the orient. angles for 1D Cal
1291            try:
1292                self.model.set_dispersion(param_name, disp_model)
1293                self.model._persistency_dict[key] = \
1294                                 [state.values, state.weights]
1295            except Exception:
1296                logging.error(traceback.format_exc())
1297            selection = self._find_polyfunc_selection(disp_model)
1298            for list in self.fittable_param:
1299                if list[1] == key and list[7] != None:
1300                    list[7].SetSelection(selection)
1301                    # For the array disp_model, set the values and weights
1302                    if selection == 1:
1303                        disp_model.set_weights(self.values[key],
1304                                               self.weights[key])
1305                        try:
1306                            # Diables all fittable params for array
1307                            list[0].SetValue(False)
1308                            list[0].Disable()
1309                            list[2].Disable()
1310                            list[5].Disable()
1311                            list[6].Disable()
1312                        except Exception:
1313                            logging.error(traceback.format_exc())
1314            # For array, disable all fixed params
1315            if selection == 1:
1316                for item in self.fixed_param:
1317                    if item[1].split(".")[0] == key.split(".")[0]:
1318                        # try it and pass it for the orientation for 1D
1319                        try:
1320                            item[2].Disable()
1321                        except Exception:
1322                            logging.error(traceback.format_exc())
1323
1324    def _selectDlg(self):
1325        """
1326        open a dialog file to selected the customized dispersity
1327        """
1328        if self.parent != None:
1329            self._default_save_location = \
1330                        self._manager.parent.get_save_location()
1331        dlg = wx.FileDialog(self, "Choose a weight file",
1332                            self._default_save_location, "",
1333                            "*.*", wx.OPEN)
1334        path = None
1335        if dlg.ShowModal() == wx.ID_OK:
1336            path = dlg.GetPath()
1337        dlg.Destroy()
1338        return path
1339
1340    def _reset_context_menu(self):
1341        """
1342        reset the context menu
1343        """
1344        ids = iter(self._id_pool)  # Reusing ids for context menu
1345        for name, _ in self.state.saved_states.iteritems():
1346            self.number_saved_state += 1
1347            ## Add item in the context menu
1348            wx_id = ids.next()
1349            msg = 'Save model and state %g' % self.number_saved_state
1350            self.popUpMenu.Append(wx_id, name, msg)
1351            wx.EVT_MENU(self, wx_id, self.onResetModel)
1352
1353    def _reset_plotting_range(self, state):
1354        """
1355        Reset the plotting range to a given state
1356        """
1357        self.qmin.SetValue(str(state.qmin))
1358        self.qmax.SetValue(str(state.qmax))
1359
1360    def _save_typeOfmodel(self):
1361        """
1362        save radiobutton containing the type model that can be selected
1363        """
1364        #self.state.shape_rbutton = self.shape_rbutton.GetValue()
1365        #self.state.shape_indep_rbutton = self.shape_indep_rbutton.GetValue()
1366        #self.state.struct_rbutton = self.struct_rbutton.GetValue()
1367        #self.state.plugin_rbutton = self.plugin_rbutton.GetValue()
1368        self.state.structurecombobox = self.structurebox.GetLabel()
1369        self.state.formfactorcombobox = self.formfactorbox.GetLabel()
1370        self.state.categorycombobox = self.categorybox.GetLabel()
1371
1372        ## post state to fit panel
1373        event = PageInfoEvent(page=self)
1374        wx.PostEvent(self.parent, event)
1375
1376    def _save_plotting_range(self):
1377        """
1378        save the state of plotting range
1379        """
1380        self.state.qmin = self.qmin_x
1381        self.state.qmax = self.qmax_x
1382        self.state.npts = self.npts_x
1383
1384    def _onparamEnter_helper(self):
1385        """
1386        check if values entered by the user are changed and valid to replot
1387        model
1388        """
1389        # Flag to register when a parameter has changed.
1390        is_modified = False
1391        self.fitrange = True
1392        is_2Ddata = False
1393        #self._undo.Enable(True)
1394        # check if 2d data
1395        if self.data.__class__.__name__ == "Data2D":
1396            is_2Ddata = True
1397        if self.model != None:
1398            is_modified = (self._check_value_enter(self.fittable_param)
1399                           or self._check_value_enter(self.fixed_param)
1400                           or self._check_value_enter(self.parameters))
1401
1402            # Here we should check whether the boundaries have been modified.
1403            # If qmin and qmax have been modified, update qmin and qmax and
1404            # set the is_modified flag to True
1405            if self._validate_qrange(self.qmin, self.qmax):
1406                tempmin = float(self.qmin.GetValue())
1407                if tempmin != self.qmin_x:
1408                    self.qmin_x = tempmin
1409                    is_modified = True
1410                tempmax = float(self.qmax.GetValue())
1411                if tempmax != self.qmax_x:
1412                    self.qmax_x = tempmax
1413                    is_modified = True
1414
1415                if is_2Ddata:
1416                    # set mask
1417                    is_modified = self._validate_Npts()
1418
1419            else:
1420                self.fitrange = False
1421
1422            ## if any value is modify draw model with new value
1423            if not self.fitrange:
1424                #self.btFit.Disable()
1425                if is_2Ddata:
1426                    self.btEditMask.Disable()
1427            else:
1428                if is_2Ddata and self.data.is_data and not self.batch_on:
1429                    self.btEditMask.Enable(True)
1430            if is_modified and self.fitrange:
1431                # Theory case: need to get npts value to draw
1432                self.npts_x = float(self.Npts_total.GetValue())
1433                self.create_default_data()
1434                self.state_change = True
1435                self._draw_model()
1436                self.Refresh()
1437
1438        logging.info("is_modified flag set to %g",is_modified)
1439        return is_modified
1440
1441    def _update_paramv_on_fit(self):
1442        """
1443        make sure that update param values just before the fitting
1444        """
1445        #flag for qmin qmax check values
1446        flag = True
1447        self.fitrange = True
1448
1449        #wx.PostEvent(self._manager.parent, StatusEvent(status=" \
1450        #updating ... ",type="update"))
1451
1452        ##So make sure that update param values on_Fit.
1453        #self._undo.Enable(True)
1454        if self.model != None:
1455            if self.Npts_total.GetValue() != self.Npts_fit.GetValue():
1456                if not self.data.is_data:
1457                    self._manager.page_finder[self.uid].set_fit_data(data=\
1458                                                                [self.data])
1459            ##Check the values
1460            self._check_value_enter(self.fittable_param)
1461            self._check_value_enter(self.fixed_param)
1462            self._check_value_enter(self.parameters)
1463
1464            # If qmin and qmax have been modified, update qmin and qmax and
1465            # Here we should check whether the boundaries have been modified.
1466            # If qmin and qmax have been modified, update qmin and qmax and
1467            # set the is_modified flag to True
1468            self.fitrange = self._validate_qrange(self.qmin, self.qmax)
1469            if self.fitrange:
1470                tempmin = float(self.qmin.GetValue())
1471                if tempmin != self.qmin_x:
1472                    self.qmin_x = tempmin
1473                tempmax = float(self.qmax.GetValue())
1474                if tempmax != self.qmax_x:
1475                    self.qmax_x = tempmax
1476                if tempmax == tempmin:
1477                    flag = False
1478                temp_smearer = None
1479                if not self.disable_smearer.GetValue():
1480                    temp_smearer = self.current_smearer
1481                    if self.slit_smearer.GetValue():
1482                        flag = self.update_slit_smear()
1483                    elif self.pinhole_smearer.GetValue():
1484                        flag = self.update_pinhole_smear()
1485                    else:
1486                        enable_smearer = not self.disable_smearer.GetValue()
1487                        self._manager.set_smearer(smearer=temp_smearer,
1488                                                  uid=self.uid,
1489                                                  fid=self.data.id,
1490                                                  qmin=float(self.qmin_x),
1491                                                  qmax=float(self.qmax_x),
1492                                                  enable_smearer=enable_smearer,
1493                                                  draw=False)
1494                elif not self._is_2D():
1495                    enable_smearer = not self.disable_smearer.GetValue()
1496                    self._manager.set_smearer(smearer=temp_smearer,
1497                                              qmin=float(self.qmin_x),
1498                                              uid=self.uid,
1499                                              fid=self.data.id,
1500                                              qmax=float(self.qmax_x),
1501                                              enable_smearer=enable_smearer,
1502                                              draw=False)
1503                    if self.data != None:
1504                        index_data = ((self.qmin_x <= self.data.x) & \
1505                                      (self.data.x <= self.qmax_x))
1506                        val = str(len(self.data.x[index_data == True]))
1507                        self.Npts_fit.SetValue(val)
1508                    else:
1509                        # No data in the panel
1510                        try:
1511                            self.npts_x = float(self.Npts_total.GetValue())
1512                        except:
1513                            flag = False
1514                            return flag
1515                    flag = True
1516                if self._is_2D():
1517                    # only 2D case set mask
1518                    flag = self._validate_Npts()
1519                    if not flag:
1520                        return flag
1521            else:
1522                flag = False
1523        else:
1524            flag = False
1525
1526        #For invalid q range, disable the mask editor and fit button, vs.
1527        if not self.fitrange:
1528            if self._is_2D():
1529                self.btEditMask.Disable()
1530        else:
1531            if self._is_2D() and  self.data.is_data and not self.batch_on:
1532                self.btEditMask.Enable(True)
1533
1534        if not flag:
1535            msg = "Cannot Plot or Fit :Must select a "
1536            msg += " model or Fitting range is not valid!!!  "
1537            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1538
1539        try:
1540            self.save_current_state()
1541        except Exception:
1542            logging.error(traceback.format_exc())
1543
1544        return flag
1545
1546    def _reset_parameters_state(self, listtorestore, statelist):
1547        """
1548        Reset the parameters at the given state
1549        """
1550        if len(statelist) == 0 or len(listtorestore) == 0:
1551            return
1552        if len(statelist) != len(listtorestore):
1553            return
1554
1555        for j in range(len(listtorestore)):
1556            item_page = listtorestore[j]
1557            item_page_info = statelist[j]
1558            ##change the state of the check box for simple parameters
1559            if item_page[0] != None:
1560                item_page[0].SetValue(item_page_info[0])
1561            if item_page[2] != None:
1562                item_page[2].SetValue(item_page_info[2])
1563                if item_page[2].__class__.__name__ == "ComboBox":
1564                    if item_page_info[2] in self.model.fun_list:
1565                        fun_val = self.model.fun_list[item_page_info[2]]
1566                        self.model.setParam(item_page_info[1], fun_val)
1567            if item_page[3] != None:
1568                ## show or hide text +/-
1569                if item_page_info[2]:
1570                    item_page[3].Show(True)
1571                else:
1572                    item_page[3].Hide()
1573            if item_page[4] != None:
1574                ## show of hide the text crtl for fitting error
1575                if item_page_info[4][0]:
1576                    item_page[4].Show(True)
1577                    item_page[4].SetValue(item_page_info[4][1])
1578                else:
1579                    item_page[3].Hide()
1580            if item_page[5] != None:
1581                ## show of hide the text crtl for fitting error
1582                item_page[5].Show(item_page_info[5][0])
1583                item_page[5].SetValue(item_page_info[5][1])
1584
1585            if item_page[6] != None:
1586                ## show of hide the text crtl for fitting error
1587                item_page[6].Show(item_page_info[6][0])
1588                item_page[6].SetValue(item_page_info[6][1])
1589
1590    def _reset_strparam_state(self, listtorestore, statelist):
1591        """
1592        Reset the string parameters at the given state
1593        """
1594        if len(statelist) == 0:
1595            return
1596
1597        listtorestore = copy.deepcopy(statelist)
1598
1599        for j in range(len(listtorestore)):
1600            item_page = listtorestore[j]
1601            item_page_info = statelist[j]
1602            ##change the state of the check box for simple parameters
1603
1604            if item_page[0] != None:
1605                item_page[0].SetValue(format_number(item_page_info[0], True))
1606
1607            if item_page[2] != None:
1608                param_name = item_page_info[1]
1609                value = item_page_info[2]
1610                selection = value
1611                if value in self.model.fun_list:
1612                    selection = self.model.fun_list[value]
1613                item_page[2].SetValue(selection)
1614                self.model.setParam(param_name, selection)
1615
1616    def _copy_parameters_state(self, listtocopy, statelist):
1617        """
1618        copy the state of button
1619
1620        :param listtocopy: the list of check button to copy
1621        :param statelist: list of state object to store the current state
1622
1623        """
1624        if len(listtocopy) == 0:
1625            return
1626
1627        for item in listtocopy:
1628
1629            checkbox_state = None
1630            if item[0] != None:
1631                checkbox_state = item[0].GetValue()
1632            parameter_name = item[1]
1633            parameter_value = None
1634            if item[2] != None:
1635                parameter_value = item[2].GetValue()
1636            static_text = None
1637            if item[3] != None:
1638                static_text = item[3].IsShown()
1639            error_value = None
1640            error_state = None
1641            if item[4] != None:
1642                error_value = item[4].GetValue()
1643                error_state = item[4].IsShown()
1644
1645            min_value = None
1646            min_state = None
1647            if item[5] != None:
1648                min_value = item[5].GetValue()
1649                min_state = item[5].IsShown()
1650
1651            max_value = None
1652            max_state = None
1653            if item[6] != None:
1654                max_value = item[6].GetValue()
1655                max_state = item[6].IsShown()
1656            unit = None
1657            if item[7] != None:
1658                unit = item[7].GetLabel()
1659
1660            statelist.append([checkbox_state, parameter_name, parameter_value,
1661                              static_text, [error_state, error_value],
1662                              [min_state, min_value],
1663                              [max_state, max_value], unit])
1664
1665
1666    def _draw_model(self, update_chisqr=True, source='model'):
1667        """
1668        Method to draw or refresh a plotted model.
1669        The method will use the data member from the model page
1670        to build a call to the fitting perspective manager.
1671
1672        :param chisqr: update chisqr value [bool]
1673        """
1674        wx.CallAfter(self._draw_model_after, update_chisqr, source)
1675
1676    def _draw_model_after(self, update_chisqr=True, source='model'):
1677        """
1678        Method to draw or refresh a plotted model.
1679        The method will use the data member from the model page
1680        to build a call to the fitting perspective manager.
1681
1682        :param chisqr: update chisqr value [bool]
1683        """
1684        #if self.check_invalid_panel():
1685        #    return
1686        if self.model != None:
1687            temp_smear = None
1688            if hasattr(self, "enable_smearer"):
1689                if not self.disable_smearer.GetValue():
1690                    temp_smear = self.current_smearer
1691            # compute weight for the current data
1692            from sas.sasgui.perspectives.fitting.utils import get_weight
1693            flag = self.get_weight_flag()
1694            weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
1695            toggle_mode_on = self.model_view.IsEnabled()
1696            is_2d = self._is_2D()
1697            self._manager.draw_model(self.model,
1698                                    data=self.data,
1699                                    smearer=temp_smear,
1700                                    qmin=float(self.qmin_x),
1701                                    qmax=float(self.qmax_x),
1702                                    page_id=self.uid,
1703                                    toggle_mode_on=toggle_mode_on,
1704                                    state=self.state,
1705                                    enable2D=is_2d,
1706                                    update_chisqr=update_chisqr,
1707                                    source='model',
1708                                    weight=weight)
1709
1710    def _on_show_sld(self, event=None):
1711        """
1712        Plot SLD profile
1713        """
1714        # get profile data
1715        x, y = self.model.getProfile()
1716
1717        from sas.sasgui.plottools import Data1D as pf_data1d
1718        #from sas.sasgui.perspectives.theory.profile_dialog import SLDPanel
1719        from sas.sasgui.guiframe.local_perspectives.plotting.profile_dialog \
1720        import SLDPanel
1721        sld_data = pf_data1d(x, y)
1722        sld_data.name = 'SLD'
1723        sld_data.axes = self.sld_axes
1724        self.panel = SLDPanel(self, data=sld_data, axes=self.sld_axes,
1725                              id=wx.ID_ANY)
1726        self.panel.ShowModal()
1727
1728    def _set_multfactor_combobox(self, multiplicity=10):
1729        """
1730        Set comboBox for muitfactor of CoreMultiShellModel
1731        :param multiplicit: no. of multi-functionality
1732        """
1733        # build content of the combobox
1734        for idx in range(0, multiplicity):
1735            self.multifactorbox.Append(str(idx), int(idx))
1736        self._hide_multfactor_combobox()
1737
1738    def _show_multfactor_combobox(self):
1739        """
1740        Show the comboBox of muitfactor of CoreMultiShellModel
1741        """
1742        if not self.mutifactor_text.IsShown():
1743            self.mutifactor_text.Show(True)
1744            self.mutifactor_text1.Show(True)
1745        if not self.multifactorbox.IsShown():
1746            self.multifactorbox.Show(True)
1747
1748    def _hide_multfactor_combobox(self):
1749        """
1750        Hide the comboBox of muitfactor of CoreMultiShellModel
1751        """
1752        if self.mutifactor_text.IsShown():
1753            self.mutifactor_text.Hide()
1754            self.mutifactor_text1.Hide()
1755        if self.multifactorbox.IsShown():
1756            self.multifactorbox.Hide()
1757
1758    def formfactor_combo_init(self):
1759        """
1760        First time calls _show_combox_helper
1761        """
1762        self._show_combox(None)
1763
1764    def _show_combox_helper(self):
1765        """
1766        Fill panel's combo box according to the type of model selected
1767        """
1768        custom_model = 'Customized Models'
1769        mod_cat = self.categorybox.GetStringSelection()
1770        self.structurebox.SetSelection(0)
1771        self.structurebox.Disable()
1772        self.formfactorbox.Clear()
1773        if mod_cat == None:
1774            return
1775        m_list = []
1776        try:
1777            if mod_cat == custom_model:
1778                for model in self.model_list_box[mod_cat]:
1779                    m_list.append(self.model_dict[model.name])
1780            else:
1781                cat_dic = self.master_category_dict[mod_cat]
1782                for (model, enabled) in cat_dic:
1783                    if enabled:
1784                        m_list.append(self.model_dict[model])
1785        except Exception:
1786            msg = traceback.format_exc()
1787            wx.PostEvent(self._manager.parent,
1788                         StatusEvent(status=msg, info="error"))
1789        self._populate_box(self.formfactorbox, m_list)
1790
1791    def _on_modify_cat(self, event=None):
1792        """
1793        Called when category manager is opened
1794        """
1795        self._manager.parent.on_category_panel(event)
1796
1797    def _show_combox(self, event=None):
1798        """
1799        Show combox box associate with type of model selected
1800        """
1801        self.Show(False)
1802        self._show_combox_helper()
1803        self._on_select_model(event=None)
1804        self.Show(True)
1805        self._save_typeOfmodel()
1806        self.sizer4_4.Layout()
1807        self.sizer4.Layout()
1808        self.Layout()
1809        self.Refresh()
1810
1811    def _populate_box(self, combobox, list):
1812        """
1813        fill combox box with dict item
1814
1815        :param list: contains item to fill the combox
1816            item must model class
1817        """
1818        mlist = []
1819        for models in list:
1820            if models.name != "NoStructure":
1821                mlist.append((models.name, models))
1822
1823        # Sort the models
1824        mlist_sorted = sorted(mlist)
1825        for item in mlist_sorted:
1826            combobox.Append(item[0], item[1])
1827        return 0
1828
1829    def _onQrangeEnter(self, event):
1830        """
1831        Check validity of value enter in the Q range field
1832
1833        """
1834        tcrtl = event.GetEventObject()
1835        #Clear msg if previously shown.
1836        msg = ""
1837        wx.PostEvent(self.parent, StatusEvent(status=msg))
1838        # Flag to register when a parameter has changed.
1839        if tcrtl.GetValue().lstrip().rstrip() != "":
1840            try:
1841                float(tcrtl.GetValue())
1842                tcrtl.SetBackgroundColour(wx.WHITE)
1843                # If qmin and qmax have been modified, update qmin and qmax
1844                if self._validate_qrange(self.qmin, self.qmax):
1845                    tempmin = float(self.qmin.GetValue())
1846                    if tempmin != self.qmin_x:
1847                        self.qmin_x = tempmin
1848                    tempmax = float(self.qmax.GetValue())
1849                    if tempmax != self.qmax_x:
1850                        self.qmax_x = tempmax
1851                else:
1852                    tcrtl.SetBackgroundColour("pink")
1853                    msg = "Model Error: wrong value entered: %s" % \
1854                                    sys.exc_info()[1]
1855                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1856                    return
1857            except:
1858                tcrtl.SetBackgroundColour("pink")
1859                msg = "Model Error: wrong value entered: %s" % sys.exc_info()[1]
1860                wx.PostEvent(self.parent, StatusEvent(status=msg))
1861                return
1862            #Check if # of points for theory model are valid(>0).
1863            if self.npts != None:
1864                if check_float(self.npts):
1865                    temp_npts = float(self.npts.GetValue())
1866                    if temp_npts != self.num_points:
1867                        self.num_points = temp_npts
1868                else:
1869                    msg = "Cannot plot: No points in Q range!!!  "
1870                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1871        else:
1872            tcrtl.SetBackgroundColour("pink")
1873            msg = "Model Error: wrong value entered!!!"
1874            wx.PostEvent(self.parent, StatusEvent(status=msg))
1875        self.save_current_state()
1876        event = PageInfoEvent(page=self)
1877        wx.PostEvent(self.parent, event)
1878        self.state_change = False
1879        #Draw the model for a different range
1880        if not self.data.is_data:
1881            self.create_default_data()
1882        self._draw_model()
1883
1884    def _theory_qrange_enter(self, event):
1885        """
1886        Check validity of value enter in the Q range field
1887        """
1888
1889        tcrtl = event.GetEventObject()
1890        #Clear msg if previously shown.
1891        msg = ""
1892        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1893        # Flag to register when a parameter has changed.
1894        is_modified = False
1895        if tcrtl.GetValue().lstrip().rstrip() != "":
1896            try:
1897                value = float(tcrtl.GetValue())
1898                tcrtl.SetBackgroundColour(wx.WHITE)
1899
1900                # If qmin and qmax have been modified, update qmin and qmax
1901                if self._validate_qrange(self.theory_qmin, self.theory_qmax):
1902                    tempmin = float(self.theory_qmin.GetValue())
1903                    if tempmin != self.theory_qmin_x:
1904                        self.theory_qmin_x = tempmin
1905                    tempmax = float(self.theory_qmax.GetValue())
1906                    if tempmax != self.qmax_x:
1907                        self.theory_qmax_x = tempmax
1908                else:
1909                    tcrtl.SetBackgroundColour("pink")
1910                    msg = "Model Error: wrong value entered: %s" % \
1911                                        sys.exc_info()[1]
1912                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1913                    return
1914            except:
1915                tcrtl.SetBackgroundColour("pink")
1916                msg = "Model Error: wrong value entered: %s" % sys.exc_info()[1]
1917                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1918                return
1919            #Check if # of points for theory model are valid(>0).
1920            if self.Npts_total.IsEditable():
1921                if check_float(self.Npts_total):
1922                    temp_npts = float(self.Npts_total.GetValue())
1923                    if temp_npts != self.num_points:
1924                        self.num_points = temp_npts
1925                        is_modified = True
1926                else:
1927                    msg = "Cannot Plot: No points in Q range!!!  "
1928                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1929        else:
1930            tcrtl.SetBackgroundColour("pink")
1931            msg = "Model Error: wrong value entered!!!"
1932            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1933        self.save_current_state()
1934        event = PageInfoEvent(page=self)
1935        wx.PostEvent(self.parent, event)
1936        self.state_change = False
1937        #Draw the model for a different range
1938        self.create_default_data()
1939        self._draw_model()
1940
1941    def _on_select_model_helper(self):
1942        """
1943        call back for model selection
1944        """
1945        ## reset dictionary containing reference to dispersion
1946        self._disp_obj_dict = {}
1947        self.disp_cb_dict = {}
1948        self.temp_multi_functional = False
1949        f_id = self.formfactorbox.GetCurrentSelection()
1950        #For MAC
1951        form_factor = None
1952        if f_id >= 0:
1953            form_factor = self.formfactorbox.GetClientData(f_id)
1954
1955        if form_factor is None or not form_factor.is_form_factor:
1956            self.structurebox.Hide()
1957            self.text2.Hide()
1958            self.structurebox.Disable()
1959            self.structurebox.SetSelection(0)
1960            self.text2.Disable()
1961        else:
1962            self.structurebox.Show()
1963            self.text2.Show()
1964            self.structurebox.Enable()
1965            self.text2.Enable()
1966
1967        if form_factor != None:
1968            # set multifactor for Mutifunctional models
1969            if form_factor.is_multiplicity_model:
1970                m_id = self.multifactorbox.GetCurrentSelection()
1971                multiplicity = form_factor.multiplicity_info[0]
1972                self.multifactorbox.Clear()
1973                self._set_multfactor_combobox(multiplicity)
1974                self._show_multfactor_combobox()
1975                #ToDo:  this info should be called directly from the model
1976                text = form_factor.multiplicity_info[1]  # 'No. of Shells: '
1977
1978                self.mutifactor_text.SetLabel(text)
1979                if m_id > multiplicity - 1:
1980                    # default value
1981                    m_id = 1
1982
1983                self.multi_factor = self.multifactorbox.GetClientData(m_id)
1984                if self.multi_factor == None:
1985                    self.multi_factor = 0
1986                self.multifactorbox.SetSelection(m_id)
1987                # Check len of the text1 and max_multiplicity
1988                text = ''
1989                if form_factor.multiplicity_info[0] == \
1990                                        len(form_factor.multiplicity_info[2]):
1991                    text = form_factor.multiplicity_info[2][self.multi_factor]
1992                self.mutifactor_text1.SetLabel(text)
1993                # Check if model has  get sld profile.
1994                if len(form_factor.multiplicity_info[3]) > 0:
1995                    self.sld_axes = form_factor.multiplicity_info[3]
1996                    self.show_sld_button.Show(True)
1997                else:
1998                    self.sld_axes = ""
1999            else:
2000                self._hide_multfactor_combobox()
2001                self.show_sld_button.Hide()
2002                self.multi_factor = None
2003        else:
2004            self._hide_multfactor_combobox()
2005            self.show_sld_button.Hide()
2006            self.multi_factor = None
2007
2008        s_id = self.structurebox.GetCurrentSelection()
2009        struct_factor = self.structurebox.GetClientData(s_id)
2010
2011        if  struct_factor != None:
2012            from sas.sascalc.fit.MultiplicationModel import MultiplicationModel
2013            self.model = MultiplicationModel(form_factor(self.multi_factor),
2014                                             struct_factor())
2015            # multifunctional form factor
2016            if len(form_factor.non_fittable) > 0:
2017                self.temp_multi_functional = True
2018        elif form_factor != None:
2019            if self.multi_factor is not None:
2020                self.model = form_factor(self.multi_factor)
2021            else:
2022                # old style plugin models do not accept a multiplicity argument
2023                self.model = form_factor()
2024        else:
2025            self.model = None
2026            return
2027
2028        # check if model has magnetic parameters
2029        if len(self.model.magnetic_params) > 0:
2030            self._has_magnetic = True
2031        else:
2032            self._has_magnetic = False
2033        ## post state to fit panel
2034        self.state.parameters = []
2035        self.state.model = self.model
2036        self.state.qmin = self.qmin_x
2037        self.state.multi_factor = self.multi_factor
2038        self.disp_list = self.model.getDispParamList()
2039        self.state.disp_list = self.disp_list
2040        self.on_set_focus(None)
2041        self.Layout()
2042
2043
2044    def _validate_qrange(self, qmin_ctrl, qmax_ctrl):
2045        """
2046        Verify that the Q range controls have valid values
2047        and that Qmin < Qmax.
2048
2049        :param qmin_ctrl: text control for Qmin
2050        :param qmax_ctrl: text control for Qmax
2051
2052        :return: True is the Q range is value, False otherwise
2053
2054        """
2055        qmin_validity = check_float(qmin_ctrl)
2056        qmax_validity = check_float(qmax_ctrl)
2057        if not (qmin_validity and qmax_validity):
2058            return False
2059        else:
2060            qmin = float(qmin_ctrl.GetValue())
2061            qmax = float(qmax_ctrl.GetValue())
2062            if qmin < qmax:
2063                #Make sure to set both colours white.
2064                qmin_ctrl.SetBackgroundColour(wx.WHITE)
2065                qmin_ctrl.Refresh()
2066                qmax_ctrl.SetBackgroundColour(wx.WHITE)
2067                qmax_ctrl.Refresh()
2068            else:
2069                qmin_ctrl.SetBackgroundColour("pink")
2070                qmin_ctrl.Refresh()
2071                qmax_ctrl.SetBackgroundColour("pink")
2072                qmax_ctrl.Refresh()
2073                msg = "Invalid Q range: Q min must be smaller than Q max"
2074                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2075                return False
2076        return True
2077
2078    def _validate_Npts(self):
2079        """
2080        Validate the number of points for fitting is more than 10 points.
2081        If valid, setvalues Npts_fit otherwise post msg.
2082        """
2083        #default flag
2084        flag = True
2085        # Theory
2086        if self.data == None and self.enable2D:
2087            return flag
2088        for data in self.data_list:
2089            # q value from qx and qy
2090            radius = numpy.sqrt(data.qx_data * data.qx_data +
2091                                data.qy_data * data.qy_data)
2092            #get unmasked index
2093            index_data = (float(self.qmin.GetValue()) <= radius) & \
2094                            (radius <= float(self.qmax.GetValue()))
2095            index_data = (index_data) & (data.mask)
2096            index_data = (index_data) & (numpy.isfinite(data.data))
2097
2098            if len(index_data[index_data]) < 10:
2099                # change the color pink.
2100                self.qmin.SetBackgroundColour("pink")
2101                self.qmin.Refresh()
2102                self.qmax.SetBackgroundColour("pink")
2103                self.qmax.Refresh()
2104                msg = "Data Error: "
2105                msg += "Too few points in %s." % data.name
2106                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2107                self.fitrange = False
2108                flag = False
2109            else:
2110                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
2111                self.fitrange = True
2112
2113        return flag
2114
2115    def _validate_Npts_1D(self):
2116        """
2117        Validate the number of points for fitting is more than 5 points.
2118        If valid, setvalues Npts_fit otherwise post msg.
2119        """
2120        #default flag
2121        flag = True
2122        # Theory
2123        if self.data == None:
2124            return flag
2125        for data in self.data_list:
2126            # q value from qx and qy
2127            radius = data.x
2128            #get unmasked index
2129            index_data = (float(self.qmin.GetValue()) <= radius) & \
2130                            (radius <= float(self.qmax.GetValue()))
2131            index_data = (index_data) & (numpy.isfinite(data.y))
2132
2133            if len(index_data[index_data]) < 5:
2134                # change the color pink.
2135                self.qmin.SetBackgroundColour("pink")
2136                self.qmin.Refresh()
2137                self.qmax.SetBackgroundColour("pink")
2138                self.qmax.Refresh()
2139                msg = "Data Error: "
2140                msg += "Too few points in %s." % data.name
2141                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2142                self.fitrange = False
2143                flag = False
2144            else:
2145                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
2146                self.fitrange = True
2147
2148        return flag
2149
2150    def _check_value_enter(self, list):
2151        """
2152        :param list: model parameter and panel info
2153        :Note: each item of the list should be as follow:
2154            item=[check button state, parameter's name,
2155                paramater's value, string="+/-",
2156                parameter's error of fit,
2157                parameter's minimum value,
2158                parameter's maximum value ,
2159                parameter's units]
2160
2161        Returns True if the model parameters have changed.
2162        """
2163        is_modified = False
2164        for item in list:
2165            #skip angle parameters for 1D
2166            if not self.enable2D and item in self.orientation_params:
2167                continue
2168
2169            value_ctrl = item[2]
2170            if not value_ctrl.IsEnabled():
2171                # ArrayDispersion disables PD, Min, Max, Npts, Nsigs
2172                continue
2173
2174            name = item[1]
2175            value_str = value_ctrl.GetValue().strip()
2176            if name.endswith(".npts"):
2177                validity = check_int(value_ctrl)
2178                if not validity:
2179                    continue
2180                value = int(value_str)
2181
2182            elif name.endswith(".nsigmas"):
2183                validity = check_float(value_ctrl)
2184                if not validity:
2185                    continue
2186                value = float(value_str)
2187
2188            else:  # value or polydispersity
2189
2190                # Check that min, max and value are floats
2191                min_ctrl, max_ctrl = item[5], item[6]
2192                min_str = min_ctrl.GetValue().strip()
2193                max_str = max_ctrl.GetValue().strip()
2194                validity = check_float(value_ctrl)
2195                if min_str != "":
2196                    validity = validity and check_float(min_ctrl)
2197                if max_str != "":
2198                    validity = validity and check_float(max_ctrl)
2199                if not validity:
2200                    continue
2201
2202                # Check that min is less than max
2203                low = -numpy.inf if min_str == "" else float(min_str)
2204                high = numpy.inf if max_str == "" else float(max_str)
2205                if high < low:
2206                    min_ctrl.SetBackgroundColour("pink")
2207                    min_ctrl.Refresh()
2208                    max_ctrl.SetBackgroundColour("pink")
2209                    max_ctrl.Refresh()
2210                    #msg = "Invalid fit range for %s: min must be smaller than max"%name
2211                    #wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2212                    continue
2213
2214                # Force value between min and max
2215                value = float(value_str)
2216                if value < low:
2217                    value = low
2218                    value_ctrl.SetValue(format_number(value))
2219                elif value > high:
2220                    value = high
2221                    value_ctrl.SetValue(format_number(value))
2222
2223                if name not in self.model.details.keys():
2224                    self.model.details[name] = ["", None, None]
2225                old_low, old_high = self.model.details[name][1:3]
2226                if old_low != low or old_high != high:
2227                    # The configuration has changed but it won't change the
2228                    # computed curve so no need to set is_modified to True
2229                    #is_modified = True
2230                    self.model.details[name][1:3] = low, high
2231
2232            # Update value in model if it has changed
2233            if value != self.model.getParam(name):
2234                self.model.setParam(name, value)
2235                is_modified = True
2236
2237        return is_modified
2238
2239    def _set_dipers_Param(self, event):
2240        """
2241        respond to self.enable_disp and self.disable_disp radio box.
2242        The dispersity object is reset inside the model into Gaussian.
2243        When the user select yes , this method display a combo box for
2244        more selection when the user selects No,the combo box disappears.
2245        Redraw the model with the default dispersity (Gaussian)
2246        """
2247        ## On selction if no model exists.
2248        if self.model == None:
2249            self.disable_disp.SetValue(True)
2250            msg = "Please select a Model first..."
2251            wx.MessageBox(msg, 'Info')
2252            wx.PostEvent(self._manager.parent,
2253                         StatusEvent(status="Polydispersion: %s" % msg))
2254            return
2255
2256        self._reset_dispersity()
2257
2258        if self.model == None:
2259            self.model_disp.Hide()
2260            self.sizer4_4.Clear(True)
2261            return
2262
2263        if self.enable_disp.GetValue():
2264            ## layout for model containing no dispersity parameters
2265
2266            self.disp_list = self.model.getDispParamList()
2267
2268            if len(self.disp_list) == 0 and len(self.disp_cb_dict) == 0:
2269                self._layout_sizer_noDipers()
2270            else:
2271                ## set gaussian sizer
2272                self._on_select_Disp(event=None)
2273        else:
2274            self.sizer4_4.Clear(True)
2275
2276        ## post state to fit panel
2277        self.save_current_state()
2278        if event != None:
2279            event = PageInfoEvent(page=self)
2280            wx.PostEvent(self.parent, event)
2281        #draw the model with the current dispersity
2282
2283        #Wojtek P, Oct 8, 2016: Calling draw_model seems to be unessecary.
2284        #By comenting it we save an extra Iq calculation
2285        #self._draw_model()
2286
2287        ## Need to use FitInside again here to replace the next four lines.
2288        ## Otherwised polydispersity off does not resize the scrollwindow.
2289        ## PDB Nov 28, 2015
2290        self.FitInside()
2291#        self.sizer4_4.Layout()
2292#        self.sizer5.Layout()
2293#        self.Layout()
2294#        self.Refresh()
2295
2296    def _layout_sizer_noDipers(self):
2297        """
2298        Draw a sizer with no dispersity info
2299        """
2300        ix = 0
2301        iy = 1
2302        self.fittable_param = []
2303        self.fixed_param = []
2304        self.orientation_params_disp = []
2305
2306        self.sizer4_4.Clear(True)
2307        text = "No polydispersity available for this model"
2308        model_disp = wx.StaticText(self, wx.ID_ANY, text)
2309        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
2310                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
2311        self.sizer4_4.Layout()
2312        self.sizer4.Layout()
2313
2314    def _reset_dispersity(self):
2315        """
2316        put gaussian dispersity into current model
2317        """
2318        if len(self.param_toFit) > 0:
2319            for item in self.fittable_param:
2320                if item in self.param_toFit:
2321                    self.param_toFit.remove(item)
2322
2323            for item in self.orientation_params_disp:
2324                if item in self.param_toFit:
2325                    self.param_toFit.remove(item)
2326
2327        self.fittable_param = []
2328        self.fixed_param = []
2329        self.orientation_params_disp = []
2330        self.values = {}
2331        self.weights = {}
2332
2333        #from sas.models.dispersion_models import GaussianDispersion
2334        from sasmodels.weights import GaussianDispersion
2335        if len(self.disp_cb_dict) == 0:
2336            self.save_current_state()
2337            self.sizer4_4.Clear(True)
2338            self.Layout()
2339            return
2340        if (len(self.disp_cb_dict) > 0):
2341            for p in self.disp_cb_dict:
2342                # The parameter was un-selected.
2343                # Go back to Gaussian model (with 0 pts)
2344                disp_model = GaussianDispersion()
2345
2346                self._disp_obj_dict[p] = disp_model
2347                # Set the new model as the dispersion object
2348                # for the selected parameter
2349                try:
2350                    self.model.set_dispersion(p, disp_model)
2351                except Exception:
2352                    logging.error(traceback.format_exc())
2353
2354        ## save state into
2355        self.save_current_state()
2356        self.Layout()
2357        self.Refresh()
2358
2359    def _on_select_Disp(self, event):
2360        """
2361        allow selecting different dispersion
2362        self.disp_list should change type later .now only gaussian
2363        """
2364        self._set_sizer_dispersion()
2365
2366        ## Redraw the model
2367        self._draw_model()
2368        #self._undo.Enable(True)
2369        event = PageInfoEvent(page=self)
2370        wx.PostEvent(self.parent, event)
2371
2372        self.sizer4_4.Layout()
2373        self.sizer4.Layout()
2374        self.SetupScrolling()
2375
2376    def _on_disp_func(self, event=None):
2377        """
2378        Select a distribution function for the polydispersion
2379
2380        :Param event: ComboBox event
2381        """
2382        # get ready for new event
2383        if event != None:
2384            event.Skip()
2385        # Get event object
2386        disp_box = event.GetEventObject()
2387
2388        # Try to select a Distr. function
2389        try:
2390            disp_box.SetBackgroundColour("white")
2391            selection = disp_box.GetCurrentSelection()
2392            param_name = disp_box.Name.split('.')[0]
2393            disp_name = disp_box.GetValue()
2394            dispersity = disp_box.GetClientData(selection)
2395
2396            #disp_model =  GaussianDispersion()
2397            disp_model = dispersity()
2398            # Get param names to reset the values of the param
2399            name1 = param_name + ".width"
2400            name2 = param_name + ".npts"
2401            name3 = param_name + ".nsigmas"
2402            # Check Disp. function whether or not it is 'array'
2403            if disp_name.lower() == "array":
2404                value2 = ""
2405                value3 = ""
2406                value1 = self._set_array_disp(name=name1, disp=disp_model)
2407            else:
2408                self._del_array_values(name1)
2409                #self._reset_array_disp(param_name)
2410                self._disp_obj_dict[name1] = disp_model
2411                self.model.set_dispersion(param_name, disp_model)
2412                self.state._disp_obj_dict[name1] = disp_model.type
2413
2414                value1 = str(format_number(self.model.getParam(name1), True))
2415                value2 = str(format_number(self.model.getParam(name2)))
2416                value3 = str(format_number(self.model.getParam(name3)))
2417            # Reset fittable polydispersin parameter value
2418            for item in self.fittable_param:
2419                if item[1] == name1:
2420                    item[2].SetValue(value1)
2421                    item[5].SetValue("")
2422                    item[6].SetValue("")
2423                    # Disable for array
2424                    if disp_name.lower() == "array":
2425                        item[0].SetValue(False)
2426                        item[0].Disable()
2427                        item[2].Disable()
2428                        item[3].Show(False)
2429                        item[4].Show(False)
2430                        item[5].Disable()
2431                        item[6].Disable()
2432                    else:
2433                        item[0].Enable()
2434                        item[2].Enable()
2435                        item[3].Show(True)
2436                        item[4].Show(True)
2437                        item[5].Enable()
2438                        item[6].Enable()
2439                    break
2440            # Reset fixed polydispersion params
2441            for item in self.fixed_param:
2442                if item[1] == name2:
2443                    item[2].SetValue(value2)
2444                    # Disable Npts for array
2445                    if disp_name.lower() == "array":
2446                        item[2].Disable()
2447                    else:
2448                        item[2].Enable()
2449                if item[1] == name3:
2450                    item[2].SetValue(value3)
2451                    # Disable Nsigs for array
2452                    if disp_name.lower() == "array":
2453                        item[2].Disable()
2454                    else:
2455                        item[2].Enable()
2456
2457            # Make sure the check box updated
2458            self.get_all_checked_params()
2459
2460            # update params
2461            self._update_paramv_on_fit()
2462            # draw
2463            self._draw_model()
2464            self.Refresh()
2465        except Exception:
2466            logging.error(traceback.format_exc())
2467            # Error msg
2468            msg = "Error occurred:"
2469            msg += " Could not select the distribution function..."
2470            msg += " Please select another distribution function."
2471            disp_box.SetBackgroundColour("pink")
2472            # Focus on Fit button so that users can see the pinky box
2473            self.btFit.SetFocus()
2474            wx.PostEvent(self._manager.parent,
2475                         StatusEvent(status=msg, info="error"))
2476
2477    def _set_array_disp(self, name=None, disp=None):
2478        """
2479        Set array dispersion
2480
2481        :param name: name of the parameter for the dispersion to be set
2482        :param disp: the polydisperion object
2483        """
2484        # The user wants this parameter to be averaged.
2485        # Pop up the file selection dialog.
2486        path = self._selectDlg()
2487        # Array data
2488        values = []
2489        weights = []
2490        # If nothing was selected, just return
2491        if path is None:
2492            self.disp_cb_dict[name].SetValue(False)
2493            #self.noDisper_rbox.SetValue(True)
2494            return
2495        self._default_save_location = os.path.dirname(path)
2496        if self._manager != None:
2497            self._manager.parent._default_save_location = \
2498                             self._default_save_location
2499
2500        basename = os.path.basename(path)
2501        values, weights = self.read_file(path)
2502
2503        # If any of the two arrays is empty, notify the user that we won't
2504        # proceed
2505        if len(self.param_toFit) > 0:
2506            if name in self.param_toFit:
2507                self.param_toFit.remove(name)
2508
2509        # Tell the user that we are about to apply the distribution
2510        msg = "Applying loaded %s distribution: %s" % (name, path)
2511        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2512        self._set_array_disp_model(name=name, disp=disp,
2513                                    values=values, weights=weights)
2514        return basename
2515
2516    def _set_array_disp_model(self, name=None, disp=None,
2517                              values=[], weights=[]):
2518        """
2519        Set array dispersion model
2520
2521        :param name: name of the parameter for the dispersion to be set
2522        :param disp: the polydisperion object
2523        """
2524        disp.set_weights(values, weights)
2525        self._disp_obj_dict[name] = disp
2526        self.model.set_dispersion(name.split('.')[0], disp)
2527        self.state._disp_obj_dict[name] = disp.type
2528        self.values[name] = values
2529        self.weights[name] = weights
2530        # Store the object to make it persist outside the
2531        # scope of this method
2532        #TODO: refactor model to clean this up?
2533        self.state.values = {}
2534        self.state.weights = {}
2535        self.state.values = copy.deepcopy(self.values)
2536        self.state.weights = copy.deepcopy(self.weights)
2537
2538        # Set the new model as the dispersion object for the
2539        #selected parameter
2540        #self.model.set_dispersion(p, disp_model)
2541        # Store a reference to the weights in the model object
2542        #so that
2543        # it's not lost when we use the model within another thread.
2544        self.state.model = self.model.clone()
2545        self.model._persistency_dict[name.split('.')[0]] = \
2546                                        [values, weights]
2547        self.state.model._persistency_dict[name.split('.')[0]] = \
2548                                        [values, weights]
2549
2550    def _del_array_values(self, name=None):
2551        """
2552        Reset array dispersion
2553
2554        :param name: name of the parameter for the dispersion to be set
2555        """
2556        # Try to delete values and weight of the names array dic if exists
2557        try:
2558            if name in self.values:
2559                del self.values[name]
2560                del self.weights[name]
2561                # delete all other dic
2562                del self.state.values[name]
2563                del self.state.weights[name]
2564                del self.model._persistency_dict[name.split('.')[0]]
2565                del self.state.model._persistency_dict[name.split('.')[0]]
2566        except Exception:
2567            logging.error(traceback.format_exc())
2568
2569    def _lay_out(self):
2570        """
2571        returns self.Layout
2572
2573        :Note: Mac seems to like this better when self.
2574            Layout is called after fitting.
2575        """
2576        self._sleep4sec()
2577        self.Layout()
2578        return
2579
2580    def _sleep4sec(self):
2581        """
2582            sleep for 1 sec only applied on Mac
2583            Note: This 1sec helps for Mac not to crash on self.
2584            Layout after self._draw_model
2585        """
2586        if ON_MAC == True:
2587            time.sleep(1)
2588
2589    def _find_polyfunc_selection(self, disp_func=None):
2590        """
2591        FInd Comboox selection from disp_func
2592
2593        :param disp_function: dispersion distr. function
2594        """
2595        # Find the selection
2596        if disp_func is not None:
2597            try:
2598                return POLYDISPERSITY_MODELS.values().index(disp_func.__class__)
2599            except ValueError:
2600                pass  # Fall through to default class
2601        return POLYDISPERSITY_MODELS.keys().index('gaussian')
2602
2603    def on_reset_clicked(self, event):
2604        """
2605        On 'Reset' button  for Q range clicked
2606        """
2607        flag = True
2608        ##For 3 different cases: Data2D, Data1D, and theory
2609        if self.model == None:
2610            msg = "Please select a model first..."
2611            wx.MessageBox(msg, 'Info')
2612            flag = False
2613            return
2614
2615        elif self.data.__class__.__name__ == "Data2D":
2616            data_min = 0
2617            x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax))
2618            y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
2619            self.qmin_x = data_min
2620            self.qmax_x = math.sqrt(x * x + y * y)
2621            #self.data.mask = numpy.ones(len(self.data.data),dtype=bool)
2622            # check smearing
2623            if not self.disable_smearer.GetValue():
2624                ## set smearing value whether or
2625                # not the data contain the smearing info
2626                if self.pinhole_smearer.GetValue():
2627                    flag = self.update_pinhole_smear()
2628                else:
2629                    flag = True
2630
2631        elif self.data == None:
2632            self.qmin_x = _QMIN_DEFAULT
2633            self.qmax_x = _QMAX_DEFAULT
2634            self.num_points = _NPTS_DEFAULT
2635            self.state.npts = self.num_points
2636
2637        elif self.data.__class__.__name__ != "Data2D":
2638            self.qmin_x = min(self.data.x)
2639            self.qmax_x = max(self.data.x)
2640            # check smearing
2641            if not self.disable_smearer.GetValue():
2642                ## set smearing value whether or
2643                # not the data contain the smearing info
2644                if self.slit_smearer.GetValue():
2645                    flag = self.update_slit_smear()
2646                elif self.pinhole_smearer.GetValue():
2647                    flag = self.update_pinhole_smear()
2648                else:
2649                    flag = True
2650        else:
2651            flag = False
2652
2653        if flag == False:
2654            msg = "Cannot Plot :Must enter a number!!!  "
2655            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2656        else:
2657            # set relative text ctrs.
2658            self.qmin.SetValue(str(self.qmin_x))
2659            self.qmax.SetValue(str(self.qmax_x))
2660            self.show_npts2fit()
2661            # At this point, some button and variables satatus (disabled?)
2662            # should be checked such as color that should be reset to
2663            # white in case that it was pink.
2664            self._onparamEnter_helper()
2665
2666        self.save_current_state()
2667        self.state.qmin = self.qmin_x
2668        self.state.qmax = self.qmax_x
2669
2670        #reset the q range values
2671        self._reset_plotting_range(self.state)
2672        self._draw_model()
2673
2674    def select_log(self, event):
2675        """
2676        Log checked to generate log spaced points for theory model
2677        """
2678
2679    def get_images(self):
2680        """
2681        Get the images of the plots corresponding this panel for report
2682
2683        : return graphs: list of figures
2684        : Need Move to guiframe
2685        """
2686        # set list of graphs
2687        graphs = []
2688        canvases = []
2689        res_item = None
2690        # call gui_manager
2691        gui_manager = self._manager.parent
2692        # loops through the panels [dic]
2693        for _, item2 in gui_manager.plot_panels.iteritems():
2694            data_title = self.data.group_id
2695            # try to get all plots belonging to this control panel
2696            try:
2697                g_id = item2.group_id
2698                if g_id == data_title or \
2699                        str(g_id).count("res" + str(self.graph_id)) or \
2700                        str(g_id).count(str(self.uid)) > 0:
2701                    if str(g_id).count("res" + str(self.graph_id)) > 0:
2702                        res_item = [item2.figure, item2.canvas]
2703                    else:
2704                        # append to the list
2705                        graphs.append(item2.figure)
2706                        canvases.append(item2.canvas)
2707            except Exception:
2708                # Not for control panels
2709                logging.error(traceback.format_exc())
2710        # Make sure the resduals plot goes to the last
2711        if res_item != None:
2712            graphs.append(res_item[0])
2713            canvases.append(res_item[1])
2714        # return the list of graphs
2715        return graphs, canvases
2716
2717    def on_function_help_clicked(self, event):
2718        """
2719        Function called when 'Help' button is pressed next to model
2720        of interest.  This calls DocumentationWindow from
2721        documentation_window.py. It will load the top level of the model
2722        help documenation sphinx generated html if no model is presented.
2723        If a model IS present then if documention for that model exists
2724        it will load to that  point otherwise again it will go to the top.
2725        For Wx2.8 and below is used (i.e. non-released through installer)
2726        a browser is loaded and the top of the model documentation only is
2727        accessible because webbrowser module does not pass anything after
2728        the # to the browser.
2729
2730        :param evt: on Help Button pressed event
2731        """
2732
2733        if self.model != None:
2734            name = self.formfactorbox.GetValue()
2735            _TreeLocation = 'user/models/'+ name.lower()+'.html'
2736            _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2737                                              "", name + " Help")
2738        else:
2739            _TreeLocation = 'user/index.html'
2740            _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2741                                              "", "General Model Help")
2742
2743
2744    def on_model_help_clicked(self, event):
2745        """
2746        Function called when 'Description' button is pressed next to model
2747        of interest.  This calls the Description embedded in the model. This
2748        should work with either Wx2.8 and lower or higher. If no model is
2749        selected it will give the message that a model must be chosen first
2750        in the box that would normally contain the description.  If a badly
2751        behaved model is encountered which has no description then it will
2752        give the message that none is available.
2753
2754        :param evt: on Description Button pressed event
2755        """
2756
2757        if self.model == None:
2758            name = 'index.html'
2759        else:
2760            name = self.formfactorbox.GetValue()
2761
2762        msg = 'Model description:\n'
2763        info = "Info"
2764        if self.model != None:
2765#                frame.Destroy()
2766            if str(self.model.description).rstrip().lstrip() == '':
2767                msg += "Sorry, no information is available for this model."
2768            else:
2769                msg += self.model.description + '\n'
2770            wx.MessageBox(msg, info)
2771        else:
2772            msg += "You must select a model to get information on this"
2773            wx.MessageBox(msg, info)
2774
2775    def _on_mag_angle_help(self, event):
2776        """
2777        Bring up Magnetic Angle definition bmp image whenever the ? button
2778        is clicked. Calls DocumentationWindow with the path of the location
2779        within the documentation tree (after /doc/ ....". When using old
2780        versions of Wx (i.e. before 2.9 and therefore not part of release
2781        versions distributed via installer) it brings up an image viewer
2782        box which allows the user to click through the rest of the images in
2783        the directory.  Not ideal but probably better than alternative which
2784        would bring up the entire discussion of how magnetic models work?
2785        Specially since it is not likely to be accessed.  The normal release
2786        versions bring up the normal image box.
2787
2788        :param evt: Triggers on clicking ? in Magnetic Angles? box
2789        """
2790
2791        _TreeLocation = "_images/M_angles_pic.bmp"
2792        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
2793                                          "Magnetic Angle Defintions")
2794
2795    def _on_mag_help(self, event):
2796        """
2797        Bring up Magnetic Angle definition bmp image whenever the ? button
2798        is clicked. Calls DocumentationWindow with the path of the location
2799        within the documentation tree (after /doc/ ....". When using old
2800        versions of Wx (i.e. before 2.9 and therefore not part of release
2801        versions distributed via installer) it brings up an image viewer
2802        box which allows the user to click through the rest of the images in
2803        the directory.  Not ideal but probably better than alternative which
2804        would bring up the entire discussion of how magnetic models work?
2805        Specially since it is not likely to be accessed.  The normal release
2806        versions bring up the normal image box.
2807
2808        :param evt: Triggers on clicking ? in Magnetic Angles? box
2809        """
2810
2811        _TreeLocation = "user/magnetism.html"
2812        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
2813                                          "Polarized Beam/Magnetc Help")
2814
2815    def _on_mag_on(self, event):
2816        """
2817        Magnetic Parameters ON/OFF
2818        """
2819        button = event.GetEventObject()
2820
2821        if button.GetLabel().count('ON') > 0:
2822            self.magnetic_on = True
2823            button.SetLabel("Magnetic OFF")
2824            m_value = 1.0e-06
2825            for key in self.model.magnetic_params:
2826                if key.count('M0') > 0:
2827                    self.model.setParam(key, m_value)
2828                    m_value += 0.5e-06
2829        else:
2830            self.magnetic_on = False
2831            button.SetLabel("Magnetic ON")
2832            for key in self.model.magnetic_params:
2833                if key.count('M0') > 0:
2834                    #reset mag value to zero fo safety
2835                    self.model.setParam(key, 0.0)
2836
2837        self.Show(False)
2838        self.set_model_param_sizer(self.model)
2839        #self._set_sizer_dispersion()
2840        self.state.magnetic_on = self.magnetic_on
2841        self.SetupScrolling()
2842        self.Show(True)
2843
2844    def on_pd_help_clicked(self, event):
2845        """
2846        Bring up Polydispersity Documentation whenever the ? button is clicked.
2847        Calls DocumentationWindow with the path of the location within the
2848        documentation tree (after /doc/ ....".  Note that when using old
2849        versions of Wx (before 2.9) and thus not the release version of
2850        istallers, the help comes up at the top level of the file as
2851        webbrowser does not pass anything past the # to the browser when it is
2852        running "file:///...."
2853
2854        :param evt: Triggers on clicking ? in polydispersity box
2855        """
2856
2857        _TreeLocation = "user/sasgui/perspectives/fitting/pd_help.html"
2858        _PageAnchor = ""
2859        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2860                                          _PageAnchor, "Polydispersity Help")
2861
2862    def on_left_down(self, event):
2863        """
2864        Get key stroke event
2865        """
2866        # Figuring out key combo: Cmd for copy, Alt for paste
2867        if event.CmdDown() and event.ShiftDown():
2868            self.get_paste()
2869        elif event.CmdDown():
2870            self.get_copy()
2871        else:
2872            event.Skip()
2873            return
2874        # make event free
2875        event.Skip()
2876
2877    def get_copy(self):
2878        """
2879        Get copy params to clipboard
2880        """
2881        content = self.get_copy_params()
2882        flag = self.set_clipboard(content)
2883        self._copy_info(flag)
2884        return flag
2885
2886    def get_copy_params(self):
2887        """
2888        Get the string copies of the param names and values in the tap
2889        """
2890        content = 'sasview_parameter_values:'
2891        # Do it if params exist
2892        if  self.parameters != []:
2893
2894            # go through the parameters
2895            strings = self._get_copy_helper(self.parameters,
2896                                           self.orientation_params)
2897            content += strings
2898
2899            # go through the fittables
2900            strings = self._get_copy_helper(self.fittable_param,
2901                                           self.orientation_params_disp)
2902            content += strings
2903
2904            # go through the fixed params
2905            strings = self._get_copy_helper(self.fixed_param,
2906                                           self.orientation_params_disp)
2907            content += strings
2908
2909            # go through the str params
2910            strings = self._get_copy_helper(self.str_parameters,
2911                                           self.orientation_params)
2912            content += strings
2913            return content
2914        else:
2915            return False
2916
2917    def get_copy_excel(self):
2918        """
2919        Get copy params to clipboard
2920        """
2921        content = self.get_copy_params_excel()
2922        flag = self.set_clipboard(content)
2923        self._copy_info(flag)
2924        return flag
2925
2926    def get_copy_params_excel(self):
2927        """
2928        Get the string copies of the param names and values in the tap
2929        """
2930        content = ''
2931
2932        crlf = chr(13) + chr(10)
2933        tab = chr(9)
2934
2935        # Do it if params exist
2936        if  self.parameters != []:
2937
2938            for param in self.parameters:
2939                content += param[1] #parameter name
2940                content += tab
2941                content += param[1] + "_err"
2942                content += tab
2943
2944            content += crlf
2945
2946            #row of values and errors...
2947            for param in self.parameters:
2948                content += param[2].GetValue() #value
2949                content += tab
2950                content += param[4].GetValue() #error
2951                content += tab
2952
2953            return content
2954        else:
2955            return False
2956
2957
2958    def get_copy_latex(self):
2959        """
2960        Get copy params to clipboard
2961        """
2962        content = self.get_copy_params_latex()
2963        flag = self.set_clipboard(content)
2964        self._copy_info(flag)
2965        return flag
2966
2967    def get_copy_params_latex(self):
2968        """
2969        Get the string copies of the param names and values in the tap
2970        """
2971        content = '\\begin{table}'
2972        content += '\\begin{tabular}[h]'
2973
2974        crlf = chr(13) + chr(10)
2975        tab = chr(9)
2976
2977        # Do it if params exist
2978        if  self.parameters != []:
2979
2980            content += '{|'
2981            for param in self.parameters:
2982                content += 'l|l|'
2983            content += '}\hline'
2984            content += crlf
2985
2986            for index, param in enumerate(self.parameters):
2987                content += param[1].replace('_', '\_') #parameter name
2988                content += ' & '
2989                content += param[1].replace('_', '\_') + "\_err"
2990                if index < len(self.parameters) - 1:
2991                    content += ' & '
2992            content += '\\\\ \\hline'
2993            content += crlf
2994
2995            #row of values and errors...
2996            for index, param in enumerate(self.parameters):
2997                content += param[2].GetValue() #parameter value
2998                content += ' & '
2999                content += param[4].GetValue() #parameter error
3000                if index < len(self.parameters) - 1:
3001                    content += ' & '
3002            content += '\\\\ \\hline'
3003            content += crlf
3004
3005            content += '\\end{tabular}'
3006            content += '\\end{table}'
3007            return content
3008        else:
3009            return False
3010
3011
3012    def set_clipboard(self, content=None):
3013        """
3014        Put the string to the clipboard
3015        """
3016        if not content:
3017            return False
3018        if wx.TheClipboard.Open():
3019            wx.TheClipboard.SetData(wx.TextDataObject(str(content)))
3020            wx.TheClipboard.Close()
3021            return True
3022        return None
3023
3024    def _get_copy_helper(self, param, orient_param):
3025        """
3026        Helping get value and name of the params
3027
3028        : param param:  parameters
3029        : param orient_param = oritational params
3030        : return content: strings [list] [name,value:....]
3031        """
3032        content = ''
3033        # go through the str params
3034        for item in param:
3035            # copy only the params shown
3036            if not item[2].IsShown():
3037                continue
3038            disfunc = ''
3039            try:
3040                if item[7].__class__.__name__ == 'ComboBox':
3041                    disfunc = str(item[7].GetValue())
3042            except Exception:
3043                logging.error(traceback.format_exc())
3044
3045            # 2D
3046            if self.data.__class__.__name__ == "Data2D":
3047                try:
3048                    check = item[0].GetValue()
3049                except Exception:
3050                    check = None
3051                name = item[1]
3052                value = item[2].GetValue()
3053            # 1D
3054            else:
3055                ## for 1D all parameters except orientation
3056                if not item[1] in orient_param:
3057                    try:
3058                        check = item[0].GetValue()
3059                    except:
3060                        check = None
3061                    name = item[1]
3062                    value = item[2].GetValue()
3063
3064            # add to the content
3065            if disfunc != '':
3066
3067                disfunc = ',' + disfunc
3068            # Need to support array func for copy/paste
3069            try:
3070                if disfunc.count('array') > 0:
3071                    disfunc += ','
3072                    for val in self.values[name]:
3073                        disfunc += ' ' + str(val)
3074                    disfunc += ','
3075                    for weight in self.weights[name]:
3076                        disfunc += ' ' + str(weight)
3077            except Exception:
3078                logging.error(traceback.format_exc())
3079            content += name + ',' + str(check) + ',' + value + disfunc + ':'
3080
3081        return content
3082
3083    def get_clipboard(self):
3084        """
3085        Get strings in the clipboard
3086        """
3087        text = ""
3088        # Get text from the clip board
3089        if wx.TheClipboard.Open():
3090            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
3091                data = wx.TextDataObject()
3092                # get wx dataobject
3093                success = wx.TheClipboard.GetData(data)
3094                # get text
3095                if success:
3096                    text = data.GetText()
3097                else:
3098                    text = ''
3099            # close clipboard
3100            wx.TheClipboard.Close()
3101        return text
3102
3103    def get_paste(self):
3104        """
3105        Paste params from the clipboard
3106        """
3107        text = self.get_clipboard()
3108        flag = self.get_paste_params(text)
3109        self._copy_info(flag)
3110        return flag
3111
3112    def get_paste_params(self, text=''):
3113        """
3114        Get the string copies of the param names and values in the tap
3115        """
3116        context = {}
3117        # put the text into dictionary
3118        lines = text.split(':')
3119        if lines[0] != 'sasview_parameter_values':
3120            self._copy_info(False)
3121            return False
3122        for line in lines[1:-1]:
3123            if len(line) != 0:
3124                item = line.split(',')
3125                check = item[1]
3126                name = item[0]
3127                value = item[2]
3128                # Transfer the text to content[dictionary]
3129                context[name] = [check, value]
3130            # ToDo: PlugIn this poly disp function for pasting
3131            try:
3132                poly_func = item[3]
3133                context[name].append(poly_func)
3134                try:
3135                    # take the vals and weights for  array
3136                    array_values = item[4].split(' ')
3137                    array_weights = item[5].split(' ')
3138                    val = [float(a_val) for a_val in array_values[1:]]
3139                    weit = [float(a_weit) for a_weit in array_weights[1:]]
3140
3141                    context[name].append(val)
3142                    context[name].append(weit)
3143                except:
3144                    raise
3145            except:
3146                poly_func = ''
3147                context[name].append(poly_func)
3148
3149        # Do it if params exist
3150        if  self.parameters != []:
3151            # go through the parameters
3152            self._get_paste_helper(self.parameters,
3153                                   self.orientation_params, context)
3154
3155            # go through the fittables
3156            self._get_paste_helper(self.fittable_param,
3157                                   self.orientation_params_disp,
3158                                   context)
3159
3160            # go through the fixed params
3161            self._get_paste_helper(self.fixed_param,
3162                                   self.orientation_params_disp, context)
3163
3164            # go through the str params
3165            self._get_paste_helper(self.str_parameters,
3166                                   self.orientation_params, context)
3167
3168            return True
3169        return None
3170
3171    def _get_paste_helper(self, param, orient_param, content):
3172        """
3173        Helping set values of the params
3174
3175        : param param:  parameters
3176        : param orient_param: oritational params
3177        : param content: dictionary [ name, value: name1.value1,...]
3178        """
3179        # go through the str params
3180        for item in param:
3181            # 2D
3182            if self.data.__class__.__name__ == "Data2D":
3183                name = item[1]
3184                if name in content.keys():
3185                    check = content[name][0]
3186                    pd = content[name][1]
3187                    if name.count('.') > 0:
3188                        # If this is parameter.width, then pd may be a floating
3189                        # point value or it may be an array distribution.
3190                        # Nothing to do for parameter.npts or parameter.nsigmas.
3191                        try:
3192                            float(pd)
3193                            if name.endswith('.npts'):
3194                                pd = int(pd)
3195                        except Exception:
3196                            #continue
3197                            if not pd and pd != '':
3198                                continue
3199                    item[2].SetValue(str(pd))
3200                    if item in self.fixed_param and pd == '':
3201                        # Only array func has pd == '' case.
3202                        item[2].Enable(False)
3203                    else:
3204                        item[2].Enable(True)
3205                    if item[2].__class__.__name__ == "ComboBox":
3206                        if content[name][1] in self.model.fun_list:
3207                            fun_val = self.model.fun_list[content[name][1]]
3208                            self.model.setParam(name, fun_val)
3209
3210                    value = content[name][1:]
3211                    self._paste_poly_help(item, value)
3212                    if check == 'True':
3213                        is_true = True
3214                    elif check == 'False':
3215                        is_true = False
3216                    else:
3217                        is_true = None
3218                    if is_true != None:
3219                        item[0].SetValue(is_true)
3220            # 1D
3221            else:
3222                ## for 1D all parameters except orientation
3223                if not item[1] in orient_param:
3224                    name = item[1]
3225                    if name in content.keys():
3226                        check = content[name][0]
3227                        # Avoid changing combox content
3228                        value = content[name][1:]
3229                        pd = value[0]
3230                        if name.count('.') > 0:
3231                            # If this is parameter.width, then pd may be a floating
3232                            # point value or it may be an array distribution.
3233                            # Nothing to do for parameter.npts or parameter.nsigmas.
3234                            try:
3235                                pd = float(pd)
3236                                if name.endswith('.npts'):
3237                                    pd = int(pd)
3238                            except:
3239                                #continue
3240                                if not pd and pd != '':
3241                                    continue
3242                        item[2].SetValue(str(pd))
3243                        if item in self.fixed_param and pd == '':
3244                            # Only array func has pd == '' case.
3245                            item[2].Enable(False)
3246                        else:
3247                            item[2].Enable(True)
3248                        if item[2].__class__.__name__ == "ComboBox":
3249                            if value[0] in self.model.fun_list:
3250                                fun_val = self.model.fun_list[value[0]]
3251                                self.model.setParam(name, fun_val)
3252                                # save state
3253                        self._paste_poly_help(item, value)
3254                        if check == 'True':
3255                            is_true = True
3256                        elif check == 'False':
3257                            is_true = False
3258                        else:
3259                            is_true = None
3260                        if is_true != None:
3261                            item[0].SetValue(is_true)
3262
3263    def _paste_poly_help(self, item, value):
3264        """
3265        Helps get paste for poly function
3266
3267        *item* is the parameter name
3268
3269        *value* depends on which parameter is being processed, and whether it
3270        has array polydispersity.
3271
3272        For parameters without array polydispersity:
3273
3274            parameter => ['FLOAT', '']
3275            parameter.width => ['FLOAT', 'DISTRIBUTION', '']
3276            parameter.npts => ['FLOAT', '']
3277            parameter.nsigmas => ['FLOAT', '']
3278
3279        For parameters with array polydispersity:
3280
3281            parameter => ['FLOAT', '']
3282            parameter.width => ['FILENAME', 'array', [x1, ...], [w1, ...]]
3283            parameter.npts => ['FLOAT', '']
3284            parameter.nsigmas => ['FLOAT', '']
3285        """
3286        # Do nothing if not setting polydispersity
3287        if len(value[1]) == 0:
3288            return
3289
3290        try:
3291            name = item[7].Name
3292            param_name = name.split('.')[0]
3293            item[7].SetValue(value[1])
3294            selection = item[7].GetCurrentSelection()
3295            dispersity = item[7].GetClientData(selection)
3296            disp_model = dispersity()
3297
3298            if value[1] == 'array':
3299                pd_vals = numpy.array(value[2])
3300                pd_weights = numpy.array(value[3])
3301                if len(pd_vals) == 0 or len(pd_vals) != len(pd_weights):
3302                    msg = ("bad array distribution parameters for %s"
3303                           % param_name)
3304                    raise ValueError(msg)
3305                self._set_disp_cb(True, item=item)
3306                self._set_array_disp_model(name=name,
3307                                           disp=disp_model,
3308                                           values=pd_vals,
3309                                           weights=pd_weights)
3310            else:
3311                self._set_disp_cb(False, item=item)
3312                self._disp_obj_dict[name] = disp_model
3313                self.model.set_dispersion(param_name, disp_model)
3314                self.state._disp_obj_dict[name] = disp_model.type
3315                # TODO: It's not an array, why update values and weights?
3316                self.model._persistency_dict[param_name] = \
3317                    [self.values, self.weights]
3318                self.state.values = self.values
3319                self.state.weights = self.weights
3320
3321        except Exception:
3322            logging.error(traceback.format_exc())
3323            print "Error in BasePage._paste_poly_help: %s" % \
3324                                    sys.exc_info()[1]
3325
3326    def _set_disp_cb(self, isarray, item):
3327        """
3328        Set cb for array disp
3329        """
3330        if isarray:
3331            item[0].SetValue(False)
3332            item[0].Enable(False)
3333            item[2].Enable(False)
3334            item[3].Show(False)
3335            item[4].Show(False)
3336            item[5].SetValue('')
3337            item[5].Enable(False)
3338            item[6].SetValue('')
3339            item[6].Enable(False)
3340        else:
3341            item[0].Enable()
3342            item[2].Enable()
3343            item[3].Show(True)
3344            item[4].Show(True)
3345            item[5].Enable()
3346            item[6].Enable()
3347
3348    def update_pinhole_smear(self):
3349        """
3350            Method to be called by sub-classes
3351            Moveit; This method doesn't belong here
3352        """
3353        print "BasicPage.update_pinhole_smear was called: skipping"
3354        return
3355
3356    def _read_category_info(self):
3357        """
3358        Reads the categories in from file
3359        """
3360        # # ILL mod starts here - July 2012 kieranrcampbell@gmail.com
3361        self.master_category_dict = defaultdict(list)
3362        self.by_model_dict = defaultdict(list)
3363        self.model_enabled_dict = defaultdict(bool)
3364        categorization_file = CategoryInstaller.get_user_file()
3365        with open(categorization_file, 'rb') as f:
3366            self.master_category_dict = json.load(f)
3367        self._regenerate_model_dict()
3368
3369    def _regenerate_model_dict(self):
3370        """
3371        regenerates self.by_model_dict which has each model name as the
3372        key and the list of categories belonging to that model
3373        along with the enabled mapping
3374        """
3375        self.by_model_dict = defaultdict(list)
3376        for category in self.master_category_dict:
3377            for (model, enabled) in self.master_category_dict[category]:
3378                self.by_model_dict[model].append(category)
3379                self.model_enabled_dict[model] = enabled
3380
3381    def _populate_listbox(self):
3382        """
3383        fills out the category list box
3384        """
3385        uncat_str = 'Customized Models'
3386        self._read_category_info()
3387
3388        self.categorybox.Clear()
3389        cat_list = sorted(self.master_category_dict.keys())
3390        if not uncat_str in cat_list:
3391            cat_list.append(uncat_str)
3392
3393        for category in cat_list:
3394            if category != '':
3395                self.categorybox.Append(category)
3396
3397        if self.categorybox.GetSelection() == wx.NOT_FOUND:
3398            self.categorybox.SetSelection(0)
3399        else:
3400            self.categorybox.SetSelection(\
3401                self.categorybox.GetSelection())
3402        #self._on_change_cat(None)
3403
3404    def _on_change_cat(self, event):
3405        """
3406        Callback for category change action
3407        """
3408        self.model_name = None
3409        category = self.categorybox.GetStringSelection()
3410        if category == None:
3411            return
3412        self.model_box.Clear()
3413
3414        if category == 'Customized Models':
3415            for model in self.model_list_box[category]:
3416                str_m = str(model).split(".")[0]
3417                self.model_box.Append(str_m)
3418
3419        else:
3420            for (model, enabled) in sorted(self.master_category_dict[category],
3421                                      key=lambda name: name[0]):
3422                if(enabled):
3423                    self.model_box.Append(model)
3424
3425    def _fill_model_sizer(self, sizer):
3426        """
3427        fill sizer containing model info
3428        """
3429        # This should only be called once per fit tab
3430        #print "==== Entering _fill_model_sizer"
3431        ##Add model function Details button in fitpanel.
3432        ##The following 3 lines are for Mac. Let JHC know before modifying...
3433        title = "Model"
3434        self.formfactorbox = None
3435        self.multifactorbox = None
3436        self.mbox_description = wx.StaticBox(self, wx.ID_ANY, str(title))
3437        boxsizer1 = wx.StaticBoxSizer(self.mbox_description, wx.VERTICAL)
3438        sizer_cat = wx.BoxSizer(wx.HORIZONTAL)
3439        self.mbox_description.SetForegroundColour(wx.RED)
3440        wx_id = self._ids.next()
3441        self.model_func = wx.Button(self, wx_id, 'Help', size=(80, 23))
3442        self.model_func.Bind(wx.EVT_BUTTON, self.on_function_help_clicked,
3443                             id=wx_id)
3444        self.model_func.SetToolTipString("Full Model Function Help")
3445        wx_id = self._ids.next()
3446        self.model_help = wx.Button(self, wx_id, 'Description', size=(80, 23))
3447        self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked,
3448                             id=wx_id)
3449        self.model_help.SetToolTipString("Short Model Function Description")
3450        wx_id = self._ids.next()
3451        self.model_view = wx.Button(self, wx_id, "Show 2D", size=(80, 23))
3452        self.model_view.Bind(wx.EVT_BUTTON, self._onModel2D, id=wx_id)
3453        hint = "toggle view of model from 1D to 2D  or 2D to 1D"
3454        self.model_view.SetToolTipString(hint)
3455
3456        cat_set_box = wx.StaticBox(self, wx.ID_ANY, 'Category')
3457        sizer_cat_box = wx.StaticBoxSizer(cat_set_box, wx.HORIZONTAL)
3458        sizer_cat_box.SetMinSize((200, 50))
3459        self.categorybox = wx.ComboBox(self, wx.ID_ANY,
3460                                       style=wx.CB_READONLY)
3461        self.categorybox.SetToolTip(wx.ToolTip("Select a Category/Type"))
3462        self._populate_listbox()
3463        wx.EVT_COMBOBOX(self.categorybox, wx.ID_ANY, self._show_combox)
3464        #self.shape_rbutton = wx.RadioButton(self, wx.ID_ANY, 'Shapes',
3465        #                                     style=wx.RB_GROUP)
3466        #self.shape_indep_rbutton = wx.RadioButton(self, wx.ID_ANY,
3467        #                                          "Shape-Independent")
3468        #self.struct_rbutton = wx.RadioButton(self, wx.ID_ANY,
3469        #                                     "Structure Factor ")
3470        #self.plugin_rbutton = wx.RadioButton(self, wx.ID_ANY,
3471        #                                     "Uncategorized")
3472
3473        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3474        #                   id=self.shape_rbutton.GetId())
3475        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3476        #                    id=self.shape_indep_rbutton.GetId())
3477        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3478        #                    id=self.struct_rbutton.GetId())
3479        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3480        #                    id=self.plugin_rbutton.GetId())
3481        #MAC needs SetValue
3482
3483        show_cat_button = wx.Button(self, wx.ID_ANY, "Modify")
3484        cat_tip = "Modify model categories \n"
3485        cat_tip += "(also accessible from the menu bar)."
3486        show_cat_button.SetToolTip( wx.ToolTip(cat_tip) )
3487        show_cat_button.Bind(wx.EVT_BUTTON, self._on_modify_cat)
3488        sizer_cat_box.Add(self.categorybox, 1, wx.RIGHT, 3)
3489        sizer_cat_box.Add((10,10))
3490        sizer_cat_box.Add(show_cat_button)
3491        #self.shape_rbutton.SetValue(True)
3492
3493        sizer_radiobutton = wx.GridSizer(2, 2, 5, 5)
3494        #sizer_radiobutton.Add(self.shape_rbutton)
3495        #sizer_radiobutton.Add(self.shape_indep_rbutton)
3496        sizer_radiobutton.Add((5,5))
3497        sizer_radiobutton.Add(self.model_view, 1, wx.RIGHT, 5)
3498        #sizer_radiobutton.Add(self.plugin_rbutton)
3499        #sizer_radiobutton.Add(self.struct_rbutton)
3500#        sizer_radiobutton.Add((5,5))
3501        sizer_radiobutton.Add(self.model_help, 1, wx.RIGHT | wx.LEFT, 5)
3502#        sizer_radiobutton.Add((5,5))
3503        sizer_radiobutton.Add(self.model_func, 1, wx.RIGHT, 5)
3504        sizer_cat.Add(sizer_cat_box, 1, wx.LEFT, 2.5)
3505        sizer_cat.Add(sizer_radiobutton)
3506        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
3507        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
3508
3509        self.text1 = wx.StaticText(self, wx.ID_ANY, "")
3510        self.text2 = wx.StaticText(self, wx.ID_ANY, "P(Q)*S(Q)")
3511        self.mutifactor_text = wx.StaticText(self, wx.ID_ANY, "No. of Shells: ")
3512        self.mutifactor_text1 = wx.StaticText(self, wx.ID_ANY, "")
3513        self.show_sld_button = wx.Button(self, wx.ID_ANY, "Show SLD Profile")
3514        self.show_sld_button.Bind(wx.EVT_BUTTON, self._on_show_sld)
3515
3516        self.formfactorbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3517        self.formfactorbox.SetToolTip(wx.ToolTip("Select a Model"))
3518        if self.model != None:
3519            self.formfactorbox.SetValue(self.model.name)
3520        self.structurebox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3521        self.multifactorbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3522        self.initialize_combox()
3523        wx.EVT_COMBOBOX(self.formfactorbox, wx.ID_ANY, self._on_select_model)
3524
3525        wx.EVT_COMBOBOX(self.structurebox, wx.ID_ANY, self._on_select_model)
3526        wx.EVT_COMBOBOX(self.multifactorbox, wx.ID_ANY, self._on_select_model)
3527        ## check model type to show sizer
3528        if self.model != None:
3529            print "_set_model_sizer_selection: disabled."
3530            #self._set_model_sizer_selection(self.model)
3531
3532        sizer_selection.Add(self.text1)
3533        sizer_selection.Add((10, 5))
3534        sizer_selection.Add(self.formfactorbox)
3535        sizer_selection.Add((5, 5))
3536        sizer_selection.Add(self.text2)
3537        sizer_selection.Add((5, 5))
3538        sizer_selection.Add(self.structurebox)
3539
3540        mutifactor_selection.Add((13, 5))
3541        mutifactor_selection.Add(self.mutifactor_text)
3542        mutifactor_selection.Add(self.multifactorbox)
3543        mutifactor_selection.Add((5, 5))
3544        mutifactor_selection.Add(self.mutifactor_text1)
3545        mutifactor_selection.Add((10, 5))
3546        mutifactor_selection.Add(self.show_sld_button)
3547
3548        boxsizer1.Add(sizer_cat)
3549        boxsizer1.Add((10, 10))
3550        boxsizer1.Add(sizer_selection)
3551        boxsizer1.Add((10, 10))
3552        boxsizer1.Add(mutifactor_selection)
3553
3554        self._set_multfactor_combobox()
3555        self.multifactorbox.SetSelection(1)
3556        self.show_sld_button.Hide()
3557        sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3558        sizer.Layout()
3559
3560    def on_smear_helper(self, update=False):
3561        """
3562        Help for onSmear if implemented
3563
3564        :param update: force or not to update
3565        """
3566    def reset_page(self, state, first=False):
3567        """
3568        reset the state  if implemented
3569        """
3570    def onSmear(self, event):
3571        """
3572        Create a smear object if implemented
3573        """
3574    def onPinholeSmear(self, event):
3575        """
3576        Create a custom pinhole smear object if implemented
3577        """
3578    def onSlitSmear(self, event):
3579        """
3580        Create a custom slit smear object if implemented
3581        """
3582    def update_slit_smear(self):
3583        """
3584        called by kill_focus on pinhole TextCntrl
3585        to update the changes if implemented
3586        """
3587    def select_param(self, event):
3588        """
3589        Select TextCtrl  checked if implemented
3590        """
3591    def set_data(self, data=None):
3592        """
3593        Sets data if implemented
3594        """
3595    def _is_2D(self):
3596        """
3597        Check if data_name is Data2D if implemented
3598        """
3599    def _on_select_model(self, event=None):
3600        """
3601        call back for model selection if implemented
3602        """
3603    def get_weight_flag(self):
3604        """
3605        Get flag corresponding to a given weighting dI data if implemented
3606        """
3607    def _set_sizer_dispersion(self):
3608        """
3609        draw sizer for dispersity if implemented
3610        """
3611    def get_all_checked_params(self):
3612        """
3613        Found all parameters current check and add them to list of parameters
3614        to fit if implemented
3615        """
3616    def show_npts2fit(self):
3617        """
3618        setValue Npts for fitting if implemented
3619        """
3620    def _onModel2D(self, event):
3621        """
3622        toggle view of model from 1D to 2D  or 2D from 1D if implemented
3623        """
3624
3625class ModelTextCtrl(wx.TextCtrl):
3626    """
3627    Text control for model and fit parameters.
3628    Binds the appropriate events for user interactions.
3629    Default callback methods can be overwritten on initialization
3630
3631    :param kill_focus_callback: callback method for EVT_KILL_FOCUS event
3632    :param set_focus_callback:  callback method for EVT_SET_FOCUS event
3633    :param mouse_up_callback:   callback method for EVT_LEFT_UP event
3634    :param text_enter_callback: callback method for EVT_TEXT_ENTER event
3635
3636    """
3637    ## Set to True when the mouse is clicked while whole string is selected
3638    full_selection = False
3639    ## Call back for EVT_SET_FOCUS events
3640    _on_set_focus_callback = None
3641
3642    def __init__(self, parent, id=-1,
3643                 value=wx.EmptyString,
3644                 pos=wx.DefaultPosition,
3645                 size=wx.DefaultSize,
3646                 style=0,
3647                 validator=wx.DefaultValidator,
3648                 name=wx.TextCtrlNameStr,
3649                 kill_focus_callback=None,
3650                 set_focus_callback=None,
3651                 mouse_up_callback=None,
3652                 text_enter_callback=None):
3653
3654        wx.TextCtrl.__init__(self, parent, id, value, pos,
3655                             size, style, validator, name)
3656
3657        # Bind appropriate events
3658        self._on_set_focus_callback = parent.onSetFocus \
3659            if set_focus_callback is None else set_focus_callback
3660        self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
3661        self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus \
3662            if kill_focus_callback is None else kill_focus_callback)
3663        self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter \
3664            if text_enter_callback is None else text_enter_callback)
3665        if not ON_MAC:
3666            self.Bind(wx.EVT_LEFT_UP, self._highlight_text \
3667                if mouse_up_callback is None else mouse_up_callback)
3668
3669    def _on_set_focus(self, event):
3670        """
3671        Catch when the text control is set in focus to highlight the whole
3672        text if necessary
3673
3674        :param event: mouse event
3675
3676        """
3677        event.Skip()
3678        self.full_selection = True
3679        return self._on_set_focus_callback(event)
3680
3681    def _highlight_text(self, event):
3682        """
3683        Highlight text of a TextCtrl only of no text has be selected
3684
3685        :param event: mouse event
3686
3687        """
3688        # Make sure the mouse event is available to other listeners
3689        event.Skip()
3690        control = event.GetEventObject()
3691        if self.full_selection:
3692            self.full_selection = False
3693            # Check that we have a TextCtrl
3694            if issubclass(control.__class__, wx.TextCtrl):
3695                # Check whether text has been selected,
3696                # if not, select the whole string
3697                (start, end) = control.GetSelection()
3698                if start == end:
3699                    control.SetSelection(-1, -1)
3700
3701    def _silent_kill_focus(self, event):
3702        """
3703        Save the state of the page
3704        """
3705
3706        event.Skip()
3707        #pass
Note: See TracBrowser for help on using the repository browser.