source: sasview/src/sas/perspectives/fitting/basepage.py @ 6f16e25

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

clean up wx id handling in fitting perspective

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