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

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 7673ecd was 7673ecd, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

refactor support for sum model; put tracebacks in logging errors

  • Property mode set to 100644
File size: 144.2 KB
Line 
1"""
2Base Page for fitting
3"""
4import sys
5import os
6import wx
7import numpy
8import time
9import copy
10import math
11import string
12import json
13import logging
14import traceback
15
16from collections import defaultdict
17from wx.lib.scrolledpanel import ScrolledPanel
18
19import sasmodels.sasview_model
20from sas.sasgui.guiframe.panel_base import PanelBase
21from sas.sasgui.guiframe.utils import format_number, check_float, IdList
22from sas.sasgui.guiframe.events import PanelOnFocusEvent
23from sas.sasgui.guiframe.events import StatusEvent
24from sas.sasgui.guiframe.events import AppendBookmarkEvent
25from sas.sasgui.guiframe.dataFitting import Data2D
26from sas.sasgui.guiframe.dataFitting import Data1D
27from sas.sasgui.guiframe.dataFitting import check_data_validity
28from sas.sasgui.guiframe.gui_style import GUIFRAME_ID
29from sas.sascalc.dataloader.data_info import Detector
30from sas.sascalc.dataloader.data_info import Source
31from sas.sasgui.perspectives.fitting.pagestate import PageState
32from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller
33from sas.sasgui.guiframe.documentation_window import DocumentationWindow
34
35
36(PageInfoEvent, EVT_PAGE_INFO) = wx.lib.newevent.NewEvent()
37(PreviousStateEvent, EVT_PREVIOUS_STATE) = wx.lib.newevent.NewEvent()
38(NextStateEvent, EVT_NEXT_STATE) = wx.lib.newevent.NewEvent()
39
40_BOX_WIDTH = 76
41_QMIN_DEFAULT = 0.0005
42_QMAX_DEFAULT = 0.5
43_NPTS_DEFAULT = 50
44#Control panel width
45if sys.platform.count("win32") > 0:
46    PANEL_WIDTH = 450
47    FONT_VARIANT = 0
48    ON_MAC = False
49else:
50    PANEL_WIDTH = 500
51    FONT_VARIANT = 1
52    ON_MAC = True
53
54
55class BasicPage(ScrolledPanel, PanelBase):
56    """
57    This class provide general structure of  fitpanel page
58    """
59    ## Internal name for the AUI manager
60    window_name = "Fit Page"
61    ## Title to appear on top of the window
62    window_caption = "Fit Page "
63
64    # These two buttons have specific IDs since they seem to be created more
65    # frequently than they need to.  In particular, set_dispers_sizer() is
66    # called by _on_select_model
67    ID_BOOKMARK = wx.NewId()
68    ID_DISPERSER_HELP = wx.NewId()
69    _id_pool = IdList()
70
71    def __init__(self, parent, color='blue', **kwargs):
72        """
73        """
74        ScrolledPanel.__init__(self, parent, **kwargs)
75        PanelBase.__init__(self, parent)
76        self.SetupScrolling()
77        #Set window's font size
78        self.SetWindowVariant(variant=FONT_VARIANT)
79        self.SetBackgroundColour(color)
80
81        self._ids = iter(self._id_pool)
82        ## parent of the page
83        self.parent = parent
84        ## manager is the fitting plugin
85        ## owner of the page (fitting plugin)
86        self.event_owner = None
87        ## current model
88        self.model = None
89        self.m_name = None
90        self.index_model = None
91        self.panel = None
92        ## data
93        self.data = None
94        #list of available data
95        self.data_list = []
96        self.mask = None
97        self.uid = wx.NewId()
98        self.graph_id = None
99        #Q range for data set
100        self.qmin_data_set = numpy.inf
101        self.qmax_data_set = None
102        self.npts_data_set = 0
103        ## Q range
104        self.qmin = None
105        self.qmax = None
106        self.qmax_x = _QMAX_DEFAULT
107        self.qmin_x = _QMIN_DEFAULT
108        self.npts_x = _NPTS_DEFAULT
109        ## total number of point: float
110        self.npts = None
111        self.num_points = None
112        ## smear default
113        self.current_smearer = None
114        ## 2D smear accuracy default
115        self.smear2d_accuracy = 'Low'
116        ## slit smear:
117        self.dxl = None
118        self.dxw = None
119        ## pinhole smear
120        self.dx_min = None
121        self.dx_max = None
122        ##semar attrbs
123        self.enable_smearer = None
124        self.disable_smearer = None
125        self.pinhole_smearer = None
126        self.slit_smearer = None
127        ##weigth attrbs
128        self.dI_noweight = None
129        self.dI_didata = None
130        self.dI_sqrdata = None
131        self.dI_idata = None
132        ##other attrbs
133        self.dq_l = None
134        self.dq_r = None
135        self.tcChi = None
136        self.disp_box = None
137        self.model_disp = None
138        self.Npts_fit = None
139        self.Npts_total = None
140        self.theory_qmin = None
141        self.theory_qmax = None
142        self.theory_qmin_x = None
143        self.theory_qmax_x = None
144        self.cb1 = None
145        self.btEditMask = None
146        self.btFit = None
147        self.sld_axes = None
148        self.multi_factor = None
149
150        self.disp_cb_dict = {}
151
152        #self.state = PageState(parent=parent)
153        ## dictionary containing list of models
154        self.model_list_box = {}
155
156        ## Data member to store the dispersion object created
157        self._disp_obj_dict = {}
158        ## selected parameters to apply dispersion
159        self.disp_cb_dict = {}
160        ## smearer object
161        self.enable2D = False
162        self._has_magnetic = False
163        self.magnetic_on = False
164        self.is_mac = ON_MAC
165        self.formfactorbox = None
166        self.structurebox = None
167        self.categorybox = None
168        ##list of model parameters. each item must have same length
169        ## each item related to a given parameters
170        ##[cb state, name, value, "+/-", error of fit, min, max , units]
171        self.parameters = []
172        # non-fittable parameter whose value is astring
173        self.str_parameters = []
174        ## list of parameters to fit , must be like self.parameters
175        self.param_toFit = []
176        ## list of looking like parameters but with non fittable parameters info
177        self.fixed_param = []
178        ## list of looking like parameters but with  fittable parameters info
179        self.fittable_param = []
180        ##list of dispersion parameters
181        self.disp_list = []
182        self.disp_name = ""
183
184        ## list of orientation parameters
185        self.orientation_params = []
186        self.orientation_params_disp = []
187#       Self.model should ALWAYS be None here.  It was set to none above in
188#       this long init setting.  no obvious function call in between setting
189#       and this - commenting out on 4/8/2014 by PDB.  Remove once clear
190#       it is pointless.
191#        if self.model != None:
192#            self.disp_list = self.model.getDispParamList()
193        self.temp_multi_functional = False
194        ##enable model 2D draw
195        self.enable2D = False
196        ## check that the fit range is correct to plot the model again
197        self.fitrange = True
198        ## Create memento to save the current state
199        self.state = PageState(parent=self.parent,
200                               model=self.model, data=self.data)
201        ## flag to determine if state has change
202        self.state_change = False
203        ## save customized array
204        self.values = []
205        self.weights = []
206        ## retrieve saved state
207        self.number_saved_state = 0
208        ## dictionary of saved state
209        self.saved_states = {}
210        ## Create context menu for page
211        self.popUpMenu = wx.Menu()
212
213        wx_id = self._ids.next()
214        self._keep = wx.MenuItem(self.popUpMenu, wx_id, "Add bookmark",
215                                 " Keep the panel status to recall it later")
216        self.popUpMenu.AppendItem(self._keep)
217        self._keep.Enable(False)
218        self._set_bookmark_flag(False)
219        self._set_save_flag(False)
220        wx.EVT_MENU(self, wx_id, self.on_bookmark)
221        self.popUpMenu.AppendSeparator()
222
223        ## Default locations
224        self._default_save_location = os.getcwd()
225        ## save initial state on context menu
226        #self.onSave(event=None)
227        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
228
229        # bind key event
230        self.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
231
232        ## create the basic structure of the panel with empty sizer
233        self.define_page_structure()
234        ## drawing Initial dispersion parameters sizer
235        self.set_dispers_sizer()
236
237        ## layout
238        self.set_layout()
239
240    def set_index_model(self, index):
241        """
242        Index related to this page
243        """
244        self.index_model = index
245
246    def create_default_data(self):
247        """
248        Given the user selection, creates a 1D or 2D data
249        Only when the page is on theory mode.
250        """
251        if not hasattr(self, "model_view"):
252            return
253        toggle_mode_on = self.model_view.IsEnabled()
254        if toggle_mode_on:
255            if self.enable2D and not check_data_validity(self.data):
256                self._create_default_2d_data()
257            else:
258                if self.pointsbox.GetValue():
259                    self._create_log_1d_data()
260                else:
261                    self._create_default_1d_data()
262
263            if self.model != None:
264                if not self.data.is_data:
265                    self._manager.page_finder[self.uid].set_fit_data(data=\
266                                                                [self.data])
267            self.on_smear_helper(update=True)
268            self.state.enable_smearer = self.enable_smearer.GetValue()
269            self.state.disable_smearer = self.disable_smearer.GetValue()
270            self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
271            self.state.slit_smearer = self.slit_smearer.GetValue()
272
273    def _create_default_1d_data(self):
274        """
275        Create default data for fitting perspective
276        Only when the page is on theory mode.
277        :warning: This data is never plotted.
278
279        """
280        x = numpy.linspace(start=self.qmin_x, stop=self.qmax_x,
281                           num=self.npts_x, endpoint=True)
282        self.data = Data1D(x=x)
283        self.data.xaxis('\\rm{Q}', "A^{-1}")
284        self.data.yaxis('\\rm{Intensity}', "cm^{-1}")
285        self.data.is_data = False
286        self.data.id = str(self.uid) + " data"
287        self.data.group_id = str(self.uid) + " Model1D"
288
289    def _create_log_1d_data(self):
290        """
291        Create log-spaced data for fitting perspective
292        Only when the page is on theory mode.
293        :warning: This data is never plotted.
294
295        """
296        if self.qmin_x >= 1.e-10:
297            qmin = numpy.log10(self.qmin_x)
298        else:
299            qmin = -10.
300
301        if self.qmax_x <= 1.e10:
302            qmax = numpy.log10(self.qmax_x)
303        else:
304            qmax = 10.
305
306        x = numpy.logspace(start=qmin, stop=qmax,
307                           num=self.npts_x, endpoint=True, base=10.0)
308        self.data = Data1D(x=x)
309        self.data.xaxis('\\rm{Q}', "A^{-1}")
310        self.data.yaxis('\\rm{Intensity}', "cm^{-1}")
311        self.data.is_data = False
312        self.data.id = str(self.uid) + " data"
313        self.data.group_id = str(self.uid) + " Model1D"
314
315    def _create_default_2d_data(self):
316        """
317        Create 2D data by default
318        Only when the page is on theory mode.
319        :warning: This data is never plotted.
320        """
321        self.data = Data2D()
322        qmax = self.qmax_x / math.sqrt(2)
323        self.data.xaxis('\\rm{Q_{x}}', 'A^{-1}')
324        self.data.yaxis('\\rm{Q_{y}}', 'A^{-1}')
325        self.data.is_data = False
326        self.data.id = str(self.uid) + " data"
327        self.data.group_id = str(self.uid) + " Model2D"
328        ## Default values
329        self.data.detector.append(Detector())
330        index = len(self.data.detector) - 1
331        self.data.detector[index].distance = 8000   # mm
332        self.data.source.wavelength = 6             # A
333        self.data.detector[index].pixel_size.x = 5  # mm
334        self.data.detector[index].pixel_size.y = 5  # mm
335        self.data.detector[index].beam_center.x = qmax
336        self.data.detector[index].beam_center.y = qmax
337        ## create x_bins and y_bins of the model 2D
338        #pixel_width_x = self.data.detector[index].pixel_size.x
339        #pixel_width_y = self.data.detector[index].pixel_size.y
340        #center_x = self.data.detector[index].beam_center.x/pixel_width_x
341        #center_y = self.data.detector[index].beam_center.y/pixel_width_y
342        # theory default: assume the beam
343        #center is located at the center of sqr detector
344        xmax = qmax
345        xmin = -qmax
346        ymax = qmax
347        ymin = -qmax
348        qstep = self.npts_x
349
350        x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)
351        y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True)
352        ## use data info instead
353        new_x = numpy.tile(x, (len(y), 1))
354        new_y = numpy.tile(y, (len(x), 1))
355        new_y = new_y.swapaxes(0, 1)
356        # all data reuire now in 1d array
357        qx_data = new_x.flatten()
358        qy_data = new_y.flatten()
359        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
360        # set all True (standing for unmasked) as default
361        mask = numpy.ones(len(qx_data), dtype=bool)
362        # calculate the range of qx and qy: this way,
363        # it is a little more independent
364        #x_size = xmax - xmin
365        #y_size = ymax - ymin
366        # store x and y bin centers in q space
367        x_bins = x
368        y_bins = y
369        # bin size: x- & y-directions
370        #xstep = x_size / len(x_bins - 1)
371        #ystep = y_size / len(y_bins - 1)
372
373        self.data.source = Source()
374        self.data.data = numpy.ones(len(mask))
375        self.data.err_data = numpy.ones(len(mask))
376        self.data.qx_data = qx_data
377        self.data.qy_data = qy_data
378        self.data.q_data = q_data
379        self.data.mask = mask
380        self.data.x_bins = x_bins
381        self.data.y_bins = y_bins
382        # max and min taking account of the bin sizes
383        self.data.xmin = xmin
384        self.data.xmax = xmax
385        self.data.ymin = ymin
386        self.data.ymax = ymax
387
388    def on_set_focus(self, event):
389        """
390        On Set Focus, update guimanger and menu
391        """
392        if self._manager is not None:
393            wx.PostEvent(self._manager.parent, PanelOnFocusEvent(panel=self))
394            self.on_tap_focus()
395
396    def on_tap_focus(self):
397        """
398        Update menu1 on cliking the page tap
399        """
400        if self._manager.menu1 != None:
401            chain_menu = self._manager.menu1.FindItemById(\
402                                                   self._manager.id_reset_flag)
403            chain_menu.Enable(self.batch_on)
404            sim_menu = self._manager.menu1.FindItemById(self._manager.id_simfit)
405            flag = self.data.is_data\
406                            and (self.model != None)
407            sim_menu.Enable(not self.batch_on and flag)
408            batch_menu = \
409                    self._manager.menu1.FindItemById(self._manager.id_batchfit)
410            batch_menu.Enable(self.batch_on and flag)
411
412    def set_page_info(self, page_info):
413        """
414        set some page important information at once
415        """
416#       THIS METHOD/FUNCTION NO LONGE APPEARS TO BE CALLED.  Started up program
417#       and started new fit window and PR and Invariant and a fit in fitting
418#       but never entered this routine which should be an initialization
419#       routine.  Leave for a while but probably something to clean up at
420#       some point?
421#
422#       PDB April 13 2014
423#
424        ##window_name
425        self.window_name = page_info.window_name
426        ##window_caption
427        self.window_caption = page_info.window_caption
428        ## manager is the fitting plugin
429        self._manager = page_info.manager
430        ## owner of the page (fitting plugin)
431        self.event_owner = page_info.event_owner
432        ## current model
433        self.model = page_info.model
434        ## data
435        self.data = page_info.data
436        ## dictionary containing list of models
437        self.model_list_box = page_info.model_list_box
438        ## Data member to store the dispersion object created
439        self.populate_box(model_dict=self.model_list_box)
440
441    def onContextMenu(self, event):
442        """
443        Retrieve the state selected state
444        """
445        # Skipping the save state functionality for release 0.9.0
446        #return
447
448        pos = event.GetPosition()
449        pos = self.ScreenToClient(pos)
450
451        self.PopupMenu(self.popUpMenu, pos)
452
453    def onUndo(self, event):
454        """
455        Cancel the previous action
456        """
457        event = PreviousStateEvent(page=self)
458        wx.PostEvent(self.parent, event)
459
460    def onRedo(self, event):
461        """
462        Restore the previous action cancelled
463        """
464        event = NextStateEvent(page=self)
465        wx.PostEvent(self.parent, event)
466
467    def define_page_structure(self):
468        """
469        Create empty sizer for a panel
470        """
471        self.vbox = wx.BoxSizer(wx.VERTICAL)
472        self.sizer0 = wx.BoxSizer(wx.VERTICAL)
473        self.sizer1 = wx.BoxSizer(wx.VERTICAL)
474        self.sizer2 = wx.BoxSizer(wx.VERTICAL)
475        self.sizer3 = wx.BoxSizer(wx.VERTICAL)
476        self.sizer4 = wx.BoxSizer(wx.VERTICAL)
477        self.sizer5 = wx.BoxSizer(wx.VERTICAL)
478        self.sizer6 = wx.BoxSizer(wx.VERTICAL)
479
480        self.sizer0.SetMinSize((PANEL_WIDTH, -1))
481        self.sizer1.SetMinSize((PANEL_WIDTH, -1))
482        self.sizer2.SetMinSize((PANEL_WIDTH, -1))
483        self.sizer3.SetMinSize((PANEL_WIDTH, -1))
484        self.sizer4.SetMinSize((PANEL_WIDTH, -1))
485        self.sizer5.SetMinSize((PANEL_WIDTH, -1))
486        self.sizer6.SetMinSize((PANEL_WIDTH, -1))
487
488        self.vbox.Add(self.sizer0)
489        self.vbox.Add(self.sizer1)
490        self.vbox.Add(self.sizer2)
491        self.vbox.Add(self.sizer3)
492        self.vbox.Add(self.sizer4)
493        self.vbox.Add(self.sizer5)
494        self.vbox.Add(self.sizer6)
495
496    def set_layout(self):
497        """
498        layout
499        """
500        self.vbox.Layout()
501        self.vbox.Fit(self)
502        self.SetSizer(self.vbox)
503        self.Centre()
504
505    def set_owner(self, owner):
506        """
507        set owner of fitpage
508
509        :param owner: the class responsible of plotting
510
511        """
512        self.event_owner = owner
513        self.state.event_owner = owner
514
515    def get_state(self):
516        """
517        return the current page state
518        """
519        return self.state
520
521    def get_data(self):
522        """
523        return the current data
524        """
525        return self.data
526
527    def get_data_list(self):
528        """
529        return the current data
530        """
531        return self.data_list
532
533    def set_manager(self, manager):
534        """
535        set panel manager
536
537        :param manager: instance of plugin fitting
538
539        """
540        self._manager = manager
541        self.state.manager = manager
542
543    def populate_box(self, model_dict):
544        """
545        Store list of model
546
547        :param model_dict: dictionary containing list of models
548
549        """
550        self.model_list_box = model_dict
551        self.state.model_list_box = self.model_list_box
552        self.initialize_combox()
553
554    def set_model_dictionary(self, model_dict):
555        """
556        Store a dictionary linking model name -> model object
557
558        :param model_dict: dictionary containing list of models
559        """
560        self.model_dict = model_dict
561
562    def initialize_combox(self):
563        """
564        put default value in the combobox
565        """
566        ## fill combox box
567        if self.model_list_box is None:
568            return
569        if len(self.model_list_box) > 0:
570        ## This is obsolete code since form factor box is no longer static.
571        ## It is now set dynamically through _show_combox and _show_combos_helper
572        ## These are called for first time by formfactor_combo_init
573        ## itself called from fitpanel only.  If we find that I'm wrong and
574        ## we DO need to initialize somehow here - do it by a call to
575        ## formfactor_combo_init
576        ## self.formfator_combo_init()
577        ## BUT NOT HERE -- make it last line of this
578        ## method so that structure box is populated before _show_comboox_helper
579        ## is called.  Otherwise wx will complain mightily:-)
580        ##
581        ## Also change the name to initiatlize_structurebox along with changes
582        ## to other combobox methods (_populate_listbox --> _populate_categorybox
583        ## etc )
584        ##
585        ##     PDB 4/26/2014
586#            self._populate_box(self.formfactorbox,
587#                               self.model_list_box["Shapes"])
588            self._populate_box(self.structurebox,
589                               self.model_list_box["Structure Factors"])
590            self.structurebox.Insert("None", 0, None)
591            self.structurebox.SetSelection(0)
592            self.structurebox.Hide()
593            self.text2.Hide()
594            self.structurebox.Disable()
595            self.text2.Disable()
596
597            if self.model.__class__ in self.model_list_box["P(Q)*S(Q)"]:
598                self.structurebox.Show()
599                self.text2.Show()
600                self.structurebox.Enable()
601                self.text2.Enable()
602
603
604    def set_dispers_sizer(self):
605        """
606        fill sizer containing dispersity info
607        """
608        #print "==== entering set_dispers_sizer ==="
609        self.sizer4.Clear(True)
610        name = "Polydispersity and Orientational Distribution"
611        box_description = wx.StaticBox(self, wx.ID_ANY, name)
612        box_description.SetForegroundColour(wx.BLUE)
613        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
614        #----------------------------------------------------
615        self.disable_disp = wx.RadioButton(self, wx.ID_ANY, 'Off', (10, 10),
616                                           style=wx.RB_GROUP)
617        self.enable_disp = wx.RadioButton(self, wx.ID_ANY, 'On', (10, 30))
618        # best size for MAC and PC
619        if ON_MAC:
620            size_q = (30, 20)
621        else:
622            size_q = (20, 15)
623        self.disp_help_bt = wx.Button(self, self.ID_DISPERSER_HELP, '?',
624                                      style=wx.BU_EXACTFIT,
625                                      size=size_q)
626        self.disp_help_bt.Bind(wx.EVT_BUTTON, self.on_pd_help_clicked,
627                               id=self.disp_help_bt.GetId())
628        self.disp_help_bt.SetToolTipString("Helps for Polydispersion.")
629
630        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
631                  id=self.disable_disp.GetId())
632        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
633                  id=self.enable_disp.GetId())
634        #MAC needs SetValue
635        self.disable_disp.SetValue(True)
636        sizer_dispersion = wx.BoxSizer(wx.HORIZONTAL)
637        sizer_dispersion.Add((20, 20))
638        name = ""  # Polydispersity and \nOrientational Distribution "
639        sizer_dispersion.Add(wx.StaticText(self, wx.ID_ANY, name))
640        sizer_dispersion.Add(self.enable_disp)
641        sizer_dispersion.Add((20, 20))
642        sizer_dispersion.Add(self.disable_disp)
643        sizer_dispersion.Add((25, 20))
644        sizer_dispersion.Add(self.disp_help_bt)
645
646        ## fill a sizer for dispersion
647        boxsizer1.Add(sizer_dispersion, 0,
648                      wx.TOP|wx.BOTTOM|wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE,
649                      border=5)
650        self.sizer4_4 = wx.GridBagSizer(6, 5)
651
652        boxsizer1.Add(self.sizer4_4)
653        #-----------------------------------------------------
654        self.sizer4.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
655        self.sizer4_4.Layout()
656        self.sizer4.Layout()
657        self.Layout()
658
659        self.Refresh()
660        ## saving the state of enable dispersity button
661        self.state.enable_disp = self.enable_disp.GetValue()
662        self.state.disable_disp = self.disable_disp.GetValue()
663        self.SetupScrolling()
664
665    def onResetModel(self, event):
666        """
667        Reset model state
668        """
669        menu = event.GetEventObject()
670        ## post help message for the selected model
671        msg = menu.GetHelpString(event.GetId())
672        msg += " reloaded"
673        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
674        self.Show(False)
675        name = menu.GetLabel(event.GetId())
676        self._on_select_model_helper()
677        if self.model != None:
678            self.m_name = self.model.name
679        if name in self.saved_states.keys():
680            previous_state = self.saved_states[name]
681            ## reset state of checkbox,textcrtl  and  regular parameters value
682
683            self.reset_page(previous_state)
684        self.state.m_name = self.m_name
685        self.Show(True)
686
687    def on_preview(self, event):
688        """
689        Report the current fit results
690        """
691        # Get plot image from plotpanel
692        images, canvases = self.get_images()
693        # get the report dialog
694        self.state.report(images, canvases)
695
696    def on_save(self, event):
697        """
698        Save the current state into file
699        """
700        self.save_current_state()
701        new_state = self.state.clone()
702        # Ask the user the location of the file to write to.
703        path = None
704        if self.parent != None:
705            self._default_save_location = \
706                        self._manager.parent._default_save_location
707        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location,
708                            self.window_caption, "*.fitv", wx.SAVE)
709
710        if dlg.ShowModal() == wx.ID_OK:
711            path = dlg.GetPath()
712            self._default_save_location = os.path.dirname(path)
713            self._manager.parent._default_save_location = \
714                                self._default_save_location
715        else:
716            return None
717        # MAC always needs the extension for saving
718        extens = ".fitv"
719        # Make sure the ext included in the file name
720        fName = os.path.splitext(path)[0] + extens
721        #the manager write the state into file
722        self._manager.save_fit_state(filepath=fName, fitstate=new_state)
723        return new_state
724
725    def on_copy(self, event):
726        """
727        Copy Parameter values to the clipboad
728        """
729        if event != None:
730            event.Skip()
731        # It seems MAC needs wxCallAfter
732        if event.GetId() == GUIFRAME_ID.COPYEX_ID:
733            print "copy excel"
734            wx.CallAfter(self.get_copy_excel)
735        elif event.GetId() == GUIFRAME_ID.COPYLAT_ID:
736            print "copy latex"
737            wx.CallAfter(self.get_copy_latex)
738        else:
739            wx.CallAfter(self.get_copy)
740
741
742    def on_paste(self, event):
743        """
744        Paste Parameter values to the panel if possible
745        """
746        #if event != None:
747        #    event.Skip()
748        # It seems MAC needs wxCallAfter for the setvalues
749        # for multiple textctrl items, otherwise it tends to crash once a while
750        wx.CallAfter(self.get_paste)
751        # messages depending on the flag
752        #self._copy_info(True)
753
754    def _copy_info(self, flag):
755        """
756        Send event dpemding on flag
757
758        : Param flag: flag that distinguish event
759        """
760        # messages depending on the flag
761        if flag == None:
762            msg = " Parameter values are copied to the clipboard..."
763            infor = 'warning'
764        elif flag:
765            msg = " Parameter values are pasted from the clipboard..."
766            infor = "warning"
767        else:
768            msg = "Error occurred: "
769            msg += "No valid parameter values to paste from the clipboard..."
770            infor = "warning"
771        # inform msg to wx
772        wx.PostEvent(self._manager.parent,
773                     StatusEvent(status=msg, info=infor))
774
775    def _get_time_stamp(self):
776        """
777        return time and date stings
778        """
779        # date and time
780        year, month, day, hour, minute, second, _, _, _ = time.localtime()
781        current_time = str(hour) + ":" + str(minute) + ":" + str(second)
782        current_date = str(month) + "/" + str(day) + "/" + str(year)
783        return current_time, current_date
784
785    def on_bookmark(self, event):
786        """
787        save history of the data and model
788        """
789        if self.model == None:
790            msg = "Can not bookmark; Please select Data and Model first..."
791            wx.MessageBox(msg, 'Info')
792            return
793        self.save_current_state()
794        new_state = self.state.clone()
795        ##Add model state on context menu
796        self.number_saved_state += 1
797        current_time, current_date = self._get_time_stamp()
798        #name= self.model.name+"[%g]"%self.number_saved_state
799        name = "Fitting: %g]" % self.number_saved_state
800        name += self.model.__class__.__name__
801        name += "bookmarked at %s on %s" % (current_time, current_date)
802        self.saved_states[name] = new_state
803
804        ## Add item in the context menu
805        msg = "Model saved at %s on %s" % (current_time, current_date)
806        ## post help message for the selected model
807        msg += " Saved! right click on this page to retrieve this model"
808        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
809
810        self.popUpMenu.Append(self.ID_BOOKMARK, name, str(msg))
811        wx.EVT_MENU(self, self.ID_BOOKMARK, self.onResetModel)
812        wx.PostEvent(self._manager.parent,
813                     AppendBookmarkEvent(title=name,
814                                         hint=str(msg),
815                                         handler=self._back_to_bookmark))
816
817    def _back_to_bookmark(self, event):
818        """
819        Back to bookmark
820        """
821        self._manager.on_perspective(event)
822        self.onResetModel(event)
823        self._draw_model()
824
825    def onSetFocus(self, evt):
826        """
827        highlight the current textcrtl and hide the error text control shown
828        after fitting
829        """
830        return
831
832    def read_file(self, path):
833        """
834        Read two columns file
835
836        :param path: the path to the file to read
837
838        """
839        try:
840            if path == None:
841                status = " Selected Distribution was not loaded: %s" % path
842                wx.PostEvent(self._manager.parent,
843                             StatusEvent(status=status))
844                return None, None
845            input_f = open(path, 'r')
846            buff = input_f.read()
847            lines = buff.split('\n')
848            input_f.close()
849            angles = []
850            weights = []
851            for line in lines:
852                toks = line.split()
853                try:
854                    angle = float(toks[0])
855                    weight = float(toks[1])
856                    angles.append(angle)
857                    weights.append(weight)
858                except Exception:
859                    # Skip non-data lines
860                    logging.error(traceback.format_exc())
861            return numpy.array(angles), numpy.array(weights)
862        except:
863            raise
864
865    def createMemento(self):
866        """
867        return the current state of the page
868        """
869        return self.state.clone()
870
871    def save_current_state(self):
872        """
873        Store current state
874        """
875        ## save model option
876        if self.model != None:
877            self.disp_list = self.model.getDispParamList()
878            self.state.disp_list = copy.deepcopy(self.disp_list)
879            self.state.model = self.model.clone()
880
881            #model combobox: complex code because of mac's silent error
882            if self.structurebox != None:
883                if self.structurebox.IsShown():
884                    self.state.structurecombobox = 'None'
885                    s_select = self.structurebox.GetSelection()
886                    if s_select > 0:
887                        self.state.structurecombobox = self.structurebox.\
888                        GetString(s_select)
889            if self.formfactorbox != None:
890                f_select = self.formfactorbox.GetSelection()
891                if f_select > 0:
892                    self.state.formfactorcombobox = self.formfactorbox.\
893                    GetString(f_select)
894        if self.categorybox != None:
895            cb_select = self.categorybox.GetSelection()
896            if cb_select > 0:
897                self.state.categorycombobox = self.categorybox.\
898                GetString(cb_select)
899
900        self.state.enable2D = copy.deepcopy(self.enable2D)
901        self.state.values = copy.deepcopy(self.values)
902        self.state.weights = copy.deepcopy(self.weights)
903        ## save data
904        self.state.data = copy.deepcopy(self.data)
905        self.state.qmax_x = self.qmax_x
906        self.state.qmin_x = self.qmin_x
907        self.state.dI_noweight = copy.deepcopy(self.dI_noweight.GetValue())
908        self.state.dI_didata = copy.deepcopy(self.dI_didata.GetValue())
909        self.state.dI_sqrdata = copy.deepcopy(self.dI_sqrdata.GetValue())
910        self.state.dI_idata = copy.deepcopy(self.dI_idata.GetValue())
911        self.state.dq_l = self.dq_l
912        self.state.dq_r = self.dq_r
913        if hasattr(self, "enable_disp"):
914            self.state.enable_disp = self.enable_disp.GetValue()
915            self.state.disable_disp = self.disable_disp.GetValue()
916
917        self.state.smearer = copy.deepcopy(self.current_smearer)
918        if hasattr(self, "enable_smearer"):
919            self.state.enable_smearer = \
920                                copy.deepcopy(self.enable_smearer.GetValue())
921            self.state.disable_smearer = \
922                                copy.deepcopy(self.disable_smearer.GetValue())
923
924        self.state.pinhole_smearer = \
925                                copy.deepcopy(self.pinhole_smearer.GetValue())
926        self.state.dx_max = copy.deepcopy(self.dx_max)
927        self.state.dx_min = copy.deepcopy(self.dx_min)
928        self.state.dxl = copy.deepcopy(self.dxl)
929        self.state.dxw = copy.deepcopy(self.dxw)
930        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())
931
932        if len(self._disp_obj_dict) > 0:
933            for k, v in self._disp_obj_dict.iteritems():
934                self.state._disp_obj_dict[k] = v
935
936            self.state.values = copy.deepcopy(self.values)
937            self.state.weights = copy.deepcopy(self.weights)
938        ## save plotting range
939        self._save_plotting_range()
940
941        self.state.orientation_params = []
942        self.state.orientation_params_disp = []
943        self.state.parameters = []
944        self.state.fittable_param = []
945        self.state.fixed_param = []
946        self.state.str_parameters = []
947
948        ## save checkbutton state and txtcrtl values
949        self._copy_parameters_state(self.str_parameters,
950                                    self.state.str_parameters)
951        self._copy_parameters_state(self.orientation_params,
952                                     self.state.orientation_params)
953        self._copy_parameters_state(self.orientation_params_disp,
954                                     self.state.orientation_params_disp)
955
956        self._copy_parameters_state(self.parameters, self.state.parameters)
957        self._copy_parameters_state(self.fittable_param,
958                                     self.state.fittable_param)
959        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
960        #save chisqr
961        self.state.tcChi = self.tcChi.GetValue()
962
963    def save_current_state_fit(self):
964        """
965        Store current state for fit_page
966        """
967        ## save model option
968        if self.model != None:
969            self.disp_list = self.model.getDispParamList()
970            self.state.disp_list = copy.deepcopy(self.disp_list)
971            self.state.model = self.model.clone()
972
973        self.state.enable2D = copy.deepcopy(self.enable2D)
974        self.state.values = copy.deepcopy(self.values)
975        self.state.weights = copy.deepcopy(self.weights)
976        ## save data
977        self.state.data = copy.deepcopy(self.data)
978
979        if hasattr(self, "enable_disp"):
980            self.state.enable_disp = self.enable_disp.GetValue()
981            self.state.disable_disp = self.disable_disp.GetValue()
982
983        self.state.smearer = copy.deepcopy(self.current_smearer)
984        if hasattr(self, "enable_smearer"):
985            self.state.enable_smearer = \
986                                copy.deepcopy(self.enable_smearer.GetValue())
987            self.state.disable_smearer = \
988                                copy.deepcopy(self.disable_smearer.GetValue())
989
990        self.state.pinhole_smearer = \
991                                copy.deepcopy(self.pinhole_smearer.GetValue())
992        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())
993        self.state.dI_noweight = copy.deepcopy(self.dI_noweight.GetValue())
994        self.state.dI_didata = copy.deepcopy(self.dI_didata.GetValue())
995        self.state.dI_sqrdata = copy.deepcopy(self.dI_sqrdata.GetValue())
996        self.state.dI_idata = copy.deepcopy(self.dI_idata.GetValue())
997        if hasattr(self, "disp_box") and self.disp_box != None:
998            self.state.disp_box = self.disp_box.GetCurrentSelection()
999
1000            if len(self.disp_cb_dict) > 0:
1001                for k, v in self.disp_cb_dict.iteritems():
1002                    if v == None:
1003                        self.state.disp_cb_dict[k] = v
1004                    else:
1005                        try:
1006                            self.state.disp_cb_dict[k] = v.GetValue()
1007                        except:
1008                            self.state.disp_cb_dict[k] = None
1009            if len(self._disp_obj_dict) > 0:
1010                for k, v in self._disp_obj_dict.iteritems():
1011                    self.state._disp_obj_dict[k] = v
1012
1013            self.state.values = copy.deepcopy(self.values)
1014            self.state.weights = copy.deepcopy(self.weights)
1015
1016        ## save plotting range
1017        self._save_plotting_range()
1018
1019        ## save checkbutton state and txtcrtl values
1020        self._copy_parameters_state(self.orientation_params,
1021                                    self.state.orientation_params)
1022        self._copy_parameters_state(self.orientation_params_disp,
1023                                    self.state.orientation_params_disp)
1024        self._copy_parameters_state(self.parameters, self.state.parameters)
1025        self._copy_parameters_state(self.fittable_param,
1026                                    self.state.fittable_param)
1027        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
1028
1029    def check_invalid_panel(self):
1030        """
1031        check if the user can already perform some action with this panel
1032        """
1033        if self.data is None:
1034            self.disable_smearer.SetValue(True)
1035            self.disable_disp.SetValue(True)
1036            msg = "Please load Data and select Model to start..."
1037            wx.MessageBox(msg, 'Info')
1038            return  True
1039
1040    def set_model_state(self, state):
1041        """
1042        reset page given a model state
1043        """
1044        self.disp_cb_dict = state.disp_cb_dict
1045        self.disp_list = state.disp_list
1046
1047        ## set the state of the radio box
1048        #self.shape_rbutton.SetValue(state.shape_rbutton)
1049        #self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
1050        #self.struct_rbutton.SetValue(state.struct_rbutton)
1051        #self.plugin_rbutton.SetValue(state.plugin_rbutton)
1052
1053        ## fill model combobox
1054        self._show_combox_helper()
1055        #select the current model
1056        try:
1057            # to support older version
1058            category_pos = int(state.categorycombobox)
1059        except:
1060            category_pos = 0
1061            for ind_cat in range(self.categorybox.GetCount()):
1062                if self.categorycombobox.GetString(ind_cat) == \
1063                                        state.categorycombobox:
1064                    category_pos = int(ind_cat)
1065                    break
1066
1067        self.categorybox.Select(category_pos)
1068        try:
1069            # to support older version
1070            formfactor_pos = int(state.formfactorcombobox)
1071        except:
1072            formfactor_pos = 0
1073            for ind_form in range(self.formfactorbox.GetCount()):
1074                if self.formfactorbox.GetString(ind_form) == \
1075                                        state.formfactorcombobox:
1076                    formfactor_pos = int(ind_form)
1077                    break
1078
1079        self.formfactorbox.Select(formfactor_pos)
1080
1081        try:
1082            # to support older version
1083            structfactor_pos = int(state.structurecombobox)
1084        except:
1085            structfactor_pos = 0
1086            for ind_struct in range(self.structurebox.GetCount()):
1087                if self.structurebox.GetString(ind_struct) == \
1088                                        state.structurecombobox:
1089                    structfactor_pos = int(ind_struct)
1090                    break
1091
1092        self.structurebox.SetSelection(structfactor_pos)
1093
1094        if state.multi_factor != None:
1095            self.multifactorbox.SetSelection(state.multi_factor)
1096
1097        ## reset state of checkbox,textcrtl  and  regular parameters value
1098        self._reset_parameters_state(self.orientation_params_disp,
1099                                     state.orientation_params_disp)
1100        self._reset_parameters_state(self.orientation_params,
1101                                     state.orientation_params)
1102        self._reset_parameters_state(self.str_parameters,
1103                                     state.str_parameters)
1104        self._reset_parameters_state(self.parameters, state.parameters)
1105        ## display dispersion info layer
1106        self.enable_disp.SetValue(state.enable_disp)
1107        self.disable_disp.SetValue(state.disable_disp)
1108
1109        if hasattr(self, "disp_box") and self.disp_box != None:
1110            self.disp_box.SetSelection(state.disp_box)
1111            n = self.disp_box.GetCurrentSelection()
1112            dispersity = self.disp_box.GetClientData(n)
1113            name = dispersity.__name__
1114
1115            self._set_dipers_Param(event=None)
1116
1117            if name == "ArrayDispersion":
1118
1119                for item in self.disp_cb_dict.keys():
1120
1121                    if hasattr(self.disp_cb_dict[item], "SetValue"):
1122                        self.disp_cb_dict[item].SetValue(\
1123                                                    state.disp_cb_dict[item])
1124                        # Create the dispersion objects
1125                        from sas.models.dispersion_models import ArrayDispersion
1126                        disp_model = ArrayDispersion()
1127                        if hasattr(state, "values") and \
1128                                 self.disp_cb_dict[item].GetValue() == True:
1129                            if len(state.values) > 0:
1130                                self.values = state.values
1131                                self.weights = state.weights
1132                                disp_model.set_weights(self.values,
1133                                                       state.weights)
1134                            else:
1135                                self._reset_dispersity()
1136
1137                        self._disp_obj_dict[item] = disp_model
1138                        # Set the new model as the dispersion object
1139                        #for the selected parameter
1140                        self.model.set_dispersion(item, disp_model)
1141
1142                        self.model._persistency_dict[item] = \
1143                                                [state.values, state.weights]
1144
1145            else:
1146                keys = self.model.getParamList()
1147                for item in keys:
1148                    if item in self.disp_list and \
1149                        not item in self.model.details:
1150                        self.model.details[item] = ["", None, None]
1151                self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1152                self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1153        ## smearing info  restore
1154        if hasattr(self, "enable_smearer"):
1155            ## set smearing value whether or not the data
1156            #contain the smearing info
1157            self.enable_smearer.SetValue(state.enable_smearer)
1158            self.disable_smearer.SetValue(state.disable_smearer)
1159            self.onSmear(event=None)
1160        self.pinhole_smearer.SetValue(state.pinhole_smearer)
1161        self.slit_smearer.SetValue(state.slit_smearer)
1162
1163        self.dI_noweight.SetValue(state.dI_noweight)
1164        self.dI_didata.SetValue(state.dI_didata)
1165        self.dI_sqrdata.SetValue(state.dI_sqrdata)
1166        self.dI_idata.SetValue(state.dI_idata)
1167
1168        ## we have two more options for smearing
1169        if self.pinhole_smearer.GetValue():
1170            self.onPinholeSmear(event=None)
1171        elif self.slit_smearer.GetValue():
1172            self.onSlitSmear(event=None)
1173
1174        ## reset state of checkbox,textcrtl  and dispersity parameters value
1175        self._reset_parameters_state(self.fittable_param, state.fittable_param)
1176        self._reset_parameters_state(self.fixed_param, state.fixed_param)
1177
1178        ## draw the model with previous parameters value
1179        self._onparamEnter_helper()
1180        self.select_param(event=None)
1181        #Save state_fit
1182        self.save_current_state_fit()
1183        self._lay_out()
1184        self.Refresh()
1185
1186    def reset_page_helper(self, state):
1187        """
1188        Use page_state and change the state of existing page
1189
1190        :precondition: the page is already drawn or created
1191
1192        :postcondition: the state of the underlying data change as well as the
1193            state of the graphic interface
1194        """
1195        if state == None:
1196            return
1197        # set data, etc. from the state
1198        # reset page between theory and fitting from bookmarking
1199        #if state.data == None:
1200        #    data = None
1201        #else:
1202        data = state.data
1203
1204        if data == None:
1205            data_min = state.qmin
1206            data_max = state.qmax
1207            self.qmin_x = data_min
1208            self.qmax_x = data_max
1209            self.qmin.SetValue(str(data_min))
1210            self.qmax.SetValue(str(data_max))
1211
1212            self.state.data = data
1213            self.state.qmin = self.qmin_x
1214            self.state.qmax = self.qmax_x
1215        else:
1216            self.set_data(data)
1217
1218        self.enable2D = state.enable2D
1219        try:
1220            self.magnetic_on = state.magnetic_on
1221        except:
1222            # Backward compatibility (for older state files)
1223            self.magnetic_on = False
1224
1225        self.disp_cb_dict = state.disp_cb_dict
1226        self.disp_list = state.disp_list
1227
1228        ## set the state of the radio box
1229        #self.shape_rbutton.SetValue(state.shape_rbutton)
1230        #self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
1231        #self.struct_rbutton.SetValue(state.struct_rbutton)
1232        #self.plugin_rbutton.SetValue(state.plugin_rbutton)
1233
1234        ## fill model combobox
1235        self._show_combox_helper()
1236        #select the current model
1237        try:
1238            # to support older version
1239            category_pos = int(state.categorycombobox)
1240        except:
1241            category_pos = 0
1242            for ind_cat in range(self.categorybox.GetCount()):
1243                if self.categorybox.GetString(ind_cat) == \
1244                                        state.categorycombobox:
1245                    category_pos = int(ind_cat)
1246                    break
1247
1248        self.categorybox.Select(category_pos)
1249        self._show_combox(None)
1250        try:
1251            # to support older version
1252            formfactor_pos = int(state.formfactorcombobox)
1253        except:
1254            formfactor_pos = 0
1255            for ind_form in range(self.formfactorbox.GetCount()):
1256                if self.formfactorbox.GetString(ind_form) == \
1257                                                    (state.formfactorcombobox):
1258                    formfactor_pos = int(ind_form)
1259                    break
1260
1261        self.formfactorbox.Select(formfactor_pos)
1262
1263        try:
1264            # to support older version
1265            structfactor_pos = int(state.structurecombobox)
1266        except:
1267            structfactor_pos = 0
1268            for ind_struct in range(self.structurebox.GetCount()):
1269                if self.structurebox.GetString(ind_struct) == \
1270                                                    (state.structurecombobox):
1271                    structfactor_pos = int(ind_struct)
1272                    break
1273
1274        self.structurebox.SetSelection(structfactor_pos)
1275
1276        if state.multi_factor != None:
1277            self.multifactorbox.SetSelection(state.multi_factor)
1278
1279        #draw the panel according to the new model parameter
1280        self._on_select_model(event=None)
1281
1282        # take care of 2D button
1283        if data == None and self.model_view.IsEnabled():
1284            if self.enable2D:
1285                self.model_view.SetLabel("2D Mode")
1286            else:
1287                self.model_view.SetLabel("1D Mode")
1288
1289        ## set the select all check box to the a given state
1290        self.cb1.SetValue(state.cb1)
1291
1292        ## reset state of checkbox,textcrtl  and  regular parameters value
1293        self._reset_parameters_state(self.orientation_params_disp,
1294                                     state.orientation_params_disp)
1295        self._reset_parameters_state(self.orientation_params,
1296                                     state.orientation_params)
1297        self._reset_parameters_state(self.str_parameters,
1298                                     state.str_parameters)
1299        self._reset_parameters_state(self.parameters, state.parameters)
1300        ## display dispersion info layer
1301        self.enable_disp.SetValue(state.enable_disp)
1302        self.disable_disp.SetValue(state.disable_disp)
1303        # If the polydispersion is ON
1304        if state.enable_disp:
1305            # reset dispersion according the state
1306            self._set_dipers_Param(event=None)
1307            self._reset_page_disp_helper(state)
1308        ##plotting range restore
1309        self._reset_plotting_range(state)
1310        ## smearing info  restore
1311        if hasattr(self, "enable_smearer"):
1312            ## set smearing value whether or not the data
1313            #contain the smearing info
1314            self.enable_smearer.SetValue(state.enable_smearer)
1315            self.disable_smearer.SetValue(state.disable_smearer)
1316            self.onSmear(event=None)
1317        self.pinhole_smearer.SetValue(state.pinhole_smearer)
1318        self.slit_smearer.SetValue(state.slit_smearer)
1319        try:
1320            self.dI_noweight.SetValue(state.dI_noweight)
1321            self.dI_didata.SetValue(state.dI_didata)
1322            self.dI_sqrdata.SetValue(state.dI_sqrdata)
1323            self.dI_idata.SetValue(state.dI_idata)
1324        except:
1325            # to support older state file formats
1326            self.dI_noweight.SetValue(False)
1327            self.dI_didata.SetValue(True)
1328            self.dI_sqrdata.SetValue(False)
1329            self.dI_idata.SetValue(False)
1330
1331        ## we have two more options for smearing
1332        if self.pinhole_smearer.GetValue():
1333            self.dx_min = state.dx_min
1334            self.dx_max = state.dx_max
1335            if self.dx_min != None:
1336                self.smear_pinhole_min.SetValue(str(self.dx_min))
1337            if self.dx_max != None:
1338                self.smear_pinhole_max.SetValue(str(self.dx_max))
1339            self.onPinholeSmear(event=None)
1340        elif self.slit_smearer.GetValue():
1341            self.dxl = state.dxl
1342            self.dxw = state.dxw
1343            if self.dxl != None:
1344                self.smear_slit_height.SetValue(str(self.dxl))
1345            if self.dxw != None:
1346                self.smear_slit_width.SetValue(str(self.dxw))
1347            else:
1348                self.smear_slit_width.SetValue('')
1349            self.onSlitSmear(event=None)
1350
1351        ## reset state of checkbox,textcrtl  and dispersity parameters value
1352        self._reset_parameters_state(self.fittable_param, state.fittable_param)
1353        self._reset_parameters_state(self.fixed_param, state.fixed_param)
1354
1355        ## draw the model with previous parameters value
1356        self._onparamEnter_helper()
1357        #reset the value of chisqr when not consistent with the value computed
1358        self.tcChi.SetValue(str(self.state.tcChi))
1359        ## reset context menu items
1360        self._reset_context_menu()
1361
1362        ## set the value of the current state to the state given as parameter
1363        self.state = state.clone()
1364        self.state.m_name = self.m_name
1365
1366    def _reset_page_disp_helper(self, state):
1367        """
1368        Help to rest page for dispersions
1369        """
1370        keys = self.model.getParamList()
1371        for item in keys:
1372            if item in self.disp_list and \
1373                not item in self.model.details:
1374                self.model.details[item] = ["", None, None]
1375        #for k,v in self.state.disp_cb_dict.iteritems():
1376        self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1377        self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
1378        self.values = copy.deepcopy(state.values)
1379        self.weights = copy.deepcopy(state.weights)
1380
1381        for key, disp in state._disp_obj_dict.iteritems():
1382            # From saved file, disp_model can not be sent in model obj.
1383            # it will be sent as a string here, then converted to model object.
1384            if disp.__class__.__name__ == 'str':
1385                disp_model = None
1386                com_str = "from sasmodels.weights "
1387                com_str += "import %s as disp_func \ndisp_model = disp_func()"
1388                exec com_str % disp
1389            else:
1390                disp_model = disp
1391            self._disp_obj_dict[key] = disp_model
1392            param_name = key.split('.')[0]
1393            # Try to set dispersion only when available
1394            # for eg., pass the orient. angles for 1D Cal
1395            try:
1396                self.model.set_dispersion(param_name, disp_model)
1397                self.model._persistency_dict[key] = \
1398                                 [state.values, state.weights]
1399            except Exception:
1400                logging.error(traceback.format_exc())
1401            selection = self._find_polyfunc_selection(disp_model)
1402            for list in self.fittable_param:
1403                if list[1] == key and list[7] != None:
1404                    list[7].SetSelection(selection)
1405                    # For the array disp_model, set the values and weights
1406                    if selection == 1:
1407                        disp_model.set_weights(self.values[key],
1408                                               self.weights[key])
1409                        try:
1410                            # Diables all fittable params for array
1411                            list[0].SetValue(False)
1412                            list[0].Disable()
1413                            list[2].Disable()
1414                            list[5].Disable()
1415                            list[6].Disable()
1416                        except Exception:
1417                            logging.error(traceback.format_exc())
1418            # For array, disable all fixed params
1419            if selection == 1:
1420                for item in self.fixed_param:
1421                    if item[1].split(".")[0] == key.split(".")[0]:
1422                        # try it and pass it for the orientation for 1D
1423                        try:
1424                            item[2].Disable()
1425                        except Exception:
1426                            logging.error(traceback.format_exc())
1427
1428        # Make sure the check box updated when all checked
1429        if self.cb1.GetValue():
1430            self.select_all_param(None)
1431
1432    def _selectDlg(self):
1433        """
1434        open a dialog file to selected the customized dispersity
1435        """
1436        if self.parent != None:
1437            self._default_save_location = \
1438                        self._manager.parent.get_save_location()
1439        dlg = wx.FileDialog(self, "Choose a weight file",
1440                            self._default_save_location, "",
1441                            "*.*", wx.OPEN)
1442        path = None
1443        if dlg.ShowModal() == wx.ID_OK:
1444            path = dlg.GetPath()
1445        dlg.Destroy()
1446        return path
1447
1448    def _reset_context_menu(self):
1449        """
1450        reset the context menu
1451        """
1452        ids = iter(self._id_pool)  # Reusing ids for context menu
1453        for name, _ in self.state.saved_states.iteritems():
1454            self.number_saved_state += 1
1455            ## Add item in the context menu
1456            wx_id = ids.next()
1457            msg = 'Save model and state %g' % self.number_saved_state
1458            self.popUpMenu.Append(wx_id, name, msg)
1459            wx.EVT_MENU(self, wx_id, self.onResetModel)
1460
1461    def _reset_plotting_range(self, state):
1462        """
1463        Reset the plotting range to a given state
1464        """
1465        self.qmin.SetValue(str(state.qmin))
1466        self.qmax.SetValue(str(state.qmax))
1467
1468    def _save_typeOfmodel(self):
1469        """
1470        save radiobutton containing the type model that can be selected
1471        """
1472        #self.state.shape_rbutton = self.shape_rbutton.GetValue()
1473        #self.state.shape_indep_rbutton = self.shape_indep_rbutton.GetValue()
1474        #self.state.struct_rbutton = self.struct_rbutton.GetValue()
1475        #self.state.plugin_rbutton = self.plugin_rbutton.GetValue()
1476        self.state.structurecombobox = self.structurebox.GetLabel()
1477        self.state.formfactorcombobox = self.formfactorbox.GetLabel()
1478        self.state.categorycombobox = self.categorybox.GetLabel()
1479
1480        ## post state to fit panel
1481        event = PageInfoEvent(page=self)
1482        wx.PostEvent(self.parent, event)
1483
1484    def _save_plotting_range(self):
1485        """
1486        save the state of plotting range
1487        """
1488        self.state.qmin = self.qmin_x
1489        self.state.qmax = self.qmax_x
1490        self.state.npts = self.npts_x
1491
1492    def _onparamEnter_helper(self):
1493        """
1494        check if values entered by the user are changed and valid to replot
1495        model
1496        """
1497        # Flag to register when a parameter has changed.
1498        is_modified = False
1499        self.fitrange = True
1500        is_2Ddata = False
1501        #self._undo.Enable(True)
1502        # check if 2d data
1503        if self.data.__class__.__name__ == "Data2D":
1504            is_2Ddata = True
1505        if self.model != None:
1506            try:
1507                is_modified = self._check_value_enter(self.fittable_param,
1508                                                      is_modified)
1509                is_modified = self._check_value_enter(self.fixed_param,
1510                                                      is_modified)
1511                is_modified = self._check_value_enter(self.parameters,
1512                                                      is_modified)
1513            except Exception:
1514                logging.error(traceback.format_exc())
1515
1516            # Here we should check whether the boundaries have been modified.
1517            # If qmin and qmax have been modified, update qmin and qmax and
1518            # set the is_modified flag to True
1519            if self._validate_qrange(self.qmin, self.qmax):
1520                tempmin = float(self.qmin.GetValue())
1521                if tempmin != self.qmin_x:
1522                    self.qmin_x = tempmin
1523                    is_modified = True
1524                tempmax = float(self.qmax.GetValue())
1525                if tempmax != self.qmax_x:
1526                    self.qmax_x = tempmax
1527                    is_modified = True
1528
1529                if is_2Ddata:
1530                    # set mask
1531                    is_modified = self._validate_Npts()
1532
1533            else:
1534                self.fitrange = False
1535
1536            if not self.data.is_data:
1537                is_modified = True
1538
1539            ## if any value is modify draw model with new value
1540            if not self.fitrange:
1541                #self.btFit.Disable()
1542                if is_2Ddata:
1543                    self.btEditMask.Disable()
1544            else:
1545                if is_2Ddata and self.data.is_data and not self.batch_on:
1546                    self.btEditMask.Enable(True)
1547            if is_modified and self.fitrange:
1548                # Theory case: need to get npts value to draw
1549                self.npts_x = float(self.Npts_total.GetValue())
1550                self.create_default_data()
1551                self.state_change = True
1552                self._draw_model()
1553                self.Refresh()
1554        return is_modified
1555
1556    def _update_paramv_on_fit(self):
1557        """
1558        make sure that update param values just before the fitting
1559        """
1560        #flag for qmin qmax check values
1561        flag = True
1562        self.fitrange = True
1563        is_modified = False
1564
1565        #wx.PostEvent(self._manager.parent, StatusEvent(status=" \
1566        #updating ... ",type="update"))
1567
1568        ##So make sure that update param values on_Fit.
1569        #self._undo.Enable(True)
1570        if self.model != None:
1571            if self.Npts_total.GetValue() != self.Npts_fit.GetValue():
1572                if not self.data.is_data:
1573                    self._manager.page_finder[self.uid].set_fit_data(data=\
1574                                                                [self.data])
1575            ##Check the values
1576            self._check_value_enter(self.fittable_param, is_modified)
1577            self._check_value_enter(self.fixed_param, is_modified)
1578            self._check_value_enter(self.parameters, is_modified)
1579
1580            # If qmin and qmax have been modified, update qmin and qmax and
1581            # Here we should check whether the boundaries have been modified.
1582            # If qmin and qmax have been modified, update qmin and qmax and
1583            # set the is_modified flag to True
1584            self.fitrange = self._validate_qrange(self.qmin, self.qmax)
1585            if self.fitrange:
1586                tempmin = float(self.qmin.GetValue())
1587                if tempmin != self.qmin_x:
1588                    self.qmin_x = tempmin
1589                tempmax = float(self.qmax.GetValue())
1590                if tempmax != self.qmax_x:
1591                    self.qmax_x = tempmax
1592                if tempmax == tempmin:
1593                    flag = False
1594                temp_smearer = None
1595                if not self.disable_smearer.GetValue():
1596                    temp_smearer = self.current_smearer
1597                    if self.slit_smearer.GetValue():
1598                        flag = self.update_slit_smear()
1599                    elif self.pinhole_smearer.GetValue():
1600                        flag = self.update_pinhole_smear()
1601                    else:
1602                        enable_smearer = not self.disable_smearer.GetValue()
1603                        self._manager.set_smearer(smearer=temp_smearer,
1604                                                  uid=self.uid,
1605                                                  fid=self.data.id,
1606                                                  qmin=float(self.qmin_x),
1607                                                  qmax=float(self.qmax_x),
1608                                                  enable_smearer=enable_smearer,
1609                                                  draw=False)
1610                elif not self._is_2D():
1611                    enable_smearer = not self.disable_smearer.GetValue()
1612                    self._manager.set_smearer(smearer=temp_smearer,
1613                                              qmin=float(self.qmin_x),
1614                                              uid=self.uid,
1615                                              fid=self.data.id,
1616                                              qmax=float(self.qmax_x),
1617                                              enable_smearer=enable_smearer,
1618                                              draw=False)
1619                    if self.data != None:
1620                        index_data = ((self.qmin_x <= self.data.x) & \
1621                                      (self.data.x <= self.qmax_x))
1622                        val = str(len(self.data.x[index_data == True]))
1623                        self.Npts_fit.SetValue(val)
1624                    else:
1625                        # No data in the panel
1626                        try:
1627                            self.npts_x = float(self.Npts_total.GetValue())
1628                        except:
1629                            flag = False
1630                            return flag
1631                    flag = True
1632                if self._is_2D():
1633                    # only 2D case set mask
1634                    flag = self._validate_Npts()
1635                    if not flag:
1636                        return flag
1637            else:
1638                flag = False
1639        else:
1640            flag = False
1641
1642        #For invalid q range, disable the mask editor and fit button, vs.
1643        if not self.fitrange:
1644            if self._is_2D():
1645                self.btEditMask.Disable()
1646        else:
1647            if self._is_2D() and  self.data.is_data and not self.batch_on:
1648                self.btEditMask.Enable(True)
1649
1650        if not flag:
1651            msg = "Cannot Plot or Fit :Must select a "
1652            msg += " model or Fitting range is not valid!!!  "
1653            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1654
1655        try:
1656            self.save_current_state()
1657        except Exception:
1658            logging.error(traceback.format_exc())
1659
1660        return flag
1661
1662    def _is_modified(self, is_modified):
1663        """
1664        return to self._is_modified
1665        """
1666        return is_modified
1667
1668    def _reset_parameters_state(self, listtorestore, statelist):
1669        """
1670        Reset the parameters at the given state
1671        """
1672        if len(statelist) == 0 or len(listtorestore) == 0:
1673            return
1674        if len(statelist) != len(listtorestore):
1675            return
1676
1677        for j in range(len(listtorestore)):
1678            item_page = listtorestore[j]
1679            item_page_info = statelist[j]
1680            ##change the state of the check box for simple parameters
1681            if item_page[0] != None:
1682                item_page[0].SetValue(item_page_info[0])
1683            if item_page[2] != None:
1684                item_page[2].SetValue(item_page_info[2])
1685                if item_page[2].__class__.__name__ == "ComboBox":
1686                    if item_page_info[2] in self.model.fun_list:
1687                        fun_val = self.model.fun_list[item_page_info[2]]
1688                        self.model.setParam(item_page_info[1], fun_val)
1689            if item_page[3] != None:
1690                ## show or hide text +/-
1691                if item_page_info[2]:
1692                    item_page[3].Show(True)
1693                else:
1694                    item_page[3].Hide()
1695            if item_page[4] != None:
1696                ## show of hide the text crtl for fitting error
1697                if item_page_info[4][0]:
1698                    item_page[4].Show(True)
1699                    item_page[4].SetValue(item_page_info[4][1])
1700                else:
1701                    item_page[3].Hide()
1702            if item_page[5] != None:
1703                ## show of hide the text crtl for fitting error
1704                item_page[5].Show(item_page_info[5][0])
1705                item_page[5].SetValue(item_page_info[5][1])
1706
1707            if item_page[6] != None:
1708                ## show of hide the text crtl for fitting error
1709                item_page[6].Show(item_page_info[6][0])
1710                item_page[6].SetValue(item_page_info[6][1])
1711
1712    def _reset_strparam_state(self, listtorestore, statelist):
1713        """
1714        Reset the string parameters at the given state
1715        """
1716        if len(statelist) == 0:
1717            return
1718
1719        listtorestore = copy.deepcopy(statelist)
1720
1721        for j in range(len(listtorestore)):
1722            item_page = listtorestore[j]
1723            item_page_info = statelist[j]
1724            ##change the state of the check box for simple parameters
1725
1726            if item_page[0] != None:
1727                item_page[0].SetValue(format_number(item_page_info[0], True))
1728
1729            if item_page[2] != None:
1730                param_name = item_page_info[1]
1731                value = item_page_info[2]
1732                selection = value
1733                if value in self.model.fun_list:
1734                    selection = self.model.fun_list[value]
1735                item_page[2].SetValue(selection)
1736                self.model.setParam(param_name, selection)
1737
1738    def _copy_parameters_state(self, listtocopy, statelist):
1739        """
1740        copy the state of button
1741
1742        :param listtocopy: the list of check button to copy
1743        :param statelist: list of state object to store the current state
1744
1745        """
1746        if len(listtocopy) == 0:
1747            return
1748
1749        for item in listtocopy:
1750
1751            checkbox_state = None
1752            if item[0] != None:
1753                checkbox_state = item[0].GetValue()
1754            parameter_name = item[1]
1755            parameter_value = None
1756            if item[2] != None:
1757                parameter_value = item[2].GetValue()
1758            static_text = None
1759            if item[3] != None:
1760                static_text = item[3].IsShown()
1761            error_value = None
1762            error_state = None
1763            if item[4] != None:
1764                error_value = item[4].GetValue()
1765                error_state = item[4].IsShown()
1766
1767            min_value = None
1768            min_state = None
1769            if item[5] != None:
1770                min_value = item[5].GetValue()
1771                min_state = item[5].IsShown()
1772
1773            max_value = None
1774            max_state = None
1775            if item[6] != None:
1776                max_value = item[6].GetValue()
1777                max_state = item[6].IsShown()
1778            unit = None
1779            if item[7] != None:
1780                unit = item[7].GetLabel()
1781
1782            statelist.append([checkbox_state, parameter_name, parameter_value,
1783                              static_text, [error_state, error_value],
1784                              [min_state, min_value],
1785                              [max_state, max_value], unit])
1786
1787
1788    def _draw_model(self, update_chisqr=True, source='model'):
1789        """
1790        Method to draw or refresh a plotted model.
1791        The method will use the data member from the model page
1792        to build a call to the fitting perspective manager.
1793
1794        :param chisqr: update chisqr value [bool]
1795        """
1796        wx.CallAfter(self._draw_model_after, update_chisqr, source)
1797
1798    def _draw_model_after(self, update_chisqr=True, source='model'):
1799        """
1800        Method to draw or refresh a plotted model.
1801        The method will use the data member from the model page
1802        to build a call to the fitting perspective manager.
1803
1804        :param chisqr: update chisqr value [bool]
1805        """
1806        #if self.check_invalid_panel():
1807        #    return
1808        if self.model != None:
1809            temp_smear = None
1810            if hasattr(self, "enable_smearer"):
1811                if not self.disable_smearer.GetValue():
1812                    temp_smear = self.current_smearer
1813            # compute weight for the current data
1814            from sas.sasgui.perspectives.fitting.utils import get_weight
1815            flag = self.get_weight_flag()
1816            weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
1817            toggle_mode_on = self.model_view.IsEnabled()
1818            is_2d = self._is_2D()
1819            self._manager.draw_model(self.model,
1820                                    data=self.data,
1821                                    smearer=temp_smear,
1822                                    qmin=float(self.qmin_x),
1823                                    qmax=float(self.qmax_x),
1824                                    page_id=self.uid,
1825                                    toggle_mode_on=toggle_mode_on,
1826                                    state=self.state,
1827                                    enable2D=is_2d,
1828                                    update_chisqr=update_chisqr,
1829                                    source='model',
1830                                    weight=weight)
1831
1832    def _on_show_sld(self, event=None):
1833        """
1834        Plot SLD profile
1835        """
1836        # get profile data
1837        x, y = self.model.getProfile()
1838
1839        from sas.sasgui.plottools import Data1D as pf_data1d
1840        #from sas.sasgui.perspectives.theory.profile_dialog import SLDPanel
1841        from sas.sasgui.guiframe.local_perspectives.plotting.profile_dialog \
1842        import SLDPanel
1843        sld_data = pf_data1d(x, y)
1844        sld_data.name = 'SLD'
1845        sld_data.axes = self.sld_axes
1846        self.panel = SLDPanel(self, data=sld_data, axes=self.sld_axes,
1847                              id=wx.ID_ANY)
1848        self.panel.ShowModal()
1849
1850    def _set_multfactor_combobox(self, multiplicity=10):
1851        """
1852        Set comboBox for muitfactor of CoreMultiShellModel
1853        :param multiplicit: no. of multi-functionality
1854        """
1855        # build content of the combobox
1856        for idx in range(0, multiplicity):
1857            self.multifactorbox.Append(str(idx), int(idx))
1858        self._hide_multfactor_combobox()
1859
1860    def _show_multfactor_combobox(self):
1861        """
1862        Show the comboBox of muitfactor of CoreMultiShellModel
1863        """
1864        if not self.mutifactor_text.IsShown():
1865            self.mutifactor_text.Show(True)
1866            self.mutifactor_text1.Show(True)
1867        if not self.multifactorbox.IsShown():
1868            self.multifactorbox.Show(True)
1869
1870    def _hide_multfactor_combobox(self):
1871        """
1872        Hide the comboBox of muitfactor of CoreMultiShellModel
1873        """
1874        if self.mutifactor_text.IsShown():
1875            self.mutifactor_text.Hide()
1876            self.mutifactor_text1.Hide()
1877        if self.multifactorbox.IsShown():
1878            self.multifactorbox.Hide()
1879
1880    def formfactor_combo_init(self):
1881        """
1882        First time calls _show_combox_helper
1883        """
1884        self._show_combox(None)
1885
1886    def _show_combox_helper(self):
1887        """
1888        Fill panel's combo box according to the type of model selected
1889        """
1890        custom_model = 'Customized Models'
1891        mod_cat = self.categorybox.GetStringSelection()
1892        self.structurebox.SetSelection(0)
1893        self.structurebox.Disable()
1894        self.formfactorbox.Clear()
1895        if mod_cat == None:
1896            return
1897        m_list = []
1898        try:
1899            if mod_cat == custom_model:
1900                for model in self.model_list_box[mod_cat]:
1901                    str_m = model.id if hasattr(model, 'id') else model.name
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 Exception:
1913            msg = traceback.format_exc()
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 Exception:
2477                    logging.error(traceback.format_exc())
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 Exception:
2690            logging.error(traceback.format_exc())
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 Exception:
2835                # Not for control panels
2836                logging.error(traceback.format_exc())
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 Exception:
3170                logging.error(traceback.format_exc())
3171
3172            # 2D
3173            if self.data.__class__.__name__ == "Data2D":
3174                try:
3175                    check = item[0].GetValue()
3176                except Exception:
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 Exception:
3205                logging.error(traceback.format_exc())
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 Exception:
3406                    logging.error(traceback.format_exc())
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 Exception:
3421                logging.error(traceback.format_exc())
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.