source: sasview/src/sas/sasgui/perspectives/fitting/basepage.py @ 55db501

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 55db501 was 55db501, checked in by jhbakker, 7 years ago

test branch for SESANS class in sesans.py

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