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

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

Data1D class changed to include SESANS Data format

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