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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 16b769b was cb4ef58, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

remove references to internal _model_info attribute from sasview

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