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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 8d3d20a was c65a265, checked in by ajj, 7 years ago

Removing logic that caused compute button to do nothing when data was present in the fit page.

Fixes #694

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