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

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 d7bb526 was d7bb526, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 8 years ago

Refactored plottools into sasgui

  • Property mode set to 100644
File size: 144.1 KB
Line 
1"""
2Base Page for fitting
3"""
4import sys
5import os
6import wx
7import numpy
8import time
9import copy
10import math
11import string
12import json
13import logging
14from collections import defaultdict
15from wx.lib.scrolledpanel import ScrolledPanel
16from sas.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.sascalc.dataloader.data_info import Detector
26from sas.sascalc.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.sasgui.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        ## Need to use FitInside again here to replace the next four lines.
2416        ## Otherwised polydispersity off does not resize the scrollwindow.
2417        ## PDB Nov 28, 2015
2418        self.FitInside()
2419#        self.sizer4_4.Layout()
2420#        self.sizer5.Layout()
2421#        self.Layout()
2422#        self.Refresh()
2423
2424    def _layout_sizer_noDipers(self):
2425        """
2426        Draw a sizer with no dispersity info
2427        """
2428        ix = 0
2429        iy = 1
2430        self.fittable_param = []
2431        self.fixed_param = []
2432        self.orientation_params_disp = []
2433
2434        self.sizer4_4.Clear(True)
2435        text = "No polydispersity available for this model"
2436        model_disp = wx.StaticText(self, wx.ID_ANY, text)
2437        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
2438                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
2439        self.sizer4_4.Layout()
2440        self.sizer4.Layout()
2441
2442    def _reset_dispersity(self):
2443        """
2444        put gaussian dispersity into current model
2445        """
2446        if len(self.param_toFit) > 0:
2447            for item in self.fittable_param:
2448                if item in self.param_toFit:
2449                    self.param_toFit.remove(item)
2450
2451            for item in self.orientation_params_disp:
2452                if item in self.param_toFit:
2453                    self.param_toFit.remove(item)
2454
2455        self.fittable_param = []
2456        self.fixed_param = []
2457        self.orientation_params_disp = []
2458        self.values = {}
2459        self.weights = {}
2460
2461        from sas.models.dispersion_models import GaussianDispersion
2462        if len(self.disp_cb_dict) == 0:
2463            self.save_current_state()
2464            self.sizer4_4.Clear(True)
2465            self.Layout()
2466            return
2467        if (len(self.disp_cb_dict) > 0):
2468            for p in self.disp_cb_dict:
2469                # The parameter was un-selected.
2470                # Go back to Gaussian model (with 0 pts)
2471                disp_model = GaussianDispersion()
2472
2473                self._disp_obj_dict[p] = disp_model
2474                # Set the new model as the dispersion object
2475                # for the selected parameter
2476                try:
2477                    self.model.set_dispersion(p, disp_model)
2478                except:
2479                    logging.error(sys.exc_info()[1])
2480
2481        ## save state into
2482        self.save_current_state()
2483        self.Layout()
2484        self.Refresh()
2485
2486    def _on_select_Disp(self, event):
2487        """
2488        allow selecting different dispersion
2489        self.disp_list should change type later .now only gaussian
2490        """
2491        self._set_sizer_dispersion()
2492
2493        ## Redraw the model
2494        self._draw_model()
2495        #self._undo.Enable(True)
2496        event = PageInfoEvent(page=self)
2497        wx.PostEvent(self.parent, event)
2498
2499        self.sizer4_4.Layout()
2500        self.sizer4.Layout()
2501        self.SetupScrolling()
2502
2503    def _on_disp_func(self, event=None):
2504        """
2505        Select a distribution function for the polydispersion
2506
2507        :Param event: ComboBox event
2508        """
2509        # get ready for new event
2510        if event != None:
2511            event.Skip()
2512        # Get event object
2513        disp_box = event.GetEventObject()
2514
2515        # Try to select a Distr. function
2516        try:
2517            disp_box.SetBackgroundColour("white")
2518            selection = disp_box.GetCurrentSelection()
2519            param_name = disp_box.Name.split('.')[0]
2520            disp_name = disp_box.GetValue()
2521            dispersity = disp_box.GetClientData(selection)
2522
2523            #disp_model =  GaussianDispersion()
2524            disp_model = dispersity()
2525            # Get param names to reset the values of the param
2526            name1 = param_name + ".width"
2527            name2 = param_name + ".npts"
2528            name3 = param_name + ".nsigmas"
2529            # Check Disp. function whether or not it is 'array'
2530            if disp_name.lower() == "array":
2531                value2 = ""
2532                value3 = ""
2533                value1 = self._set_array_disp(name=name1, disp=disp_model)
2534            else:
2535                self._del_array_values(name1)
2536                #self._reset_array_disp(param_name)
2537                self._disp_obj_dict[name1] = disp_model
2538                self.model.set_dispersion(param_name, disp_model)
2539                self.state._disp_obj_dict[name1] = disp_model
2540
2541                value1 = str(format_number(self.model.getParam(name1), True))
2542                value2 = str(format_number(self.model.getParam(name2)))
2543                value3 = str(format_number(self.model.getParam(name3)))
2544            # Reset fittable polydispersin parameter value
2545            for item in self.fittable_param:
2546                if item[1] == name1:
2547                    item[2].SetValue(value1)
2548                    item[5].SetValue("")
2549                    item[6].SetValue("")
2550                    # Disable for array
2551                    if disp_name.lower() == "array":
2552                        item[0].SetValue(False)
2553                        item[0].Disable()
2554                        item[2].Disable()
2555                        item[3].Show(False)
2556                        item[4].Show(False)
2557                        item[5].Disable()
2558                        item[6].Disable()
2559                    else:
2560                        item[0].Enable()
2561                        item[2].Enable()
2562                        item[5].Enable()
2563                        item[6].Enable()
2564                    break
2565            # Reset fixed polydispersion params
2566            for item in self.fixed_param:
2567                if item[1] == name2:
2568                    item[2].SetValue(value2)
2569                    # Disable Npts for array
2570                    if disp_name.lower() == "array":
2571                        item[2].Disable()
2572                    else:
2573                        item[2].Enable()
2574                if item[1] == name3:
2575                    item[2].SetValue(value3)
2576                    # Disable Nsigs for array
2577                    if disp_name.lower() == "array":
2578                        item[2].Disable()
2579                    else:
2580                        item[2].Enable()
2581
2582            # Make sure the check box updated when all checked
2583            if self.cb1.GetValue():
2584                #self.select_all_param(None)
2585                self.get_all_checked_params()
2586
2587            # update params
2588            self._update_paramv_on_fit()
2589            # draw
2590            self._draw_model()
2591            self.Refresh()
2592        except:
2593            # Error msg
2594            msg = "Error occurred:"
2595            msg += " Could not select the distribution function..."
2596            msg += " Please select another distribution function."
2597            disp_box.SetBackgroundColour("pink")
2598            # Focus on Fit button so that users can see the pinky box
2599            self.btFit.SetFocus()
2600            wx.PostEvent(self._manager.parent,
2601                         StatusEvent(status=msg, info="error"))
2602
2603    def _set_array_disp(self, name=None, disp=None):
2604        """
2605        Set array dispersion
2606
2607        :param name: name of the parameter for the dispersion to be set
2608        :param disp: the polydisperion object
2609        """
2610        # The user wants this parameter to be averaged.
2611        # Pop up the file selection dialog.
2612        path = self._selectDlg()
2613        # Array data
2614        values = []
2615        weights = []
2616        # If nothing was selected, just return
2617        if path is None:
2618            self.disp_cb_dict[name].SetValue(False)
2619            #self.noDisper_rbox.SetValue(True)
2620            return
2621        self._default_save_location = os.path.dirname(path)
2622        if self._manager != None:
2623            self._manager.parent._default_save_location = \
2624                             self._default_save_location
2625
2626        basename = os.path.basename(path)
2627        values, weights = self.read_file(path)
2628
2629        # If any of the two arrays is empty, notify the user that we won't
2630        # proceed
2631        if len(self.param_toFit) > 0:
2632            if name in self.param_toFit:
2633                self.param_toFit.remove(name)
2634
2635        # Tell the user that we are about to apply the distribution
2636        msg = "Applying loaded %s distribution: %s" % (name, path)
2637        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2638        self._set_array_disp_model(name=name, disp=disp,
2639                                    values=values, weights=weights)
2640        return basename
2641
2642    def _set_array_disp_model(self, name=None, disp=None,
2643                              values=[], weights=[]):
2644        """
2645        Set array dispersion model
2646
2647        :param name: name of the parameter for the dispersion to be set
2648        :param disp: the polydisperion object
2649        """
2650        disp.set_weights(values, weights)
2651        self._disp_obj_dict[name] = disp
2652        self.model.set_dispersion(name.split('.')[0], disp)
2653        self.state._disp_obj_dict[name] = disp
2654        self.values[name] = values
2655        self.weights[name] = weights
2656        # Store the object to make it persist outside the
2657        # scope of this method
2658        #TODO: refactor model to clean this up?
2659        self.state.values = {}
2660        self.state.weights = {}
2661        self.state.values = copy.deepcopy(self.values)
2662        self.state.weights = copy.deepcopy(self.weights)
2663
2664        # Set the new model as the dispersion object for the
2665        #selected parameter
2666        #self.model.set_dispersion(p, disp_model)
2667        # Store a reference to the weights in the model object
2668        #so that
2669        # it's not lost when we use the model within another thread.
2670        self.state.model = self.model.clone()
2671        self.model._persistency_dict[name.split('.')[0]] = \
2672                                        [values, weights]
2673        self.state.model._persistency_dict[name.split('.')[0]] = \
2674                                        [values, weights]
2675
2676    def _del_array_values(self, name=None):
2677        """
2678        Reset array dispersion
2679
2680        :param name: name of the parameter for the dispersion to be set
2681        """
2682        # Try to delete values and weight of the names array dic if exists
2683        try:
2684            del self.values[name]
2685            del self.weights[name]
2686            # delete all other dic
2687            del self.state.values[name]
2688            del self.state.weights[name]
2689            del self.model._persistency_dict[name.split('.')[0]]
2690            del self.state.model._persistency_dict[name.split('.')[0]]
2691        except:
2692            logging.error(sys.exc_info()[1])
2693
2694    def _lay_out(self):
2695        """
2696        returns self.Layout
2697
2698        :Note: Mac seems to like this better when self.
2699            Layout is called after fitting.
2700        """
2701        self._sleep4sec()
2702        self.Layout()
2703        return
2704
2705    def _sleep4sec(self):
2706        """
2707            sleep for 1 sec only applied on Mac
2708            Note: This 1sec helps for Mac not to crash on self.
2709            Layout after self._draw_model
2710        """
2711        if ON_MAC == True:
2712            time.sleep(1)
2713
2714    def _find_polyfunc_selection(self, disp_func=None):
2715        """
2716        FInd Comboox selection from disp_func
2717
2718        :param disp_function: dispersion distr. function
2719        """
2720        # List of the poly_model name in the combobox
2721        list = ["RectangleDispersion", "ArrayDispersion",
2722                "LogNormalDispersion", "GaussianDispersion",
2723                "SchulzDispersion"]
2724
2725        # Find the selection
2726        try:
2727            selection = list.index(disp_func.__class__.__name__)
2728            return selection
2729        except:
2730            return 3
2731
2732    def on_reset_clicked(self, event):
2733        """
2734        On 'Reset' button  for Q range clicked
2735        """
2736        flag = True
2737        ##For 3 different cases: Data2D, Data1D, and theory
2738        if self.model == None:
2739            msg = "Please select a model first..."
2740            wx.MessageBox(msg, 'Info')
2741            flag = False
2742            return
2743
2744        elif self.data.__class__.__name__ == "Data2D":
2745            data_min = 0
2746            x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax))
2747            y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
2748            self.qmin_x = data_min
2749            self.qmax_x = math.sqrt(x * x + y * y)
2750            #self.data.mask = numpy.ones(len(self.data.data),dtype=bool)
2751            # check smearing
2752            if not self.disable_smearer.GetValue():
2753                ## set smearing value whether or
2754                # not the data contain the smearing info
2755                if self.pinhole_smearer.GetValue():
2756                    flag = self.update_pinhole_smear()
2757                else:
2758                    flag = True
2759
2760        elif self.data == None:
2761            self.qmin_x = _QMIN_DEFAULT
2762            self.qmax_x = _QMAX_DEFAULT
2763            self.num_points = _NPTS_DEFAULT
2764            self.state.npts = self.num_points
2765
2766        elif self.data.__class__.__name__ != "Data2D":
2767            self.qmin_x = min(self.data.x)
2768            self.qmax_x = max(self.data.x)
2769            # check smearing
2770            if not self.disable_smearer.GetValue():
2771                ## set smearing value whether or
2772                # not the data contain the smearing info
2773                if self.slit_smearer.GetValue():
2774                    flag = self.update_slit_smear()
2775                elif self.pinhole_smearer.GetValue():
2776                    flag = self.update_pinhole_smear()
2777                else:
2778                    flag = True
2779        else:
2780            flag = False
2781
2782        if flag == False:
2783            msg = "Cannot Plot :Must enter a number!!!  "
2784            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2785        else:
2786            # set relative text ctrs.
2787            self.qmin.SetValue(str(self.qmin_x))
2788            self.qmax.SetValue(str(self.qmax_x))
2789            self.show_npts2fit()
2790            # At this point, some button and variables satatus (disabled?)
2791            # should be checked such as color that should be reset to
2792            # white in case that it was pink.
2793            self._onparamEnter_helper()
2794
2795        self.save_current_state()
2796        self.state.qmin = self.qmin_x
2797        self.state.qmax = self.qmax_x
2798
2799        #reset the q range values
2800        self._reset_plotting_range(self.state)
2801        self._draw_model()
2802
2803    def select_log(self, event):
2804        """
2805        Log checked to generate log spaced points for theory model
2806        """
2807
2808    def get_images(self):
2809        """
2810        Get the images of the plots corresponding this panel for report
2811
2812        : return graphs: list of figures
2813        : Need Move to guiframe
2814        """
2815        # set list of graphs
2816        graphs = []
2817        canvases = []
2818        res_item = None
2819        # call gui_manager
2820        gui_manager = self._manager.parent
2821        # loops through the panels [dic]
2822        for _, item2 in gui_manager.plot_panels.iteritems():
2823            data_title = self.data.group_id
2824            # try to get all plots belonging to this control panel
2825            try:
2826                g_id = item2.group_id
2827                if g_id == data_title or \
2828                        str(g_id).count("res" + str(self.graph_id)) or \
2829                        str(g_id).count(str(self.uid)) > 0:
2830                    if str(g_id).count("res" + str(self.graph_id)) > 0:
2831                        res_item = [item2.figure, item2.canvas]
2832                    else:
2833                        # append to the list
2834                        graphs.append(item2.figure)
2835                        canvases.append(item2.canvas)
2836            except:
2837                # Not for control panels
2838                logging.error(sys.exc_info()[1])
2839        # Make sure the resduals plot goes to the last
2840        if res_item != None:
2841            graphs.append(res_item[0])
2842            canvases.append(res_item[1])
2843        # return the list of graphs
2844        return graphs, canvases
2845
2846    def on_function_help_clicked(self, event):
2847        """
2848        Function called when 'Help' button is pressed next to model
2849        of interest.  This calls DocumentationWindow from
2850        documentation_window.py. It will load the top level of the model
2851        help documenation sphinx generated html if no model is presented.
2852        If a model IS present then if documention for that model exists
2853        it will load to that  point otherwise again it will go to the top.
2854        For Wx2.8 and below is used (i.e. non-released through installer)
2855        a browser is loaded and the top of the model documentation only is
2856        accessible because webbrowser module does not pass anything after
2857        the # to the browser.
2858
2859        :param evt: on Help Button pressed event
2860        """
2861
2862        _TreeLocation = 'user/models/model_functions.html'
2863        if self.model != None:
2864            name = self.formfactorbox.GetValue()
2865            _PageAnchor = '#' + name.lower()
2866            _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2867                                              _PageAnchor, name + " Help")
2868        else:
2869            _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2870                                              "", "General Model Help")
2871
2872
2873    def on_model_help_clicked(self, event):
2874        """
2875        Function called when 'Description' button is pressed next to model
2876        of interest.  This calls the Description embedded in the model. This
2877        should work with either Wx2.8 and lower or higher. If no model is
2878        selected it will give the message that a model must be chosen first
2879        in the box that would normally contain the description.  If a badly
2880        behaved model is encountered which has no description then it will
2881        give the message that none is available.
2882
2883        :param evt: on Description Button pressed event
2884        """
2885
2886        if self.model == None:
2887            name = 'index.html'
2888        else:
2889            name = self.formfactorbox.GetValue()
2890
2891        msg = 'Model description:\n'
2892        info = "Info"
2893        if self.model != None:
2894#                frame.Destroy()
2895            if str(self.model.description).rstrip().lstrip() == '':
2896                msg += "Sorry, no information is available for this model."
2897            else:
2898                msg += self.model.description + '\n'
2899            wx.MessageBox(msg, info)
2900        else:
2901            msg += "You must select a model to get information on this"
2902            wx.MessageBox(msg, info)
2903
2904    def _on_mag_angle_help(self, event):
2905        """
2906        Bring up Magnetic Angle definition bmp image whenever the ? button
2907        is clicked. Calls DocumentationWindow with the path of the location
2908        within the documentation tree (after /doc/ ....". When using old
2909        versions of Wx (i.e. before 2.9 and therefore not part of release
2910        versions distributed via installer) it brings up an image viewer
2911        box which allows the user to click through the rest of the images in
2912        the directory.  Not ideal but probably better than alternative which
2913        would bring up the entire discussion of how magnetic models work?
2914        Specially since it is not likely to be accessed.  The normal release
2915        versions bring up the normal image box.
2916
2917        :param evt: Triggers on clicking ? in Magnetic Angles? box
2918        """
2919
2920        _TreeLocation = "_images/M_angles_pic.bmp"
2921        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
2922                                          "Magnetic Angle Defintions")
2923
2924    def _on_mag_help(self, event):
2925        """
2926        Bring up Magnetic Angle definition bmp image whenever the ? button
2927        is clicked. Calls DocumentationWindow with the path of the location
2928        within the documentation tree (after /doc/ ....". When using old
2929        versions of Wx (i.e. before 2.9 and therefore not part of release
2930        versions distributed via installer) it brings up an image viewer
2931        box which allows the user to click through the rest of the images in
2932        the directory.  Not ideal but probably better than alternative which
2933        would bring up the entire discussion of how magnetic models work?
2934        Specially since it is not likely to be accessed.  The normal release
2935        versions bring up the normal image box.
2936
2937        :param evt: Triggers on clicking ? in Magnetic Angles? box
2938        """
2939
2940        _TreeLocation = "user/perspectives/fitting/mag_help.html"
2941        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
2942                                          "Polarized Beam/Magnetc Help")
2943
2944    def _on_mag_on(self, event):
2945        """
2946        Magnetic Parameters ON/OFF
2947        """
2948        button = event.GetEventObject()
2949
2950        if button.GetLabel().count('ON') > 0:
2951            self.magnetic_on = True
2952            button.SetLabel("Magnetic OFF")
2953            m_value = 1.0e-06
2954            for key in self.model.magnetic_params:
2955                if key.count('M0') > 0:
2956                    self.model.setParam(key, m_value)
2957                    m_value += 0.5e-06
2958        else:
2959            self.magnetic_on = False
2960            button.SetLabel("Magnetic ON")
2961            for key in self.model.magnetic_params:
2962                if key.count('M0') > 0:
2963                    #reset mag value to zero fo safety
2964                    self.model.setParam(key, 0.0)
2965
2966        self.Show(False)
2967        self.set_model_param_sizer(self.model)
2968        #self._set_sizer_dispersion()
2969        self.state.magnetic_on = self.magnetic_on
2970        self.SetupScrolling()
2971        self.Show(True)
2972
2973    def on_pd_help_clicked(self, event):
2974        """
2975        Bring up Polydispersity Documentation whenever the ? button is clicked.
2976        Calls DocumentationWindow with the path of the location within the
2977        documentation tree (after /doc/ ....".  Note that when using old
2978        versions of Wx (before 2.9) and thus not the release version of
2979        istallers, the help comes up at the top level of the file as
2980        webbrowser does not pass anything past the # to the browser when it is
2981        running "file:///...."
2982
2983        :param evt: Triggers on clicking ? in polydispersity box
2984        """
2985
2986        _TreeLocation = "user/perspectives/fitting/pd_help.html"
2987        _PageAnchor = ""
2988        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2989                                          _PageAnchor, "Polydispersity Help")
2990
2991    def on_left_down(self, event):
2992        """
2993        Get key stroke event
2994        """
2995        # Figuring out key combo: Cmd for copy, Alt for paste
2996        if event.CmdDown() and event.ShiftDown():
2997            self.get_paste()
2998        elif event.CmdDown():
2999            self.get_copy()
3000        else:
3001            event.Skip()
3002            return
3003        # make event free
3004        event.Skip()
3005
3006    def get_copy(self):
3007        """
3008        Get copy params to clipboard
3009        """
3010        content = self.get_copy_params()
3011        flag = self.set_clipboard(content)
3012        self._copy_info(flag)
3013        return flag
3014
3015    def get_copy_params(self):
3016        """
3017        Get the string copies of the param names and values in the tap
3018        """
3019        content = 'sasview_parameter_values:'
3020        # Do it if params exist
3021        if  self.parameters != []:
3022
3023            # go through the parameters
3024            strings = self._get_copy_helper(self.parameters,
3025                                           self.orientation_params)
3026            content += strings
3027
3028            # go through the fittables
3029            strings = self._get_copy_helper(self.fittable_param,
3030                                           self.orientation_params_disp)
3031            content += strings
3032
3033            # go through the fixed params
3034            strings = self._get_copy_helper(self.fixed_param,
3035                                           self.orientation_params_disp)
3036            content += strings
3037
3038            # go through the str params
3039            strings = self._get_copy_helper(self.str_parameters,
3040                                           self.orientation_params)
3041            content += strings
3042            return content
3043        else:
3044            return False
3045
3046    def get_copy_excel(self):
3047        """
3048        Get copy params to clipboard
3049        """
3050        content = self.get_copy_params_excel()
3051        flag = self.set_clipboard(content)
3052        self._copy_info(flag)
3053        return flag
3054
3055    def get_copy_params_excel(self):
3056        """
3057        Get the string copies of the param names and values in the tap
3058        """
3059        content = ''
3060
3061        crlf = chr(13) + chr(10)
3062        tab = chr(9)
3063
3064        # Do it if params exist
3065        if  self.parameters != []:
3066
3067            for param in self.parameters:
3068                content += param[1] #parameter name
3069                content += tab
3070                content += param[1] + "_err"
3071                content += tab
3072
3073            content += crlf
3074
3075            #row of values and errors...
3076            for param in self.parameters:
3077                content += param[2].GetValue() #value
3078                content += tab
3079                content += param[4].GetValue() #error
3080                content += tab
3081
3082            return content
3083        else:
3084            return False
3085
3086
3087    def get_copy_latex(self):
3088        """
3089        Get copy params to clipboard
3090        """
3091        content = self.get_copy_params_latex()
3092        flag = self.set_clipboard(content)
3093        self._copy_info(flag)
3094        return flag
3095
3096    def get_copy_params_latex(self):
3097        """
3098        Get the string copies of the param names and values in the tap
3099        """
3100        content = '\\begin{table}'
3101        content += '\\begin{tabular}[h]'
3102
3103        crlf = chr(13) + chr(10)
3104        tab = chr(9)
3105
3106        # Do it if params exist
3107        if  self.parameters != []:
3108
3109            content += '{|'
3110            for param in self.parameters:
3111                content += 'l|l|'
3112            content += '}\hline'
3113            content += crlf
3114
3115            for index, param in enumerate(self.parameters):
3116                content += param[1].replace('_', '\_') #parameter name
3117                content += ' & '
3118                content += param[1].replace('_', '\_') + "\_err"
3119                if index < len(self.parameters) - 1:
3120                    content += ' & '
3121            content += '\\\\ \\hline'
3122            content += crlf
3123
3124            #row of values and errors...
3125            for index, param in enumerate(self.parameters):
3126                content += param[2].GetValue() #parameter value
3127                content += ' & '
3128                content += param[4].GetValue() #parameter error
3129                if index < len(self.parameters) - 1:
3130                    content += ' & '
3131            content += '\\\\ \\hline'
3132            content += crlf
3133
3134            content += '\\end{tabular}'
3135            content += '\\end{table}'
3136            return content
3137        else:
3138            return False
3139
3140
3141    def set_clipboard(self, content=None):
3142        """
3143        Put the string to the clipboard
3144        """
3145        if not content:
3146            return False
3147        if wx.TheClipboard.Open():
3148            wx.TheClipboard.SetData(wx.TextDataObject(str(content)))
3149            wx.TheClipboard.Close()
3150            return True
3151        return None
3152
3153    def _get_copy_helper(self, param, orient_param):
3154        """
3155        Helping get value and name of the params
3156
3157        : param param:  parameters
3158        : param orient_param = oritational params
3159        : return content: strings [list] [name,value:....]
3160        """
3161        content = ''
3162        # go through the str params
3163        for item in param:
3164            # copy only the params shown
3165            if not item[2].IsShown():
3166                continue
3167            disfunc = ''
3168            try:
3169                if item[7].__class__.__name__ == 'ComboBox':
3170                    disfunc = str(item[7].GetValue())
3171            except:
3172                logging.error(sys.exc_info()[1])
3173
3174            # 2D
3175            if self.data.__class__.__name__ == "Data2D":
3176                try:
3177                    check = item[0].GetValue()
3178                except:
3179                    check = None
3180                name = item[1]
3181                value = item[2].GetValue()
3182            # 1D
3183            else:
3184                ## for 1D all parameters except orientation
3185                if not item[1] in orient_param:
3186                    try:
3187                        check = item[0].GetValue()
3188                    except:
3189                        check = None
3190                    name = item[1]
3191                    value = item[2].GetValue()
3192
3193            # add to the content
3194            if disfunc != '':
3195
3196                disfunc = ',' + disfunc
3197            # Need to support array func for copy/paste
3198            try:
3199                if disfunc.count('array') > 0:
3200                    disfunc += ','
3201                    for val in self.values[name]:
3202                        disfunc += ' ' + str(val)
3203                    disfunc += ','
3204                    for weight in self.weights[name]:
3205                        disfunc += ' ' + str(weight)
3206            except:
3207                logging.error(sys.exc_info()[1])
3208            content += name + ',' + str(check) + ',' + value + disfunc + ':'
3209
3210        return content
3211
3212    def get_clipboard(self):
3213        """
3214        Get strings in the clipboard
3215        """
3216        text = ""
3217        # Get text from the clip board
3218        if wx.TheClipboard.Open():
3219            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
3220                data = wx.TextDataObject()
3221                # get wx dataobject
3222                success = wx.TheClipboard.GetData(data)
3223                # get text
3224                if success:
3225                    text = data.GetText()
3226                else:
3227                    text = ''
3228            # close clipboard
3229            wx.TheClipboard.Close()
3230        return text
3231
3232    def get_paste(self):
3233        """
3234        Paste params from the clipboard
3235        """
3236        text = self.get_clipboard()
3237        flag = self.get_paste_params(text)
3238        self._copy_info(flag)
3239        return flag
3240
3241    def get_paste_params(self, text=''):
3242        """
3243        Get the string copies of the param names and values in the tap
3244        """
3245        context = {}
3246        # put the text into dictionary
3247        lines = text.split(':')
3248        if lines[0] != 'sasview_parameter_values':
3249            self._copy_info(False)
3250            return False
3251        for line in lines[1:-1]:
3252            if len(line) != 0:
3253                item = line.split(',')
3254                check = item[1]
3255                name = item[0]
3256                value = item[2]
3257                # Transfer the text to content[dictionary]
3258                context[name] = [check, value]
3259            # ToDo: PlugIn this poly disp function for pasting
3260            try:
3261                poly_func = item[3]
3262                context[name].append(poly_func)
3263                try:
3264                    # take the vals and weights for  array
3265                    array_values = item[4].split(' ')
3266                    array_weights = item[5].split(' ')
3267                    val = [float(a_val) for a_val in array_values[1:]]
3268                    weit = [float(a_weit) for a_weit in array_weights[1:]]
3269
3270                    context[name].append(val)
3271                    context[name].append(weit)
3272                except:
3273                    raise
3274            except:
3275                poly_func = ''
3276                context[name].append(poly_func)
3277
3278        # Do it if params exist
3279        if  self.parameters != []:
3280            # go through the parameters
3281            self._get_paste_helper(self.parameters,
3282                                   self.orientation_params, context)
3283
3284            # go through the fittables
3285            self._get_paste_helper(self.fittable_param,
3286                                   self.orientation_params_disp,
3287                                   context)
3288
3289            # go through the fixed params
3290            self._get_paste_helper(self.fixed_param,
3291                                   self.orientation_params_disp, context)
3292
3293            # go through the str params
3294            self._get_paste_helper(self.str_parameters,
3295                                   self.orientation_params, context)
3296
3297            return True
3298        return None
3299
3300    def _get_paste_helper(self, param, orient_param, content):
3301        """
3302        Helping set values of the params
3303
3304        : param param:  parameters
3305        : param orient_param: oritational params
3306        : param content: dictionary [ name, value: name1.value1,...]
3307        """
3308        # go through the str params
3309        for item in param:
3310            # 2D
3311            if self.data.__class__.__name__ == "Data2D":
3312                name = item[1]
3313                if name in content.keys():
3314                    check = content[name][0]
3315                    pd = content[name][1]
3316                    if name.count('.') > 0:
3317                        try:
3318                            float(pd)
3319                        except:
3320                            #continue
3321                            if not pd and pd != '':
3322                                continue
3323                    item[2].SetValue(str(pd))
3324                    if item in self.fixed_param and pd == '':
3325                        # Only array func has pd == '' case.
3326                        item[2].Enable(False)
3327                    if item[2].__class__.__name__ == "ComboBox":
3328                        if content[name][1] in self.model.fun_list:
3329                            fun_val = self.model.fun_list[content[name][1]]
3330                            self.model.setParam(name, fun_val)
3331
3332                    value = content[name][1:]
3333                    self._paste_poly_help(item, value)
3334                    if check == 'True':
3335                        is_true = True
3336                    elif check == 'False':
3337                        is_true = False
3338                    else:
3339                        is_true = None
3340                    if is_true != None:
3341                        item[0].SetValue(is_true)
3342            # 1D
3343            else:
3344                ## for 1D all parameters except orientation
3345                if not item[1] in orient_param:
3346                    name = item[1]
3347                    if name in content.keys():
3348                        check = content[name][0]
3349                        # Avoid changing combox content
3350                        value = content[name][1:]
3351                        pd = value[0]
3352                        if name.count('.') > 0:
3353                            try:
3354                                pd = float(pd)
3355                            except:
3356                                #continue
3357                                if not pd and pd != '':
3358                                    continue
3359                        item[2].SetValue(str(pd))
3360                        if item in self.fixed_param and pd == '':
3361                            # Only array func has pd == '' case.
3362                            item[2].Enable(False)
3363                        if item[2].__class__.__name__ == "ComboBox":
3364                            if value[0] in self.model.fun_list:
3365                                fun_val = self.model.fun_list[value[0]]
3366                                self.model.setParam(name, fun_val)
3367                                # save state
3368                        self._paste_poly_help(item, value)
3369                        if check == 'True':
3370                            is_true = True
3371                        elif check == 'False':
3372                            is_true = False
3373                        else:
3374                            is_true = None
3375                        if is_true != None:
3376                            item[0].SetValue(is_true)
3377
3378    def _paste_poly_help(self, item, value):
3379        """
3380        Helps get paste for poly function
3381
3382        :param item: Gui param items
3383        :param value: the values for parameter ctrols
3384        """
3385        is_array = False
3386        if len(value[1]) > 0:
3387            # Only for dispersion func.s
3388            try:
3389                item[7].SetValue(value[1])
3390                selection = item[7].GetCurrentSelection()
3391                name = item[7].Name
3392                param_name = name.split('.')[0]
3393                dispersity = item[7].GetClientData(selection)
3394                disp_model = dispersity()
3395                # Only for array disp
3396                try:
3397                    pd_vals = numpy.array(value[2])
3398                    pd_weights = numpy.array(value[3])
3399                    if len(pd_vals) > 0 and len(pd_vals) > 0:
3400                        if len(pd_vals) == len(pd_weights):
3401                            self._set_disp_array_cb(item=item)
3402                            self._set_array_disp_model(name=name,
3403                                                       disp=disp_model,
3404                                                       values=pd_vals,
3405                                                       weights=pd_weights)
3406                            is_array = True
3407                except:
3408                    logging.error(sys.exc_info()[1])
3409                if not is_array:
3410                    self._disp_obj_dict[name] = disp_model
3411                    self.model.set_dispersion(name,
3412                                              disp_model)
3413                    self.state._disp_obj_dict[name] = \
3414                                              disp_model
3415                    self.model.set_dispersion(param_name, disp_model)
3416                    self.state.values = self.values
3417                    self.state.weights = self.weights
3418                    self.model._persistency_dict[param_name] = \
3419                                            [self.state.values,
3420                                             self.state.weights]
3421
3422            except:
3423                logging.error(sys.exc_info()[1])
3424                print "Error in BasePage._paste_poly_help: %s" % \
3425                                        sys.exc_info()[1]
3426
3427    def _set_disp_array_cb(self, item):
3428        """
3429        Set cb for array disp
3430        """
3431        item[0].SetValue(False)
3432        item[0].Enable(False)
3433        item[2].Enable(False)
3434        item[3].Show(False)
3435        item[4].Show(False)
3436        item[5].SetValue('')
3437        item[5].Enable(False)
3438        item[6].SetValue('')
3439        item[6].Enable(False)
3440
3441    def update_pinhole_smear(self):
3442        """
3443            Method to be called by sub-classes
3444            Moveit; This method doesn't belong here
3445        """
3446        print "BasicPage.update_pinhole_smear was called: skipping"
3447        return
3448
3449    def _read_category_info(self):
3450        """
3451        Reads the categories in from file
3452        """
3453        # # ILL mod starts here - July 2012 kieranrcampbell@gmail.com
3454        self.master_category_dict = defaultdict(list)
3455        self.by_model_dict = defaultdict(list)
3456        self.model_enabled_dict = defaultdict(bool)
3457
3458        try:
3459            categorization_file = CategoryInstaller.get_user_file()
3460            if not os.path.isfile(categorization_file):
3461                categorization_file = CategoryInstaller.get_default_file()
3462            cat_file = open(categorization_file, 'rb')
3463            self.master_category_dict = json.load(cat_file)
3464            self._regenerate_model_dict()
3465            cat_file.close()
3466        except IOError:
3467            raise
3468            print 'Problem reading in category file.'
3469            print 'We even looked for it, made sure it was there.'
3470            print 'An existential crisis if there ever was one.'
3471
3472    def _regenerate_model_dict(self):
3473        """
3474        regenerates self.by_model_dict which has each model name as the
3475        key and the list of categories belonging to that model
3476        along with the enabled mapping
3477        """
3478        self.by_model_dict = defaultdict(list)
3479        for category in self.master_category_dict:
3480            for (model, enabled) in self.master_category_dict[category]:
3481                self.by_model_dict[model].append(category)
3482                self.model_enabled_dict[model] = enabled
3483
3484    def _populate_listbox(self):
3485        """
3486        fills out the category list box
3487        """
3488        uncat_str = 'Customized Models'
3489        self._read_category_info()
3490
3491        self.categorybox.Clear()
3492        cat_list = sorted(self.master_category_dict.keys())
3493        if not uncat_str in cat_list:
3494            cat_list.append(uncat_str)
3495
3496        for category in cat_list:
3497            if category != '':
3498                self.categorybox.Append(category)
3499
3500        if self.categorybox.GetSelection() == wx.NOT_FOUND:
3501            self.categorybox.SetSelection(0)
3502        else:
3503            self.categorybox.SetSelection(\
3504                self.categorybox.GetSelection())
3505        #self._on_change_cat(None)
3506
3507    def _on_change_cat(self, event):
3508        """
3509        Callback for category change action
3510        """
3511        self.model_name = None
3512        category = self.categorybox.GetStringSelection()
3513        if category == None:
3514            return
3515        self.model_box.Clear()
3516
3517        if category == 'Customized Models':
3518            for model in self.model_list_box[category]:
3519                str_m = str(model).split(".")[0]
3520                self.model_box.Append(str_m)
3521
3522        else:
3523            for (model, enabled) in sorted(self.master_category_dict[category],
3524                                      key=lambda name: name[0]):
3525                if(enabled):
3526                    self.model_box.Append(model)
3527
3528    def _fill_model_sizer(self, sizer):
3529        """
3530        fill sizer containing model info
3531        """
3532        # This should only be called once per fit tab
3533        #print "==== Entering _fill_model_sizer"
3534        ##Add model function Details button in fitpanel.
3535        ##The following 3 lines are for Mac. Let JHC know before modifying...
3536        title = "Model"
3537        self.formfactorbox = None
3538        self.multifactorbox = None
3539        self.mbox_description = wx.StaticBox(self, wx.ID_ANY, str(title))
3540        boxsizer1 = wx.StaticBoxSizer(self.mbox_description, wx.VERTICAL)
3541        sizer_cat = wx.BoxSizer(wx.HORIZONTAL)
3542        self.mbox_description.SetForegroundColour(wx.RED)
3543        wx_id = self._ids.next()
3544        self.model_func = wx.Button(self, wx_id, 'Help', size=(80, 23))
3545        self.model_func.Bind(wx.EVT_BUTTON, self.on_function_help_clicked,
3546                             id=wx_id)
3547        self.model_func.SetToolTipString("Full Model Function Help")
3548        wx_id = self._ids.next()
3549        self.model_help = wx.Button(self, wx_id, 'Description', size=(80, 23))
3550        self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked,
3551                             id=wx_id)
3552        self.model_help.SetToolTipString("Short Model Function Description")
3553        wx_id = self._ids.next()
3554        self.model_view = wx.Button(self, wx_id, "Show 2D", size=(80, 23))
3555        self.model_view.Bind(wx.EVT_BUTTON, self._onModel2D, id=wx_id)
3556        hint = "toggle view of model from 1D to 2D  or 2D to 1D"
3557        self.model_view.SetToolTipString(hint)
3558
3559        cat_set_box = wx.StaticBox(self, wx.ID_ANY, 'Category')
3560        sizer_cat_box = wx.StaticBoxSizer(cat_set_box, wx.HORIZONTAL)
3561        sizer_cat_box.SetMinSize((200, 50))
3562        self.categorybox = wx.ComboBox(self, wx.ID_ANY,
3563                                       style=wx.CB_READONLY)
3564        self.categorybox.SetToolTip(wx.ToolTip("Select a Category/Type"))
3565        self._populate_listbox()
3566        wx.EVT_COMBOBOX(self.categorybox, wx.ID_ANY, self._show_combox)
3567        #self.shape_rbutton = wx.RadioButton(self, wx.ID_ANY, 'Shapes',
3568        #                                     style=wx.RB_GROUP)
3569        #self.shape_indep_rbutton = wx.RadioButton(self, wx.ID_ANY,
3570        #                                          "Shape-Independent")
3571        #self.struct_rbutton = wx.RadioButton(self, wx.ID_ANY,
3572        #                                     "Structure Factor ")
3573        #self.plugin_rbutton = wx.RadioButton(self, wx.ID_ANY,
3574        #                                     "Uncategorized")
3575
3576        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3577        #                   id=self.shape_rbutton.GetId())
3578        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3579        #                    id=self.shape_indep_rbutton.GetId())
3580        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3581        #                    id=self.struct_rbutton.GetId())
3582        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3583        #                    id=self.plugin_rbutton.GetId())
3584        #MAC needs SetValue
3585
3586        show_cat_button = wx.Button(self, wx.ID_ANY, "Modify")
3587        cat_tip = "Modify model categories \n"
3588        cat_tip += "(also accessible from the menu bar)."
3589        show_cat_button.SetToolTip(wx.ToolTip(cat_tip))
3590        show_cat_button.Bind(wx.EVT_BUTTON, self._on_modify_cat)
3591        sizer_cat_box.Add(self.categorybox, 1, wx.RIGHT, 3)
3592        sizer_cat_box.Add((10, 10))
3593        sizer_cat_box.Add(show_cat_button)
3594        #self.shape_rbutton.SetValue(True)
3595
3596        sizer_radiobutton = wx.GridSizer(2, 2, 5, 5)
3597        #sizer_radiobutton.Add(self.shape_rbutton)
3598        #sizer_radiobutton.Add(self.shape_indep_rbutton)
3599        sizer_radiobutton.Add((5, 5))
3600        sizer_radiobutton.Add(self.model_view, 1, wx.RIGHT, 5)
3601        #sizer_radiobutton.Add(self.plugin_rbutton)
3602        #sizer_radiobutton.Add(self.struct_rbutton)
3603#        sizer_radiobutton.Add((5,5))
3604        sizer_radiobutton.Add(self.model_help, 1, wx.RIGHT | wx.LEFT, 5)
3605#        sizer_radiobutton.Add((5,5))
3606        sizer_radiobutton.Add(self.model_func, 1, wx.RIGHT, 5)
3607        sizer_cat.Add(sizer_cat_box, 1, wx.LEFT, 2.5)
3608        sizer_cat.Add(sizer_radiobutton)
3609        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
3610        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
3611
3612        self.text1 = wx.StaticText(self, wx.ID_ANY, "")
3613        self.text2 = wx.StaticText(self, wx.ID_ANY, "P(Q)*S(Q)")
3614        self.mutifactor_text = wx.StaticText(self, wx.ID_ANY, "No. of Shells: ")
3615        self.mutifactor_text1 = wx.StaticText(self, wx.ID_ANY, "")
3616        self.show_sld_button = wx.Button(self, wx.ID_ANY, "Show SLD Profile")
3617        self.show_sld_button.Bind(wx.EVT_BUTTON, self._on_show_sld)
3618
3619        self.formfactorbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3620        self.formfactorbox.SetToolTip(wx.ToolTip("Select a Model"))
3621        if self.model != None:
3622            self.formfactorbox.SetValue(self.model.name)
3623        self.structurebox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3624        self.multifactorbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3625        self.initialize_combox()
3626        wx.EVT_COMBOBOX(self.formfactorbox, wx.ID_ANY, self._on_select_model)
3627
3628        wx.EVT_COMBOBOX(self.structurebox, wx.ID_ANY, self._on_select_model)
3629        wx.EVT_COMBOBOX(self.multifactorbox, wx.ID_ANY, self._on_select_model)
3630        ## check model type to show sizer
3631        if self.model != None:
3632            print "_set_model_sizer_selection: disabled."
3633            #self._set_model_sizer_selection(self.model)
3634
3635        sizer_selection.Add(self.text1)
3636        sizer_selection.Add((10, 5))
3637        sizer_selection.Add(self.formfactorbox)
3638        sizer_selection.Add((5, 5))
3639        sizer_selection.Add(self.text2)
3640        sizer_selection.Add((5, 5))
3641        sizer_selection.Add(self.structurebox)
3642
3643        mutifactor_selection.Add((13, 5))
3644        mutifactor_selection.Add(self.mutifactor_text)
3645        mutifactor_selection.Add(self.multifactorbox)
3646        mutifactor_selection.Add((5, 5))
3647        mutifactor_selection.Add(self.mutifactor_text1)
3648        mutifactor_selection.Add((10, 5))
3649        mutifactor_selection.Add(self.show_sld_button)
3650
3651        boxsizer1.Add(sizer_cat)
3652        boxsizer1.Add((10, 10))
3653        boxsizer1.Add(sizer_selection)
3654        boxsizer1.Add((10, 10))
3655        boxsizer1.Add(mutifactor_selection)
3656
3657        self._set_multfactor_combobox()
3658        self.multifactorbox.SetSelection(1)
3659        self.show_sld_button.Hide()
3660        sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3661        sizer.Layout()
3662
3663    def on_smear_helper(self, update=False):
3664        """
3665        Help for onSmear if implemented
3666
3667        :param update: force or not to update
3668        """
3669    def reset_page(self, state, first=False):
3670        """
3671        reset the state  if implemented
3672        """
3673    def onSmear(self, event):
3674        """
3675        Create a smear object if implemented
3676        """
3677    def onPinholeSmear(self, event):
3678        """
3679        Create a custom pinhole smear object if implemented
3680        """
3681    def onSlitSmear(self, event):
3682        """
3683        Create a custom slit smear object if implemented
3684        """
3685    def update_slit_smear(self):
3686        """
3687        called by kill_focus on pinhole TextCntrl
3688        to update the changes if implemented
3689        """
3690    def select_param(self, event):
3691        """
3692        Select TextCtrl  checked if implemented
3693        """
3694    def set_data(self, data=None):
3695        """
3696        Sets data if implemented
3697        """
3698    def _is_2D(self):
3699        """
3700        Check if data_name is Data2D if implemented
3701        """
3702    def _on_select_model(self, event=None):
3703        """
3704        call back for model selection if implemented
3705        """
3706    def select_all_param(self, event):
3707        """
3708        set to true or false all checkBox if implemented
3709        """
3710    def get_weight_flag(self):
3711        """
3712        Get flag corresponding to a given weighting dI data if implemented
3713        """
3714    def _set_sizer_dispersion(self):
3715        """
3716        draw sizer for dispersity if implemented
3717        """
3718    def get_all_checked_params(self):
3719        """
3720        Found all parameters current check and add them to list of parameters
3721        to fit if implemented
3722        """
3723    def show_npts2fit(self):
3724        """
3725        setValue Npts for fitting if implemented
3726        """
3727    def _onModel2D(self, event):
3728        """
3729        toggle view of model from 1D to 2D  or 2D from 1D if implemented
3730        """
3731
3732class ModelTextCtrl(wx.TextCtrl):
3733    """
3734    Text control for model and fit parameters.
3735    Binds the appropriate events for user interactions.
3736    Default callback methods can be overwritten on initialization
3737
3738    :param kill_focus_callback: callback method for EVT_KILL_FOCUS event
3739    :param set_focus_callback:  callback method for EVT_SET_FOCUS event
3740    :param mouse_up_callback:   callback method for EVT_LEFT_UP event
3741    :param text_enter_callback: callback method for EVT_TEXT_ENTER event
3742
3743    """
3744    ## Set to True when the mouse is clicked while whole string is selected
3745    full_selection = False
3746    ## Call back for EVT_SET_FOCUS events
3747    _on_set_focus_callback = None
3748
3749    def __init__(self, parent, id=-1,
3750                 value=wx.EmptyString,
3751                 pos=wx.DefaultPosition,
3752                 size=wx.DefaultSize,
3753                 style=0,
3754                 validator=wx.DefaultValidator,
3755                 name=wx.TextCtrlNameStr,
3756                 kill_focus_callback=None,
3757                 set_focus_callback=None,
3758                 mouse_up_callback=None,
3759                 text_enter_callback=None):
3760
3761        wx.TextCtrl.__init__(self, parent, id, value, pos,
3762                             size, style, validator, name)
3763
3764        # Bind appropriate events
3765        self._on_set_focus_callback = parent.onSetFocus \
3766            if set_focus_callback is None else set_focus_callback
3767        self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
3768        self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus \
3769            if kill_focus_callback is None else kill_focus_callback)
3770        self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter \
3771            if text_enter_callback is None else text_enter_callback)
3772        if not ON_MAC:
3773            self.Bind(wx.EVT_LEFT_UP, self._highlight_text \
3774                if mouse_up_callback is None else mouse_up_callback)
3775
3776    def _on_set_focus(self, event):
3777        """
3778        Catch when the text control is set in focus to highlight the whole
3779        text if necessary
3780
3781        :param event: mouse event
3782
3783        """
3784        event.Skip()
3785        self.full_selection = True
3786        return self._on_set_focus_callback(event)
3787
3788    def _highlight_text(self, event):
3789        """
3790        Highlight text of a TextCtrl only of no text has be selected
3791
3792        :param event: mouse event
3793
3794        """
3795        # Make sure the mouse event is available to other listeners
3796        event.Skip()
3797        control = event.GetEventObject()
3798        if self.full_selection:
3799            self.full_selection = False
3800            # Check that we have a TextCtrl
3801            if issubclass(control.__class__, wx.TextCtrl):
3802                # Check whether text has been selected,
3803                # if not, select the whole string
3804                (start, end) = control.GetSelection()
3805                if start == end:
3806                    control.SetSelection(-1, -1)
3807
3808    def _silent_kill_focus(self, event):
3809        """
3810        Save the state of the page
3811        """
3812
3813        event.Skip()
3814        #pass
3815
Note: See TracBrowser for help on using the repository browser.