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

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

support alternate distributions in save/load and copy/paste. Closes #669

  • 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 not self.data.is_data:
1524                is_modified = True
1525
1526            ## if any value is modify draw model with new value
1527            if not self.fitrange:
1528                #self.btFit.Disable()
1529                if is_2Ddata:
1530                    self.btEditMask.Disable()
1531            else:
1532                if is_2Ddata and self.data.is_data and not self.batch_on:
1533                    self.btEditMask.Enable(True)
1534            if is_modified and self.fitrange:
1535                # Theory case: need to get npts value to draw
1536                self.npts_x = float(self.Npts_total.GetValue())
1537                self.create_default_data()
1538                self.state_change = True
1539                self._draw_model()
1540                self.Refresh()
1541        return is_modified
1542
1543    def _update_paramv_on_fit(self):
1544        """
1545        make sure that update param values just before the fitting
1546        """
1547        #flag for qmin qmax check values
1548        flag = True
1549        self.fitrange = True
1550
1551        #wx.PostEvent(self._manager.parent, StatusEvent(status=" \
1552        #updating ... ",type="update"))
1553
1554        ##So make sure that update param values on_Fit.
1555        #self._undo.Enable(True)
1556        if self.model != None:
1557            if self.Npts_total.GetValue() != self.Npts_fit.GetValue():
1558                if not self.data.is_data:
1559                    self._manager.page_finder[self.uid].set_fit_data(data=\
1560                                                                [self.data])
1561            ##Check the values
1562            self._check_value_enter(self.fittable_param)
1563            self._check_value_enter(self.fixed_param)
1564            self._check_value_enter(self.parameters)
1565
1566            # If qmin and qmax have been modified, update qmin and qmax and
1567            # Here we should check whether the boundaries have been modified.
1568            # If qmin and qmax have been modified, update qmin and qmax and
1569            # set the is_modified flag to True
1570            self.fitrange = self._validate_qrange(self.qmin, self.qmax)
1571            if self.fitrange:
1572                tempmin = float(self.qmin.GetValue())
1573                if tempmin != self.qmin_x:
1574                    self.qmin_x = tempmin
1575                tempmax = float(self.qmax.GetValue())
1576                if tempmax != self.qmax_x:
1577                    self.qmax_x = tempmax
1578                if tempmax == tempmin:
1579                    flag = False
1580                temp_smearer = None
1581                if not self.disable_smearer.GetValue():
1582                    temp_smearer = self.current_smearer
1583                    if self.slit_smearer.GetValue():
1584                        flag = self.update_slit_smear()
1585                    elif self.pinhole_smearer.GetValue():
1586                        flag = self.update_pinhole_smear()
1587                    else:
1588                        enable_smearer = not self.disable_smearer.GetValue()
1589                        self._manager.set_smearer(smearer=temp_smearer,
1590                                                  uid=self.uid,
1591                                                  fid=self.data.id,
1592                                                  qmin=float(self.qmin_x),
1593                                                  qmax=float(self.qmax_x),
1594                                                  enable_smearer=enable_smearer,
1595                                                  draw=False)
1596                elif not self._is_2D():
1597                    enable_smearer = not self.disable_smearer.GetValue()
1598                    self._manager.set_smearer(smearer=temp_smearer,
1599                                              qmin=float(self.qmin_x),
1600                                              uid=self.uid,
1601                                              fid=self.data.id,
1602                                              qmax=float(self.qmax_x),
1603                                              enable_smearer=enable_smearer,
1604                                              draw=False)
1605                    if self.data != None:
1606                        index_data = ((self.qmin_x <= self.data.x) & \
1607                                      (self.data.x <= self.qmax_x))
1608                        val = str(len(self.data.x[index_data == True]))
1609                        self.Npts_fit.SetValue(val)
1610                    else:
1611                        # No data in the panel
1612                        try:
1613                            self.npts_x = float(self.Npts_total.GetValue())
1614                        except:
1615                            flag = False
1616                            return flag
1617                    flag = True
1618                if self._is_2D():
1619                    # only 2D case set mask
1620                    flag = self._validate_Npts()
1621                    if not flag:
1622                        return flag
1623            else:
1624                flag = False
1625        else:
1626            flag = False
1627
1628        #For invalid q range, disable the mask editor and fit button, vs.
1629        if not self.fitrange:
1630            if self._is_2D():
1631                self.btEditMask.Disable()
1632        else:
1633            if self._is_2D() and  self.data.is_data and not self.batch_on:
1634                self.btEditMask.Enable(True)
1635
1636        if not flag:
1637            msg = "Cannot Plot or Fit :Must select a "
1638            msg += " model or Fitting range is not valid!!!  "
1639            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1640
1641        try:
1642            self.save_current_state()
1643        except Exception:
1644            logging.error(traceback.format_exc())
1645
1646        return flag
1647
1648    def _reset_parameters_state(self, listtorestore, statelist):
1649        """
1650        Reset the parameters at the given state
1651        """
1652        if len(statelist) == 0 or len(listtorestore) == 0:
1653            return
1654        if len(statelist) != len(listtorestore):
1655            return
1656
1657        for j in range(len(listtorestore)):
1658            item_page = listtorestore[j]
1659            item_page_info = statelist[j]
1660            ##change the state of the check box for simple parameters
1661            if item_page[0] != None:
1662                item_page[0].SetValue(item_page_info[0])
1663            if item_page[2] != None:
1664                item_page[2].SetValue(item_page_info[2])
1665                if item_page[2].__class__.__name__ == "ComboBox":
1666                    if item_page_info[2] in self.model.fun_list:
1667                        fun_val = self.model.fun_list[item_page_info[2]]
1668                        self.model.setParam(item_page_info[1], fun_val)
1669            if item_page[3] != None:
1670                ## show or hide text +/-
1671                if item_page_info[2]:
1672                    item_page[3].Show(True)
1673                else:
1674                    item_page[3].Hide()
1675            if item_page[4] != None:
1676                ## show of hide the text crtl for fitting error
1677                if item_page_info[4][0]:
1678                    item_page[4].Show(True)
1679                    item_page[4].SetValue(item_page_info[4][1])
1680                else:
1681                    item_page[3].Hide()
1682            if item_page[5] != None:
1683                ## show of hide the text crtl for fitting error
1684                item_page[5].Show(item_page_info[5][0])
1685                item_page[5].SetValue(item_page_info[5][1])
1686
1687            if item_page[6] != None:
1688                ## show of hide the text crtl for fitting error
1689                item_page[6].Show(item_page_info[6][0])
1690                item_page[6].SetValue(item_page_info[6][1])
1691
1692    def _reset_strparam_state(self, listtorestore, statelist):
1693        """
1694        Reset the string parameters at the given state
1695        """
1696        if len(statelist) == 0:
1697            return
1698
1699        listtorestore = copy.deepcopy(statelist)
1700
1701        for j in range(len(listtorestore)):
1702            item_page = listtorestore[j]
1703            item_page_info = statelist[j]
1704            ##change the state of the check box for simple parameters
1705
1706            if item_page[0] != None:
1707                item_page[0].SetValue(format_number(item_page_info[0], True))
1708
1709            if item_page[2] != None:
1710                param_name = item_page_info[1]
1711                value = item_page_info[2]
1712                selection = value
1713                if value in self.model.fun_list:
1714                    selection = self.model.fun_list[value]
1715                item_page[2].SetValue(selection)
1716                self.model.setParam(param_name, selection)
1717
1718    def _copy_parameters_state(self, listtocopy, statelist):
1719        """
1720        copy the state of button
1721
1722        :param listtocopy: the list of check button to copy
1723        :param statelist: list of state object to store the current state
1724
1725        """
1726        if len(listtocopy) == 0:
1727            return
1728
1729        for item in listtocopy:
1730
1731            checkbox_state = None
1732            if item[0] != None:
1733                checkbox_state = item[0].GetValue()
1734            parameter_name = item[1]
1735            parameter_value = None
1736            if item[2] != None:
1737                parameter_value = item[2].GetValue()
1738            static_text = None
1739            if item[3] != None:
1740                static_text = item[3].IsShown()
1741            error_value = None
1742            error_state = None
1743            if item[4] != None:
1744                error_value = item[4].GetValue()
1745                error_state = item[4].IsShown()
1746
1747            min_value = None
1748            min_state = None
1749            if item[5] != None:
1750                min_value = item[5].GetValue()
1751                min_state = item[5].IsShown()
1752
1753            max_value = None
1754            max_state = None
1755            if item[6] != None:
1756                max_value = item[6].GetValue()
1757                max_state = item[6].IsShown()
1758            unit = None
1759            if item[7] != None:
1760                unit = item[7].GetLabel()
1761
1762            statelist.append([checkbox_state, parameter_name, parameter_value,
1763                              static_text, [error_state, error_value],
1764                              [min_state, min_value],
1765                              [max_state, max_value], unit])
1766
1767
1768    def _draw_model(self, update_chisqr=True, source='model'):
1769        """
1770        Method to draw or refresh a plotted model.
1771        The method will use the data member from the model page
1772        to build a call to the fitting perspective manager.
1773
1774        :param chisqr: update chisqr value [bool]
1775        """
1776        wx.CallAfter(self._draw_model_after, update_chisqr, source)
1777
1778    def _draw_model_after(self, update_chisqr=True, source='model'):
1779        """
1780        Method to draw or refresh a plotted model.
1781        The method will use the data member from the model page
1782        to build a call to the fitting perspective manager.
1783
1784        :param chisqr: update chisqr value [bool]
1785        """
1786        #if self.check_invalid_panel():
1787        #    return
1788        if self.model != None:
1789            temp_smear = None
1790            if hasattr(self, "enable_smearer"):
1791                if not self.disable_smearer.GetValue():
1792                    temp_smear = self.current_smearer
1793            # compute weight for the current data
1794            from sas.sasgui.perspectives.fitting.utils import get_weight
1795            flag = self.get_weight_flag()
1796            weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
1797            toggle_mode_on = self.model_view.IsEnabled()
1798            is_2d = self._is_2D()
1799            self._manager.draw_model(self.model,
1800                                    data=self.data,
1801                                    smearer=temp_smear,
1802                                    qmin=float(self.qmin_x),
1803                                    qmax=float(self.qmax_x),
1804                                    page_id=self.uid,
1805                                    toggle_mode_on=toggle_mode_on,
1806                                    state=self.state,
1807                                    enable2D=is_2d,
1808                                    update_chisqr=update_chisqr,
1809                                    source='model',
1810                                    weight=weight)
1811
1812    def _on_show_sld(self, event=None):
1813        """
1814        Plot SLD profile
1815        """
1816        # get profile data
1817        x, y = self.model.getProfile()
1818
1819        from sas.sasgui.plottools import Data1D as pf_data1d
1820        #from sas.sasgui.perspectives.theory.profile_dialog import SLDPanel
1821        from sas.sasgui.guiframe.local_perspectives.plotting.profile_dialog \
1822        import SLDPanel
1823        sld_data = pf_data1d(x, y)
1824        sld_data.name = 'SLD'
1825        sld_data.axes = self.sld_axes
1826        self.panel = SLDPanel(self, data=sld_data, axes=self.sld_axes,
1827                              id=wx.ID_ANY)
1828        self.panel.ShowModal()
1829
1830    def _set_multfactor_combobox(self, multiplicity=10):
1831        """
1832        Set comboBox for muitfactor of CoreMultiShellModel
1833        :param multiplicit: no. of multi-functionality
1834        """
1835        # build content of the combobox
1836        for idx in range(0, multiplicity):
1837            self.multifactorbox.Append(str(idx), int(idx))
1838        self._hide_multfactor_combobox()
1839
1840    def _show_multfactor_combobox(self):
1841        """
1842        Show the comboBox of muitfactor of CoreMultiShellModel
1843        """
1844        if not self.mutifactor_text.IsShown():
1845            self.mutifactor_text.Show(True)
1846            self.mutifactor_text1.Show(True)
1847        if not self.multifactorbox.IsShown():
1848            self.multifactorbox.Show(True)
1849
1850    def _hide_multfactor_combobox(self):
1851        """
1852        Hide the comboBox of muitfactor of CoreMultiShellModel
1853        """
1854        if self.mutifactor_text.IsShown():
1855            self.mutifactor_text.Hide()
1856            self.mutifactor_text1.Hide()
1857        if self.multifactorbox.IsShown():
1858            self.multifactorbox.Hide()
1859
1860    def formfactor_combo_init(self):
1861        """
1862        First time calls _show_combox_helper
1863        """
1864        self._show_combox(None)
1865
1866    def _show_combox_helper(self):
1867        """
1868        Fill panel's combo box according to the type of model selected
1869        """
1870        custom_model = 'Customized Models'
1871        mod_cat = self.categorybox.GetStringSelection()
1872        self.structurebox.SetSelection(0)
1873        self.structurebox.Disable()
1874        self.formfactorbox.Clear()
1875        if mod_cat == None:
1876            return
1877        m_list = []
1878        try:
1879            if mod_cat == custom_model:
1880                for model in self.model_list_box[mod_cat]:
1881                    str_m = model.id if hasattr(model, 'id') else model.name
1882                    m_list.append(self.model_dict[str_m])
1883            else:
1884                cat_dic = self.master_category_dict[mod_cat]
1885                for (model, enabled) in cat_dic:
1886                    if enabled:
1887                        m_list.append(self.model_dict[model])
1888                    #else:
1889                    #    msg = "This model is disabled by Category Manager."
1890                    #    wx.PostEvent(self.parent.parent,
1891                    #                 StatusEvent(status=msg, info="error"))
1892        except Exception:
1893            msg = traceback.format_exc()
1894            wx.PostEvent(self._manager.parent,
1895                         StatusEvent(status=msg, info="error"))
1896        self._populate_box(self.formfactorbox, m_list)
1897
1898    def _on_modify_cat(self, event=None):
1899        """
1900        Called when category manager is opened
1901        """
1902        self._manager.parent.on_category_panel(event)
1903
1904    def _show_combox(self, event=None):
1905        """
1906        Show combox box associate with type of model selected
1907        """
1908        self.Show(False)
1909        self._show_combox_helper()
1910        self._on_select_model(event=None)
1911        self.Show(True)
1912        self._save_typeOfmodel()
1913        self.sizer4_4.Layout()
1914        self.sizer4.Layout()
1915        self.Layout()
1916        self.Refresh()
1917
1918    def _populate_box(self, combobox, list):
1919        """
1920        fill combox box with dict item
1921
1922        :param list: contains item to fill the combox
1923            item must model class
1924        """
1925        mlist = []
1926        for models in list:
1927            if models.name != "NoStructure":
1928                mlist.append((models.name, models))
1929
1930        # Sort the models
1931        mlist_sorted = sorted(mlist)
1932        for item in mlist_sorted:
1933            combobox.Append(item[0], item[1])
1934        return 0
1935
1936    def _onQrangeEnter(self, event):
1937        """
1938        Check validity of value enter in the Q range field
1939
1940        """
1941        tcrtl = event.GetEventObject()
1942        #Clear msg if previously shown.
1943        msg = ""
1944        wx.PostEvent(self.parent, StatusEvent(status=msg))
1945        # Flag to register when a parameter has changed.
1946        if tcrtl.GetValue().lstrip().rstrip() != "":
1947            try:
1948                float(tcrtl.GetValue())
1949                tcrtl.SetBackgroundColour(wx.WHITE)
1950                # If qmin and qmax have been modified, update qmin and qmax
1951                if self._validate_qrange(self.qmin, self.qmax):
1952                    tempmin = float(self.qmin.GetValue())
1953                    if tempmin != self.qmin_x:
1954                        self.qmin_x = tempmin
1955                    tempmax = float(self.qmax.GetValue())
1956                    if tempmax != self.qmax_x:
1957                        self.qmax_x = tempmax
1958                else:
1959                    tcrtl.SetBackgroundColour("pink")
1960                    msg = "Model Error: wrong value entered: %s" % \
1961                                    sys.exc_info()[1]
1962                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1963                    return
1964            except:
1965                tcrtl.SetBackgroundColour("pink")
1966                msg = "Model Error: wrong value entered: %s" % sys.exc_info()[1]
1967                wx.PostEvent(self.parent, StatusEvent(status=msg))
1968                return
1969            #Check if # of points for theory model are valid(>0).
1970            if self.npts != None:
1971                if check_float(self.npts):
1972                    temp_npts = float(self.npts.GetValue())
1973                    if temp_npts != self.num_points:
1974                        self.num_points = temp_npts
1975                else:
1976                    msg = "Cannot plot: No points in Q range!!!  "
1977                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1978        else:
1979            tcrtl.SetBackgroundColour("pink")
1980            msg = "Model Error: wrong value entered!!!"
1981            wx.PostEvent(self.parent, StatusEvent(status=msg))
1982        self.save_current_state()
1983        event = PageInfoEvent(page=self)
1984        wx.PostEvent(self.parent, event)
1985        self.state_change = False
1986        #Draw the model for a different range
1987        if not self.data.is_data:
1988            self.create_default_data()
1989        self._draw_model()
1990
1991    def _theory_qrange_enter(self, event):
1992        """
1993        Check validity of value enter in the Q range field
1994        """
1995
1996        tcrtl = event.GetEventObject()
1997        #Clear msg if previously shown.
1998        msg = ""
1999        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2000        # Flag to register when a parameter has changed.
2001        is_modified = False
2002        if tcrtl.GetValue().lstrip().rstrip() != "":
2003            try:
2004                value = float(tcrtl.GetValue())
2005                tcrtl.SetBackgroundColour(wx.WHITE)
2006
2007                # If qmin and qmax have been modified, update qmin and qmax
2008                if self._validate_qrange(self.theory_qmin, self.theory_qmax):
2009                    tempmin = float(self.theory_qmin.GetValue())
2010                    if tempmin != self.theory_qmin_x:
2011                        self.theory_qmin_x = tempmin
2012                    tempmax = float(self.theory_qmax.GetValue())
2013                    if tempmax != self.qmax_x:
2014                        self.theory_qmax_x = tempmax
2015                else:
2016                    tcrtl.SetBackgroundColour("pink")
2017                    msg = "Model Error: wrong value entered: %s" % \
2018                                        sys.exc_info()[1]
2019                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2020                    return
2021            except:
2022                tcrtl.SetBackgroundColour("pink")
2023                msg = "Model Error: wrong value entered: %s" % sys.exc_info()[1]
2024                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2025                return
2026            #Check if # of points for theory model are valid(>0).
2027            if self.Npts_total.IsEditable():
2028                if check_float(self.Npts_total):
2029                    temp_npts = float(self.Npts_total.GetValue())
2030                    if temp_npts != self.num_points:
2031                        self.num_points = temp_npts
2032                        is_modified = True
2033                else:
2034                    msg = "Cannot Plot: No points in Q range!!!  "
2035                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2036        else:
2037            tcrtl.SetBackgroundColour("pink")
2038            msg = "Model Error: wrong value entered!!!"
2039            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2040        self.save_current_state()
2041        event = PageInfoEvent(page=self)
2042        wx.PostEvent(self.parent, event)
2043        self.state_change = False
2044        #Draw the model for a different range
2045        self.create_default_data()
2046        self._draw_model()
2047
2048    def _on_select_model_helper(self):
2049        """
2050        call back for model selection
2051        """
2052        ## reset dictionary containing reference to dispersion
2053        self._disp_obj_dict = {}
2054        self.disp_cb_dict = {}
2055        self.temp_multi_functional = False
2056        f_id = self.formfactorbox.GetCurrentSelection()
2057        #For MAC
2058        form_factor = None
2059        if f_id >= 0:
2060            form_factor = self.formfactorbox.GetClientData(f_id)
2061
2062        if not form_factor in  self.model_list_box["multiplication"]:
2063            self.structurebox.Hide()
2064            self.text2.Hide()
2065            self.structurebox.Disable()
2066            self.structurebox.SetSelection(0)
2067            self.text2.Disable()
2068        else:
2069            self.structurebox.Show()
2070            self.text2.Show()
2071            self.structurebox.Enable()
2072            self.text2.Enable()
2073
2074        if form_factor != None:
2075            # set multifactor for Mutifunctional models
2076            if form_factor.is_multiplicity_model:
2077                m_id = self.multifactorbox.GetCurrentSelection()
2078                multiplicity = form_factor.multiplicity_info[0]
2079                self.multifactorbox.Clear()
2080                self._set_multfactor_combobox(multiplicity)
2081                self._show_multfactor_combobox()
2082                #ToDo:  this info should be called directly from the model
2083                text = form_factor.multiplicity_info[1]  # 'No. of Shells: '
2084
2085                self.mutifactor_text.SetLabel(text)
2086                if m_id > multiplicity - 1:
2087                    # default value
2088                    m_id = 1
2089
2090                self.multi_factor = self.multifactorbox.GetClientData(m_id)
2091                if self.multi_factor == None:
2092                    self.multi_factor = 0
2093                self.multifactorbox.SetSelection(m_id)
2094                # Check len of the text1 and max_multiplicity
2095                text = ''
2096                if form_factor.multiplicity_info[0] == \
2097                                        len(form_factor.multiplicity_info[2]):
2098                    text = form_factor.multiplicity_info[2][self.multi_factor]
2099                self.mutifactor_text1.SetLabel(text)
2100                # Check if model has  get sld profile.
2101                if len(form_factor.multiplicity_info[3]) > 0:
2102                    self.sld_axes = form_factor.multiplicity_info[3]
2103                    self.show_sld_button.Show(True)
2104                else:
2105                    self.sld_axes = ""
2106            else:
2107                self._hide_multfactor_combobox()
2108                self.show_sld_button.Hide()
2109                self.multi_factor = None
2110        else:
2111            self._hide_multfactor_combobox()
2112            self.show_sld_button.Hide()
2113            self.multi_factor = None
2114
2115        s_id = self.structurebox.GetCurrentSelection()
2116        struct_factor = self.structurebox.GetClientData(s_id)
2117
2118        if  struct_factor != None:
2119            from sas.sascalc.fit.MultiplicationModel import MultiplicationModel
2120            self.model = MultiplicationModel(form_factor(self.multi_factor),
2121                                             struct_factor())
2122            # multifunctional form factor
2123            if len(form_factor.non_fittable) > 0:
2124                self.temp_multi_functional = True
2125        elif form_factor != None:
2126            if self.multi_factor is not None:
2127                self.model = form_factor(self.multi_factor)
2128            else:
2129                # old style plugin models do not accept a multiplicity argument
2130                self.model = form_factor()
2131        else:
2132            self.model = None
2133            return
2134
2135        # check if model has magnetic parameters
2136        if len(self.model.magnetic_params) > 0:
2137            self._has_magnetic = True
2138        else:
2139            self._has_magnetic = False
2140        ## post state to fit panel
2141        self.state.parameters = []
2142        self.state.model = self.model
2143        self.state.qmin = self.qmin_x
2144        self.state.multi_factor = self.multi_factor
2145        self.disp_list = self.model.getDispParamList()
2146        self.state.disp_list = self.disp_list
2147        self.on_set_focus(None)
2148        self.Layout()
2149
2150
2151    def _validate_qrange(self, qmin_ctrl, qmax_ctrl):
2152        """
2153        Verify that the Q range controls have valid values
2154        and that Qmin < Qmax.
2155
2156        :param qmin_ctrl: text control for Qmin
2157        :param qmax_ctrl: text control for Qmax
2158
2159        :return: True is the Q range is value, False otherwise
2160
2161        """
2162        qmin_validity = check_float(qmin_ctrl)
2163        qmax_validity = check_float(qmax_ctrl)
2164        if not (qmin_validity and qmax_validity):
2165            return False
2166        else:
2167            qmin = float(qmin_ctrl.GetValue())
2168            qmax = float(qmax_ctrl.GetValue())
2169            if qmin < qmax:
2170                #Make sure to set both colours white.
2171                qmin_ctrl.SetBackgroundColour(wx.WHITE)
2172                qmin_ctrl.Refresh()
2173                qmax_ctrl.SetBackgroundColour(wx.WHITE)
2174                qmax_ctrl.Refresh()
2175            else:
2176                qmin_ctrl.SetBackgroundColour("pink")
2177                qmin_ctrl.Refresh()
2178                qmax_ctrl.SetBackgroundColour("pink")
2179                qmax_ctrl.Refresh()
2180                msg = "Invalid Q range: Q min must be smaller than Q max"
2181                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2182                return False
2183        return True
2184
2185    def _validate_Npts(self):
2186        """
2187        Validate the number of points for fitting is more than 10 points.
2188        If valid, setvalues Npts_fit otherwise post msg.
2189        """
2190        #default flag
2191        flag = True
2192        # Theory
2193        if self.data == None and self.enable2D:
2194            return flag
2195        for data in self.data_list:
2196            # q value from qx and qy
2197            radius = numpy.sqrt(data.qx_data * data.qx_data +
2198                                data.qy_data * data.qy_data)
2199            #get unmasked index
2200            index_data = (float(self.qmin.GetValue()) <= radius) & \
2201                            (radius <= float(self.qmax.GetValue()))
2202            index_data = (index_data) & (data.mask)
2203            index_data = (index_data) & (numpy.isfinite(data.data))
2204
2205            if len(index_data[index_data]) < 10:
2206                # change the color pink.
2207                self.qmin.SetBackgroundColour("pink")
2208                self.qmin.Refresh()
2209                self.qmax.SetBackgroundColour("pink")
2210                self.qmax.Refresh()
2211                msg = "Data Error: "
2212                msg += "Too few points in %s." % data.name
2213                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2214                self.fitrange = False
2215                flag = False
2216            else:
2217                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
2218                self.fitrange = True
2219
2220        return flag
2221
2222    def _validate_Npts_1D(self):
2223        """
2224        Validate the number of points for fitting is more than 5 points.
2225        If valid, setvalues Npts_fit otherwise post msg.
2226        """
2227        #default flag
2228        flag = True
2229        # Theory
2230        if self.data == None:
2231            return flag
2232        for data in self.data_list:
2233            # q value from qx and qy
2234            radius = data.x
2235            #get unmasked index
2236            index_data = (float(self.qmin.GetValue()) <= radius) & \
2237                            (radius <= float(self.qmax.GetValue()))
2238            index_data = (index_data) & (numpy.isfinite(data.y))
2239
2240            if len(index_data[index_data]) < 5:
2241                # change the color pink.
2242                self.qmin.SetBackgroundColour("pink")
2243                self.qmin.Refresh()
2244                self.qmax.SetBackgroundColour("pink")
2245                self.qmax.Refresh()
2246                msg = "Data Error: "
2247                msg += "Too few points in %s." % data.name
2248                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2249                self.fitrange = False
2250                flag = False
2251            else:
2252                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
2253                self.fitrange = True
2254
2255        return flag
2256
2257    def _check_value_enter(self, list):
2258        """
2259        :param list: model parameter and panel info
2260        :Note: each item of the list should be as follow:
2261            item=[check button state, parameter's name,
2262                paramater's value, string="+/-",
2263                parameter's error of fit,
2264                parameter's minimum value,
2265                parameter's maximum value ,
2266                parameter's units]
2267
2268        Returns True if the model parameters have changed.
2269        """
2270        is_modified = False
2271        for item in list:
2272            #skip angle parameters for 1D
2273            if not self.enable2D and item in self.orientation_params:
2274                continue
2275
2276            value_ctrl = item[2]
2277            if not value_ctrl.IsEnabled():
2278                # ArrayDispersion disables PD, Min, Max, Npts, Nsigs
2279                continue
2280
2281            name = item[1]
2282            value_str = value_ctrl.GetValue().strip()
2283            if name.endswith(".npts"):
2284                validity = check_int(value_ctrl)
2285                if not validity:
2286                    continue
2287                value = int(value_str)
2288
2289            elif name.endswith(".nsigmas"):
2290                validity = check_float(value_ctrl)
2291                if not validity:
2292                    continue
2293                value = float(value_str)
2294
2295            else:  # value or polydispersity
2296
2297                # Check that min, max and value are floats
2298                min_ctrl, max_ctrl = item[5], item[6]
2299                min_str = min_ctrl.GetValue().strip()
2300                max_str = max_ctrl.GetValue().strip()
2301                validity = check_float(value_ctrl)
2302                if min_str != "":
2303                    validity = validity and check_float(min_ctrl)
2304                if max_str != "":
2305                    validity = validity and check_float(max_ctrl)
2306                if not validity:
2307                    continue
2308
2309                # Check that min is less than max
2310                low = -numpy.inf if min_str == "" else float(min_str)
2311                high = numpy.inf if max_str == "" else float(max_str)
2312                if high < low:
2313                    min_ctrl.SetBackgroundColour("pink")
2314                    min_ctrl.Refresh()
2315                    max_ctrl.SetBackgroundColour("pink")
2316                    max_ctrl.Refresh()
2317                    #msg = "Invalid fit range for %s: min must be smaller than max"%name
2318                    #wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2319                    continue
2320
2321                # Force value between min and max
2322                value = float(value_str)
2323                if value < low:
2324                    value = low
2325                    value_ctrl.SetValue(format_number(value))
2326                elif value > high:
2327                    value = high
2328                    value_ctrl.SetValue(format_number(value))
2329
2330                if name not in self.model.details.keys():
2331                    self.model.details[name] = ["", None, None]
2332                old_low, old_high = self.model.details[name][1:3]
2333                if old_low != low or old_high != high:
2334                    # The configuration has changed but it won't change the
2335                    # computed curve so no need to set is_modified to True
2336                    #is_modified = True
2337                    self.model.details[name][1:3] = low, high
2338
2339            # Update value in model if it has changed
2340            if value != self.model.getParam(name):
2341                self.model.setParam(name, value)
2342                is_modified = True
2343
2344        return is_modified
2345
2346    def _set_dipers_Param(self, event):
2347        """
2348        respond to self.enable_disp and self.disable_disp radio box.
2349        The dispersity object is reset inside the model into Gaussian.
2350        When the user select yes , this method display a combo box for
2351        more selection when the user selects No,the combo box disappears.
2352        Redraw the model with the default dispersity (Gaussian)
2353        """
2354        ## On selction if no model exists.
2355        if self.model == None:
2356            self.disable_disp.SetValue(True)
2357            msg = "Please select a Model first..."
2358            wx.MessageBox(msg, 'Info')
2359            wx.PostEvent(self._manager.parent,
2360                         StatusEvent(status="Polydispersion: %s" % msg))
2361            return
2362
2363        self._reset_dispersity()
2364
2365        if self.model == None:
2366            self.model_disp.Hide()
2367            self.sizer4_4.Clear(True)
2368            return
2369
2370        if self.enable_disp.GetValue():
2371            ## layout for model containing no dispersity parameters
2372
2373            self.disp_list = self.model.getDispParamList()
2374
2375            if len(self.disp_list) == 0 and len(self.disp_cb_dict) == 0:
2376                self._layout_sizer_noDipers()
2377            else:
2378                ## set gaussian sizer
2379                self._on_select_Disp(event=None)
2380        else:
2381            self.sizer4_4.Clear(True)
2382
2383        ## post state to fit panel
2384        self.save_current_state()
2385        if event != None:
2386            event = PageInfoEvent(page=self)
2387            wx.PostEvent(self.parent, event)
2388        #draw the model with the current dispersity
2389        self._draw_model()
2390        ## Need to use FitInside again here to replace the next four lines.
2391        ## Otherwised polydispersity off does not resize the scrollwindow.
2392        ## PDB Nov 28, 2015
2393        self.FitInside()
2394#        self.sizer4_4.Layout()
2395#        self.sizer5.Layout()
2396#        self.Layout()
2397#        self.Refresh()
2398
2399    def _layout_sizer_noDipers(self):
2400        """
2401        Draw a sizer with no dispersity info
2402        """
2403        ix = 0
2404        iy = 1
2405        self.fittable_param = []
2406        self.fixed_param = []
2407        self.orientation_params_disp = []
2408
2409        self.sizer4_4.Clear(True)
2410        text = "No polydispersity available for this model"
2411        model_disp = wx.StaticText(self, wx.ID_ANY, text)
2412        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
2413                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
2414        self.sizer4_4.Layout()
2415        self.sizer4.Layout()
2416
2417    def _reset_dispersity(self):
2418        """
2419        put gaussian dispersity into current model
2420        """
2421        if len(self.param_toFit) > 0:
2422            for item in self.fittable_param:
2423                if item in self.param_toFit:
2424                    self.param_toFit.remove(item)
2425
2426            for item in self.orientation_params_disp:
2427                if item in self.param_toFit:
2428                    self.param_toFit.remove(item)
2429
2430        self.fittable_param = []
2431        self.fixed_param = []
2432        self.orientation_params_disp = []
2433        self.values = {}
2434        self.weights = {}
2435
2436        #from sas.models.dispersion_models import GaussianDispersion
2437        from sasmodels.weights import GaussianDispersion
2438        if len(self.disp_cb_dict) == 0:
2439            self.save_current_state()
2440            self.sizer4_4.Clear(True)
2441            self.Layout()
2442            return
2443        if (len(self.disp_cb_dict) > 0):
2444            for p in self.disp_cb_dict:
2445                # The parameter was un-selected.
2446                # Go back to Gaussian model (with 0 pts)
2447                disp_model = GaussianDispersion()
2448
2449                self._disp_obj_dict[p] = disp_model
2450                # Set the new model as the dispersion object
2451                # for the selected parameter
2452                try:
2453                    self.model.set_dispersion(p, disp_model)
2454                except Exception:
2455                    logging.error(traceback.format_exc())
2456
2457        ## save state into
2458        self.save_current_state()
2459        self.Layout()
2460        self.Refresh()
2461
2462    def _on_select_Disp(self, event):
2463        """
2464        allow selecting different dispersion
2465        self.disp_list should change type later .now only gaussian
2466        """
2467        self._set_sizer_dispersion()
2468
2469        ## Redraw the model
2470        self._draw_model()
2471        #self._undo.Enable(True)
2472        event = PageInfoEvent(page=self)
2473        wx.PostEvent(self.parent, event)
2474
2475        self.sizer4_4.Layout()
2476        self.sizer4.Layout()
2477        self.SetupScrolling()
2478
2479    def _on_disp_func(self, event=None):
2480        """
2481        Select a distribution function for the polydispersion
2482
2483        :Param event: ComboBox event
2484        """
2485        # get ready for new event
2486        if event != None:
2487            event.Skip()
2488        # Get event object
2489        disp_box = event.GetEventObject()
2490
2491        # Try to select a Distr. function
2492        try:
2493            disp_box.SetBackgroundColour("white")
2494            selection = disp_box.GetCurrentSelection()
2495            param_name = disp_box.Name.split('.')[0]
2496            disp_name = disp_box.GetValue()
2497            dispersity = disp_box.GetClientData(selection)
2498
2499            #disp_model =  GaussianDispersion()
2500            disp_model = dispersity()
2501            # Get param names to reset the values of the param
2502            name1 = param_name + ".width"
2503            name2 = param_name + ".npts"
2504            name3 = param_name + ".nsigmas"
2505            # Check Disp. function whether or not it is 'array'
2506            if disp_name.lower() == "array":
2507                value2 = ""
2508                value3 = ""
2509                value1 = self._set_array_disp(name=name1, disp=disp_model)
2510            else:
2511                self._del_array_values(name1)
2512                #self._reset_array_disp(param_name)
2513                self._disp_obj_dict[name1] = disp_model
2514                self.model.set_dispersion(param_name, disp_model)
2515                self.state._disp_obj_dict[name1] = disp_model.type
2516
2517                value1 = str(format_number(self.model.getParam(name1), True))
2518                value2 = str(format_number(self.model.getParam(name2)))
2519                value3 = str(format_number(self.model.getParam(name3)))
2520            # Reset fittable polydispersin parameter value
2521            for item in self.fittable_param:
2522                if item[1] == name1:
2523                    item[2].SetValue(value1)
2524                    item[5].SetValue("")
2525                    item[6].SetValue("")
2526                    # Disable for array
2527                    if disp_name.lower() == "array":
2528                        item[0].SetValue(False)
2529                        item[0].Disable()
2530                        item[2].Disable()
2531                        item[3].Show(False)
2532                        item[4].Show(False)
2533                        item[5].Disable()
2534                        item[6].Disable()
2535                    else:
2536                        item[0].Enable()
2537                        item[2].Enable()
2538                        item[3].Show(True)
2539                        item[4].Show(True)
2540                        item[5].Enable()
2541                        item[6].Enable()
2542                    break
2543            # Reset fixed polydispersion params
2544            for item in self.fixed_param:
2545                if item[1] == name2:
2546                    item[2].SetValue(value2)
2547                    # Disable Npts for array
2548                    if disp_name.lower() == "array":
2549                        item[2].Disable()
2550                    else:
2551                        item[2].Enable()
2552                if item[1] == name3:
2553                    item[2].SetValue(value3)
2554                    # Disable Nsigs for array
2555                    if disp_name.lower() == "array":
2556                        item[2].Disable()
2557                    else:
2558                        item[2].Enable()
2559
2560            # Make sure the check box updated when all checked
2561            if self.cb1.GetValue():
2562                #self.select_all_param(None)
2563                self.get_all_checked_params()
2564
2565            # update params
2566            self._update_paramv_on_fit()
2567            # draw
2568            self._draw_model()
2569            self.Refresh()
2570        except Exception:
2571            logging.error(traceback.format_exc())
2572            # Error msg
2573            msg = "Error occurred:"
2574            msg += " Could not select the distribution function..."
2575            msg += " Please select another distribution function."
2576            disp_box.SetBackgroundColour("pink")
2577            # Focus on Fit button so that users can see the pinky box
2578            self.btFit.SetFocus()
2579            wx.PostEvent(self._manager.parent,
2580                         StatusEvent(status=msg, info="error"))
2581
2582    def _set_array_disp(self, name=None, disp=None):
2583        """
2584        Set array dispersion
2585
2586        :param name: name of the parameter for the dispersion to be set
2587        :param disp: the polydisperion object
2588        """
2589        # The user wants this parameter to be averaged.
2590        # Pop up the file selection dialog.
2591        path = self._selectDlg()
2592        # Array data
2593        values = []
2594        weights = []
2595        # If nothing was selected, just return
2596        if path is None:
2597            self.disp_cb_dict[name].SetValue(False)
2598            #self.noDisper_rbox.SetValue(True)
2599            return
2600        self._default_save_location = os.path.dirname(path)
2601        if self._manager != None:
2602            self._manager.parent._default_save_location = \
2603                             self._default_save_location
2604
2605        basename = os.path.basename(path)
2606        values, weights = self.read_file(path)
2607
2608        # If any of the two arrays is empty, notify the user that we won't
2609        # proceed
2610        if len(self.param_toFit) > 0:
2611            if name in self.param_toFit:
2612                self.param_toFit.remove(name)
2613
2614        # Tell the user that we are about to apply the distribution
2615        msg = "Applying loaded %s distribution: %s" % (name, path)
2616        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2617        self._set_array_disp_model(name=name, disp=disp,
2618                                    values=values, weights=weights)
2619        return basename
2620
2621    def _set_array_disp_model(self, name=None, disp=None,
2622                              values=[], weights=[]):
2623        """
2624        Set array dispersion model
2625
2626        :param name: name of the parameter for the dispersion to be set
2627        :param disp: the polydisperion object
2628        """
2629        disp.set_weights(values, weights)
2630        self._disp_obj_dict[name] = disp
2631        self.model.set_dispersion(name.split('.')[0], disp)
2632        self.state._disp_obj_dict[name] = disp.type
2633        self.values[name] = values
2634        self.weights[name] = weights
2635        # Store the object to make it persist outside the
2636        # scope of this method
2637        #TODO: refactor model to clean this up?
2638        self.state.values = {}
2639        self.state.weights = {}
2640        self.state.values = copy.deepcopy(self.values)
2641        self.state.weights = copy.deepcopy(self.weights)
2642
2643        # Set the new model as the dispersion object for the
2644        #selected parameter
2645        #self.model.set_dispersion(p, disp_model)
2646        # Store a reference to the weights in the model object
2647        #so that
2648        # it's not lost when we use the model within another thread.
2649        self.state.model = self.model.clone()
2650        self.model._persistency_dict[name.split('.')[0]] = \
2651                                        [values, weights]
2652        self.state.model._persistency_dict[name.split('.')[0]] = \
2653                                        [values, weights]
2654
2655    def _del_array_values(self, name=None):
2656        """
2657        Reset array dispersion
2658
2659        :param name: name of the parameter for the dispersion to be set
2660        """
2661        # Try to delete values and weight of the names array dic if exists
2662        try:
2663            if name in self.values:
2664                del self.values[name]
2665                del self.weights[name]
2666                # delete all other dic
2667                del self.state.values[name]
2668                del self.state.weights[name]
2669                del self.model._persistency_dict[name.split('.')[0]]
2670                del self.state.model._persistency_dict[name.split('.')[0]]
2671        except Exception:
2672            logging.error(traceback.format_exc())
2673
2674    def _lay_out(self):
2675        """
2676        returns self.Layout
2677
2678        :Note: Mac seems to like this better when self.
2679            Layout is called after fitting.
2680        """
2681        self._sleep4sec()
2682        self.Layout()
2683        return
2684
2685    def _sleep4sec(self):
2686        """
2687            sleep for 1 sec only applied on Mac
2688            Note: This 1sec helps for Mac not to crash on self.
2689            Layout after self._draw_model
2690        """
2691        if ON_MAC == True:
2692            time.sleep(1)
2693
2694    def _find_polyfunc_selection(self, disp_func=None):
2695        """
2696        FInd Comboox selection from disp_func
2697
2698        :param disp_function: dispersion distr. function
2699        """
2700        # Find the selection
2701        if disp_func is not None:
2702            try:
2703                return POLYDISPERSITY_MODELS.values().index(disp_func.__class__)
2704            except ValueError:
2705                pass  # Fall through to default class
2706        return POLYDISPERSITY_MODELS.keys().index('gaussian')
2707
2708    def on_reset_clicked(self, event):
2709        """
2710        On 'Reset' button  for Q range clicked
2711        """
2712        flag = True
2713        ##For 3 different cases: Data2D, Data1D, and theory
2714        if self.model == None:
2715            msg = "Please select a model first..."
2716            wx.MessageBox(msg, 'Info')
2717            flag = False
2718            return
2719
2720        elif self.data.__class__.__name__ == "Data2D":
2721            data_min = 0
2722            x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax))
2723            y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
2724            self.qmin_x = data_min
2725            self.qmax_x = math.sqrt(x * x + y * y)
2726            #self.data.mask = numpy.ones(len(self.data.data),dtype=bool)
2727            # check smearing
2728            if not self.disable_smearer.GetValue():
2729                ## set smearing value whether or
2730                # not the data contain the smearing info
2731                if self.pinhole_smearer.GetValue():
2732                    flag = self.update_pinhole_smear()
2733                else:
2734                    flag = True
2735
2736        elif self.data == None:
2737            self.qmin_x = _QMIN_DEFAULT
2738            self.qmax_x = _QMAX_DEFAULT
2739            self.num_points = _NPTS_DEFAULT
2740            self.state.npts = self.num_points
2741
2742        elif self.data.__class__.__name__ != "Data2D":
2743            self.qmin_x = min(self.data.x)
2744            self.qmax_x = max(self.data.x)
2745            # check smearing
2746            if not self.disable_smearer.GetValue():
2747                ## set smearing value whether or
2748                # not the data contain the smearing info
2749                if self.slit_smearer.GetValue():
2750                    flag = self.update_slit_smear()
2751                elif self.pinhole_smearer.GetValue():
2752                    flag = self.update_pinhole_smear()
2753                else:
2754                    flag = True
2755        else:
2756            flag = False
2757
2758        if flag == False:
2759            msg = "Cannot Plot :Must enter a number!!!  "
2760            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2761        else:
2762            # set relative text ctrs.
2763            self.qmin.SetValue(str(self.qmin_x))
2764            self.qmax.SetValue(str(self.qmax_x))
2765            self.show_npts2fit()
2766            # At this point, some button and variables satatus (disabled?)
2767            # should be checked such as color that should be reset to
2768            # white in case that it was pink.
2769            self._onparamEnter_helper()
2770
2771        self.save_current_state()
2772        self.state.qmin = self.qmin_x
2773        self.state.qmax = self.qmax_x
2774
2775        #reset the q range values
2776        self._reset_plotting_range(self.state)
2777        self._draw_model()
2778
2779    def select_log(self, event):
2780        """
2781        Log checked to generate log spaced points for theory model
2782        """
2783
2784    def get_images(self):
2785        """
2786        Get the images of the plots corresponding this panel for report
2787
2788        : return graphs: list of figures
2789        : Need Move to guiframe
2790        """
2791        # set list of graphs
2792        graphs = []
2793        canvases = []
2794        res_item = None
2795        # call gui_manager
2796        gui_manager = self._manager.parent
2797        # loops through the panels [dic]
2798        for _, item2 in gui_manager.plot_panels.iteritems():
2799            data_title = self.data.group_id
2800            # try to get all plots belonging to this control panel
2801            try:
2802                g_id = item2.group_id
2803                if g_id == data_title or \
2804                        str(g_id).count("res" + str(self.graph_id)) or \
2805                        str(g_id).count(str(self.uid)) > 0:
2806                    if str(g_id).count("res" + str(self.graph_id)) > 0:
2807                        res_item = [item2.figure, item2.canvas]
2808                    else:
2809                        # append to the list
2810                        graphs.append(item2.figure)
2811                        canvases.append(item2.canvas)
2812            except Exception:
2813                # Not for control panels
2814                logging.error(traceback.format_exc())
2815        # Make sure the resduals plot goes to the last
2816        if res_item != None:
2817            graphs.append(res_item[0])
2818            canvases.append(res_item[1])
2819        # return the list of graphs
2820        return graphs, canvases
2821
2822    def on_function_help_clicked(self, event):
2823        """
2824        Function called when 'Help' button is pressed next to model
2825        of interest.  This calls DocumentationWindow from
2826        documentation_window.py. It will load the top level of the model
2827        help documenation sphinx generated html if no model is presented.
2828        If a model IS present then if documention for that model exists
2829        it will load to that  point otherwise again it will go to the top.
2830        For Wx2.8 and below is used (i.e. non-released through installer)
2831        a browser is loaded and the top of the model documentation only is
2832        accessible because webbrowser module does not pass anything after
2833        the # to the browser.
2834
2835        :param evt: on Help Button pressed event
2836        """
2837
2838        if self.model != None:
2839            name = self.formfactorbox.GetValue()
2840            _TreeLocation = 'user/models/'+ name.lower()+'.html'
2841            _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2842                                              "", name + " Help")
2843        else:
2844            _TreeLocation = 'user/index.html'
2845            _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2846                                              "", "General Model Help")
2847
2848
2849    def on_model_help_clicked(self, event):
2850        """
2851        Function called when 'Description' button is pressed next to model
2852        of interest.  This calls the Description embedded in the model. This
2853        should work with either Wx2.8 and lower or higher. If no model is
2854        selected it will give the message that a model must be chosen first
2855        in the box that would normally contain the description.  If a badly
2856        behaved model is encountered which has no description then it will
2857        give the message that none is available.
2858
2859        :param evt: on Description Button pressed event
2860        """
2861
2862        if self.model == None:
2863            name = 'index.html'
2864        else:
2865            name = self.formfactorbox.GetValue()
2866
2867        msg = 'Model description:\n'
2868        info = "Info"
2869        if self.model != None:
2870#                frame.Destroy()
2871            if str(self.model.description).rstrip().lstrip() == '':
2872                msg += "Sorry, no information is available for this model."
2873            else:
2874                msg += self.model.description + '\n'
2875            wx.MessageBox(msg, info)
2876        else:
2877            msg += "You must select a model to get information on this"
2878            wx.MessageBox(msg, info)
2879
2880    def _on_mag_angle_help(self, event):
2881        """
2882        Bring up Magnetic Angle definition bmp image whenever the ? button
2883        is clicked. Calls DocumentationWindow with the path of the location
2884        within the documentation tree (after /doc/ ....". When using old
2885        versions of Wx (i.e. before 2.9 and therefore not part of release
2886        versions distributed via installer) it brings up an image viewer
2887        box which allows the user to click through the rest of the images in
2888        the directory.  Not ideal but probably better than alternative which
2889        would bring up the entire discussion of how magnetic models work?
2890        Specially since it is not likely to be accessed.  The normal release
2891        versions bring up the normal image box.
2892
2893        :param evt: Triggers on clicking ? in Magnetic Angles? box
2894        """
2895
2896        _TreeLocation = "_images/M_angles_pic.bmp"
2897        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
2898                                          "Magnetic Angle Defintions")
2899
2900    def _on_mag_help(self, event):
2901        """
2902        Bring up Magnetic Angle definition bmp image whenever the ? button
2903        is clicked. Calls DocumentationWindow with the path of the location
2904        within the documentation tree (after /doc/ ....". When using old
2905        versions of Wx (i.e. before 2.9 and therefore not part of release
2906        versions distributed via installer) it brings up an image viewer
2907        box which allows the user to click through the rest of the images in
2908        the directory.  Not ideal but probably better than alternative which
2909        would bring up the entire discussion of how magnetic models work?
2910        Specially since it is not likely to be accessed.  The normal release
2911        versions bring up the normal image box.
2912
2913        :param evt: Triggers on clicking ? in Magnetic Angles? box
2914        """
2915
2916        _TreeLocation = "user/magnetism.html"
2917        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
2918                                          "Polarized Beam/Magnetc Help")
2919
2920    def _on_mag_on(self, event):
2921        """
2922        Magnetic Parameters ON/OFF
2923        """
2924        button = event.GetEventObject()
2925
2926        if button.GetLabel().count('ON') > 0:
2927            self.magnetic_on = True
2928            button.SetLabel("Magnetic OFF")
2929            m_value = 1.0e-06
2930            for key in self.model.magnetic_params:
2931                if key.count('M0') > 0:
2932                    self.model.setParam(key, m_value)
2933                    m_value += 0.5e-06
2934        else:
2935            self.magnetic_on = False
2936            button.SetLabel("Magnetic ON")
2937            for key in self.model.magnetic_params:
2938                if key.count('M0') > 0:
2939                    #reset mag value to zero fo safety
2940                    self.model.setParam(key, 0.0)
2941
2942        self.Show(False)
2943        self.set_model_param_sizer(self.model)
2944        #self._set_sizer_dispersion()
2945        self.state.magnetic_on = self.magnetic_on
2946        self.SetupScrolling()
2947        self.Show(True)
2948
2949    def on_pd_help_clicked(self, event):
2950        """
2951        Bring up Polydispersity Documentation whenever the ? button is clicked.
2952        Calls DocumentationWindow with the path of the location within the
2953        documentation tree (after /doc/ ....".  Note that when using old
2954        versions of Wx (before 2.9) and thus not the release version of
2955        istallers, the help comes up at the top level of the file as
2956        webbrowser does not pass anything past the # to the browser when it is
2957        running "file:///...."
2958
2959        :param evt: Triggers on clicking ? in polydispersity box
2960        """
2961
2962        _TreeLocation = "user/sasgui/perspectives/fitting/pd_help.html"
2963        _PageAnchor = ""
2964        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2965                                          _PageAnchor, "Polydispersity Help")
2966
2967    def on_left_down(self, event):
2968        """
2969        Get key stroke event
2970        """
2971        # Figuring out key combo: Cmd for copy, Alt for paste
2972        if event.CmdDown() and event.ShiftDown():
2973            self.get_paste()
2974        elif event.CmdDown():
2975            self.get_copy()
2976        else:
2977            event.Skip()
2978            return
2979        # make event free
2980        event.Skip()
2981
2982    def get_copy(self):
2983        """
2984        Get copy params to clipboard
2985        """
2986        content = self.get_copy_params()
2987        flag = self.set_clipboard(content)
2988        self._copy_info(flag)
2989        return flag
2990
2991    def get_copy_params(self):
2992        """
2993        Get the string copies of the param names and values in the tap
2994        """
2995        content = 'sasview_parameter_values:'
2996        # Do it if params exist
2997        if  self.parameters != []:
2998
2999            # go through the parameters
3000            strings = self._get_copy_helper(self.parameters,
3001                                           self.orientation_params)
3002            content += strings
3003
3004            # go through the fittables
3005            strings = self._get_copy_helper(self.fittable_param,
3006                                           self.orientation_params_disp)
3007            content += strings
3008
3009            # go through the fixed params
3010            strings = self._get_copy_helper(self.fixed_param,
3011                                           self.orientation_params_disp)
3012            content += strings
3013
3014            # go through the str params
3015            strings = self._get_copy_helper(self.str_parameters,
3016                                           self.orientation_params)
3017            content += strings
3018            return content
3019        else:
3020            return False
3021
3022    def get_copy_excel(self):
3023        """
3024        Get copy params to clipboard
3025        """
3026        content = self.get_copy_params_excel()
3027        flag = self.set_clipboard(content)
3028        self._copy_info(flag)
3029        return flag
3030
3031    def get_copy_params_excel(self):
3032        """
3033        Get the string copies of the param names and values in the tap
3034        """
3035        content = ''
3036
3037        crlf = chr(13) + chr(10)
3038        tab = chr(9)
3039
3040        # Do it if params exist
3041        if  self.parameters != []:
3042
3043            for param in self.parameters:
3044                content += param[1] #parameter name
3045                content += tab
3046                content += param[1] + "_err"
3047                content += tab
3048
3049            content += crlf
3050
3051            #row of values and errors...
3052            for param in self.parameters:
3053                content += param[2].GetValue() #value
3054                content += tab
3055                content += param[4].GetValue() #error
3056                content += tab
3057
3058            return content
3059        else:
3060            return False
3061
3062
3063    def get_copy_latex(self):
3064        """
3065        Get copy params to clipboard
3066        """
3067        content = self.get_copy_params_latex()
3068        flag = self.set_clipboard(content)
3069        self._copy_info(flag)
3070        return flag
3071
3072    def get_copy_params_latex(self):
3073        """
3074        Get the string copies of the param names and values in the tap
3075        """
3076        content = '\\begin{table}'
3077        content += '\\begin{tabular}[h]'
3078
3079        crlf = chr(13) + chr(10)
3080        tab = chr(9)
3081
3082        # Do it if params exist
3083        if  self.parameters != []:
3084
3085            content += '{|'
3086            for param in self.parameters:
3087                content += 'l|l|'
3088            content += '}\hline'
3089            content += crlf
3090
3091            for index, param in enumerate(self.parameters):
3092                content += param[1].replace('_', '\_') #parameter name
3093                content += ' & '
3094                content += param[1].replace('_', '\_') + "\_err"
3095                if index < len(self.parameters) - 1:
3096                    content += ' & '
3097            content += '\\\\ \\hline'
3098            content += crlf
3099
3100            #row of values and errors...
3101            for index, param in enumerate(self.parameters):
3102                content += param[2].GetValue() #parameter value
3103                content += ' & '
3104                content += param[4].GetValue() #parameter error
3105                if index < len(self.parameters) - 1:
3106                    content += ' & '
3107            content += '\\\\ \\hline'
3108            content += crlf
3109
3110            content += '\\end{tabular}'
3111            content += '\\end{table}'
3112            return content
3113        else:
3114            return False
3115
3116
3117    def set_clipboard(self, content=None):
3118        """
3119        Put the string to the clipboard
3120        """
3121        if not content:
3122            return False
3123        if wx.TheClipboard.Open():
3124            wx.TheClipboard.SetData(wx.TextDataObject(str(content)))
3125            wx.TheClipboard.Close()
3126            return True
3127        return None
3128
3129    def _get_copy_helper(self, param, orient_param):
3130        """
3131        Helping get value and name of the params
3132
3133        : param param:  parameters
3134        : param orient_param = oritational params
3135        : return content: strings [list] [name,value:....]
3136        """
3137        content = ''
3138        # go through the str params
3139        for item in param:
3140            # copy only the params shown
3141            if not item[2].IsShown():
3142                continue
3143            disfunc = ''
3144            try:
3145                if item[7].__class__.__name__ == 'ComboBox':
3146                    disfunc = str(item[7].GetValue())
3147            except Exception:
3148                logging.error(traceback.format_exc())
3149
3150            # 2D
3151            if self.data.__class__.__name__ == "Data2D":
3152                try:
3153                    check = item[0].GetValue()
3154                except Exception:
3155                    check = None
3156                name = item[1]
3157                value = item[2].GetValue()
3158            # 1D
3159            else:
3160                ## for 1D all parameters except orientation
3161                if not item[1] in orient_param:
3162                    try:
3163                        check = item[0].GetValue()
3164                    except:
3165                        check = None
3166                    name = item[1]
3167                    value = item[2].GetValue()
3168
3169            # add to the content
3170            if disfunc != '':
3171
3172                disfunc = ',' + disfunc
3173            # Need to support array func for copy/paste
3174            try:
3175                if disfunc.count('array') > 0:
3176                    disfunc += ','
3177                    for val in self.values[name]:
3178                        disfunc += ' ' + str(val)
3179                    disfunc += ','
3180                    for weight in self.weights[name]:
3181                        disfunc += ' ' + str(weight)
3182            except Exception:
3183                logging.error(traceback.format_exc())
3184            content += name + ',' + str(check) + ',' + value + disfunc + ':'
3185
3186        return content
3187
3188    def get_clipboard(self):
3189        """
3190        Get strings in the clipboard
3191        """
3192        text = ""
3193        # Get text from the clip board
3194        if wx.TheClipboard.Open():
3195            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
3196                data = wx.TextDataObject()
3197                # get wx dataobject
3198                success = wx.TheClipboard.GetData(data)
3199                # get text
3200                if success:
3201                    text = data.GetText()
3202                else:
3203                    text = ''
3204            # close clipboard
3205            wx.TheClipboard.Close()
3206        return text
3207
3208    def get_paste(self):
3209        """
3210        Paste params from the clipboard
3211        """
3212        text = self.get_clipboard()
3213        flag = self.get_paste_params(text)
3214        self._copy_info(flag)
3215        return flag
3216
3217    def get_paste_params(self, text=''):
3218        """
3219        Get the string copies of the param names and values in the tap
3220        """
3221        context = {}
3222        # put the text into dictionary
3223        lines = text.split(':')
3224        if lines[0] != 'sasview_parameter_values':
3225            self._copy_info(False)
3226            return False
3227        for line in lines[1:-1]:
3228            if len(line) != 0:
3229                item = line.split(',')
3230                check = item[1]
3231                name = item[0]
3232                value = item[2]
3233                # Transfer the text to content[dictionary]
3234                context[name] = [check, value]
3235            # ToDo: PlugIn this poly disp function for pasting
3236            try:
3237                poly_func = item[3]
3238                context[name].append(poly_func)
3239                try:
3240                    # take the vals and weights for  array
3241                    array_values = item[4].split(' ')
3242                    array_weights = item[5].split(' ')
3243                    val = [float(a_val) for a_val in array_values[1:]]
3244                    weit = [float(a_weit) for a_weit in array_weights[1:]]
3245
3246                    context[name].append(val)
3247                    context[name].append(weit)
3248                except:
3249                    raise
3250            except:
3251                poly_func = ''
3252                context[name].append(poly_func)
3253
3254        # Do it if params exist
3255        if  self.parameters != []:
3256            # go through the parameters
3257            self._get_paste_helper(self.parameters,
3258                                   self.orientation_params, context)
3259
3260            # go through the fittables
3261            self._get_paste_helper(self.fittable_param,
3262                                   self.orientation_params_disp,
3263                                   context)
3264
3265            # go through the fixed params
3266            self._get_paste_helper(self.fixed_param,
3267                                   self.orientation_params_disp, context)
3268
3269            # go through the str params
3270            self._get_paste_helper(self.str_parameters,
3271                                   self.orientation_params, context)
3272
3273            return True
3274        return None
3275
3276    def _get_paste_helper(self, param, orient_param, content):
3277        """
3278        Helping set values of the params
3279
3280        : param param:  parameters
3281        : param orient_param: oritational params
3282        : param content: dictionary [ name, value: name1.value1,...]
3283        """
3284        # go through the str params
3285        for item in param:
3286            # 2D
3287            if self.data.__class__.__name__ == "Data2D":
3288                name = item[1]
3289                if name in content.keys():
3290                    check = content[name][0]
3291                    pd = content[name][1]
3292                    if name.count('.') > 0:
3293                        # If this is parameter.width, then pd may be a floating
3294                        # point value or it may be an array distribution.
3295                        # Nothing to do for parameter.npts or parameter.nsigmas.
3296                        try:
3297                            float(pd)
3298                            if name.endswith('.npts'):
3299                                pd = int(pd)
3300                        except Exception:
3301                            #continue
3302                            if not pd and pd != '':
3303                                continue
3304                    item[2].SetValue(str(pd))
3305                    if item in self.fixed_param and pd == '':
3306                        # Only array func has pd == '' case.
3307                        item[2].Enable(False)
3308                    else:
3309                        item[2].Enable(True)
3310                    if item[2].__class__.__name__ == "ComboBox":
3311                        if content[name][1] in self.model.fun_list:
3312                            fun_val = self.model.fun_list[content[name][1]]
3313                            self.model.setParam(name, fun_val)
3314
3315                    value = content[name][1:]
3316                    self._paste_poly_help(item, value)
3317                    if check == 'True':
3318                        is_true = True
3319                    elif check == 'False':
3320                        is_true = False
3321                    else:
3322                        is_true = None
3323                    if is_true != None:
3324                        item[0].SetValue(is_true)
3325            # 1D
3326            else:
3327                ## for 1D all parameters except orientation
3328                if not item[1] in orient_param:
3329                    name = item[1]
3330                    if name in content.keys():
3331                        check = content[name][0]
3332                        # Avoid changing combox content
3333                        value = content[name][1:]
3334                        pd = value[0]
3335                        if name.count('.') > 0:
3336                            # If this is parameter.width, then pd may be a floating
3337                            # point value or it may be an array distribution.
3338                            # Nothing to do for parameter.npts or parameter.nsigmas.
3339                            try:
3340                                pd = float(pd)
3341                                if name.endswith('.npts'):
3342                                    pd = int(pd)
3343                            except:
3344                                #continue
3345                                if not pd and pd != '':
3346                                    continue
3347                        item[2].SetValue(str(pd))
3348                        if item in self.fixed_param and pd == '':
3349                            # Only array func has pd == '' case.
3350                            item[2].Enable(False)
3351                        else:
3352                            item[2].Enable(True)
3353                        if item[2].__class__.__name__ == "ComboBox":
3354                            if value[0] in self.model.fun_list:
3355                                fun_val = self.model.fun_list[value[0]]
3356                                self.model.setParam(name, fun_val)
3357                                # save state
3358                        self._paste_poly_help(item, value)
3359                        if check == 'True':
3360                            is_true = True
3361                        elif check == 'False':
3362                            is_true = False
3363                        else:
3364                            is_true = None
3365                        if is_true != None:
3366                            item[0].SetValue(is_true)
3367
3368    def _paste_poly_help(self, item, value):
3369        """
3370        Helps get paste for poly function
3371
3372        *item* is the parameter name
3373
3374        *value* depends on which parameter is being processed, and whether it
3375        has array polydispersity.
3376
3377        For parameters without array polydispersity:
3378
3379            parameter => ['FLOAT', '']
3380            parameter.width => ['FLOAT', 'DISTRIBUTION', '']
3381            parameter.npts => ['FLOAT', '']
3382            parameter.nsigmas => ['FLOAT', '']
3383
3384        For parameters with array polydispersity:
3385
3386            parameter => ['FLOAT', '']
3387            parameter.width => ['FILENAME', 'array', [x1, ...], [w1, ...]]
3388            parameter.npts => ['FLOAT', '']
3389            parameter.nsigmas => ['FLOAT', '']
3390        """
3391        # Do nothing if not setting polydispersity
3392        if len(value[1]) == 0:
3393            return
3394
3395        try:
3396            name = item[7].Name
3397            param_name = name.split('.')[0]
3398            item[7].SetValue(value[1])
3399            selection = item[7].GetCurrentSelection()
3400            dispersity = item[7].GetClientData(selection)
3401            disp_model = dispersity()
3402
3403            if value[1] == 'array':
3404                pd_vals = numpy.array(value[2])
3405                pd_weights = numpy.array(value[3])
3406                if len(pd_vals) == 0 or len(pd_vals) != len(pd_weights):
3407                    msg = ("bad array distribution parameters for %s"
3408                           % param_name)
3409                    raise ValueError(msg)
3410                self._set_disp_cb(True, item=item)
3411                self._set_array_disp_model(name=name,
3412                                           disp=disp_model,
3413                                           values=pd_vals,
3414                                           weights=pd_weights)
3415            else:
3416                self._set_disp_cb(False, item=item)
3417                self._disp_obj_dict[name] = disp_model
3418                self.model.set_dispersion(param_name, disp_model)
3419                self.state._disp_obj_dict[name] = disp_model.type
3420                # TODO: It's not an array, why update values and weights?
3421                self.model._persistency_dict[param_name] = \
3422                    [self.values, self.weights]
3423                self.state.values = self.values
3424                self.state.weights = self.weights
3425
3426        except Exception:
3427            logging.error(traceback.format_exc())
3428            print "Error in BasePage._paste_poly_help: %s" % \
3429                                    sys.exc_info()[1]
3430
3431    def _set_disp_cb(self, isarray, item):
3432        """
3433        Set cb for array disp
3434        """
3435        if isarray:
3436            item[0].SetValue(False)
3437            item[0].Enable(False)
3438            item[2].Enable(False)
3439            item[3].Show(False)
3440            item[4].Show(False)
3441            item[5].SetValue('')
3442            item[5].Enable(False)
3443            item[6].SetValue('')
3444            item[6].Enable(False)
3445        else:
3446            item[0].Enable()
3447            item[2].Enable()
3448            item[3].Show(True)
3449            item[4].Show(True)
3450            item[5].Enable()
3451            item[6].Enable()
3452
3453    def update_pinhole_smear(self):
3454        """
3455            Method to be called by sub-classes
3456            Moveit; This method doesn't belong here
3457        """
3458        print "BasicPage.update_pinhole_smear was called: skipping"
3459        return
3460
3461    def _read_category_info(self):
3462        """
3463        Reads the categories in from file
3464        """
3465        # # ILL mod starts here - July 2012 kieranrcampbell@gmail.com
3466        self.master_category_dict = defaultdict(list)
3467        self.by_model_dict = defaultdict(list)
3468        self.model_enabled_dict = defaultdict(bool)
3469
3470        try:
3471            categorization_file = CategoryInstaller.get_user_file()
3472            if not os.path.isfile(categorization_file):
3473                categorization_file = CategoryInstaller.get_default_file()
3474            cat_file = open(categorization_file, 'rb')
3475            self.master_category_dict = json.load(cat_file)
3476            self._regenerate_model_dict()
3477            cat_file.close()
3478        except IOError:
3479            raise
3480            print 'Problem reading in category file.'
3481            print 'We even looked for it, made sure it was there.'
3482            print 'An existential crisis if there ever was one.'
3483
3484    def _regenerate_model_dict(self):
3485        """
3486        regenerates self.by_model_dict which has each model name as the
3487        key and the list of categories belonging to that model
3488        along with the enabled mapping
3489        """
3490        self.by_model_dict = defaultdict(list)
3491        for category in self.master_category_dict:
3492            for (model, enabled) in self.master_category_dict[category]:
3493                self.by_model_dict[model].append(category)
3494                self.model_enabled_dict[model] = enabled
3495
3496    def _populate_listbox(self):
3497        """
3498        fills out the category list box
3499        """
3500        uncat_str = 'Customized Models'
3501        self._read_category_info()
3502
3503        self.categorybox.Clear()
3504        cat_list = sorted(self.master_category_dict.keys())
3505        if not uncat_str in cat_list:
3506            cat_list.append(uncat_str)
3507
3508        for category in cat_list:
3509            if category != '':
3510                self.categorybox.Append(category)
3511
3512        if self.categorybox.GetSelection() == wx.NOT_FOUND:
3513            self.categorybox.SetSelection(0)
3514        else:
3515            self.categorybox.SetSelection(\
3516                self.categorybox.GetSelection())
3517        #self._on_change_cat(None)
3518
3519    def _on_change_cat(self, event):
3520        """
3521        Callback for category change action
3522        """
3523        self.model_name = None
3524        category = self.categorybox.GetStringSelection()
3525        if category == None:
3526            return
3527        self.model_box.Clear()
3528
3529        if category == 'Customized Models':
3530            for model in self.model_list_box[category]:
3531                str_m = str(model).split(".")[0]
3532                self.model_box.Append(str_m)
3533
3534        else:
3535            for (model, enabled) in sorted(self.master_category_dict[category],
3536                                      key=lambda name: name[0]):
3537                if(enabled):
3538                    self.model_box.Append(model)
3539
3540    def _fill_model_sizer(self, sizer):
3541        """
3542        fill sizer containing model info
3543        """
3544        # This should only be called once per fit tab
3545        #print "==== Entering _fill_model_sizer"
3546        ##Add model function Details button in fitpanel.
3547        ##The following 3 lines are for Mac. Let JHC know before modifying...
3548        title = "Model"
3549        self.formfactorbox = None
3550        self.multifactorbox = None
3551        self.mbox_description = wx.StaticBox(self, wx.ID_ANY, str(title))
3552        boxsizer1 = wx.StaticBoxSizer(self.mbox_description, wx.VERTICAL)
3553        sizer_cat = wx.BoxSizer(wx.HORIZONTAL)
3554        self.mbox_description.SetForegroundColour(wx.RED)
3555        wx_id = self._ids.next()
3556        self.model_func = wx.Button(self, wx_id, 'Help', size=(80, 23))
3557        self.model_func.Bind(wx.EVT_BUTTON, self.on_function_help_clicked,
3558                             id=wx_id)
3559        self.model_func.SetToolTipString("Full Model Function Help")
3560        wx_id = self._ids.next()
3561        self.model_help = wx.Button(self, wx_id, 'Description', size=(80, 23))
3562        self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked,
3563                             id=wx_id)
3564        self.model_help.SetToolTipString("Short Model Function Description")
3565        wx_id = self._ids.next()
3566        self.model_view = wx.Button(self, wx_id, "Show 2D", size=(80, 23))
3567        self.model_view.Bind(wx.EVT_BUTTON, self._onModel2D, id=wx_id)
3568        hint = "toggle view of model from 1D to 2D  or 2D to 1D"
3569        self.model_view.SetToolTipString(hint)
3570
3571        cat_set_box = wx.StaticBox(self, wx.ID_ANY, 'Category')
3572        sizer_cat_box = wx.StaticBoxSizer(cat_set_box, wx.HORIZONTAL)
3573        sizer_cat_box.SetMinSize((200, 50))
3574        self.categorybox = wx.ComboBox(self, wx.ID_ANY,
3575                                       style=wx.CB_READONLY)
3576        self.categorybox.SetToolTip(wx.ToolTip("Select a Category/Type"))
3577        self._populate_listbox()
3578        wx.EVT_COMBOBOX(self.categorybox, wx.ID_ANY, self._show_combox)
3579        #self.shape_rbutton = wx.RadioButton(self, wx.ID_ANY, 'Shapes',
3580        #                                     style=wx.RB_GROUP)
3581        #self.shape_indep_rbutton = wx.RadioButton(self, wx.ID_ANY,
3582        #                                          "Shape-Independent")
3583        #self.struct_rbutton = wx.RadioButton(self, wx.ID_ANY,
3584        #                                     "Structure Factor ")
3585        #self.plugin_rbutton = wx.RadioButton(self, wx.ID_ANY,
3586        #                                     "Uncategorized")
3587
3588        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3589        #                   id=self.shape_rbutton.GetId())
3590        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3591        #                    id=self.shape_indep_rbutton.GetId())
3592        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3593        #                    id=self.struct_rbutton.GetId())
3594        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3595        #                    id=self.plugin_rbutton.GetId())
3596        #MAC needs SetValue
3597
3598        show_cat_button = wx.Button(self, wx.ID_ANY, "Modify")
3599        cat_tip = "Modify model categories \n"
3600        cat_tip += "(also accessible from the menu bar)."
3601        show_cat_button.SetToolTip( wx.ToolTip(cat_tip) )
3602        show_cat_button.Bind(wx.EVT_BUTTON, self._on_modify_cat)
3603        sizer_cat_box.Add(self.categorybox, 1, wx.RIGHT, 3)
3604        sizer_cat_box.Add((10,10))
3605        sizer_cat_box.Add(show_cat_button)
3606        #self.shape_rbutton.SetValue(True)
3607
3608        sizer_radiobutton = wx.GridSizer(2, 2, 5, 5)
3609        #sizer_radiobutton.Add(self.shape_rbutton)
3610        #sizer_radiobutton.Add(self.shape_indep_rbutton)
3611        sizer_radiobutton.Add((5,5))
3612        sizer_radiobutton.Add(self.model_view, 1, wx.RIGHT, 5)
3613        #sizer_radiobutton.Add(self.plugin_rbutton)
3614        #sizer_radiobutton.Add(self.struct_rbutton)
3615#        sizer_radiobutton.Add((5,5))
3616        sizer_radiobutton.Add(self.model_help, 1, wx.RIGHT | wx.LEFT, 5)
3617#        sizer_radiobutton.Add((5,5))
3618        sizer_radiobutton.Add(self.model_func, 1, wx.RIGHT, 5)
3619        sizer_cat.Add(sizer_cat_box, 1, wx.LEFT, 2.5)
3620        sizer_cat.Add(sizer_radiobutton)
3621        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
3622        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
3623
3624        self.text1 = wx.StaticText(self, wx.ID_ANY, "")
3625        self.text2 = wx.StaticText(self, wx.ID_ANY, "P(Q)*S(Q)")
3626        self.mutifactor_text = wx.StaticText(self, wx.ID_ANY, "No. of Shells: ")
3627        self.mutifactor_text1 = wx.StaticText(self, wx.ID_ANY, "")
3628        self.show_sld_button = wx.Button(self, wx.ID_ANY, "Show SLD Profile")
3629        self.show_sld_button.Bind(wx.EVT_BUTTON, self._on_show_sld)
3630
3631        self.formfactorbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3632        self.formfactorbox.SetToolTip(wx.ToolTip("Select a Model"))
3633        if self.model != None:
3634            self.formfactorbox.SetValue(self.model.name)
3635        self.structurebox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3636        self.multifactorbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3637        self.initialize_combox()
3638        wx.EVT_COMBOBOX(self.formfactorbox, wx.ID_ANY, self._on_select_model)
3639
3640        wx.EVT_COMBOBOX(self.structurebox, wx.ID_ANY, self._on_select_model)
3641        wx.EVT_COMBOBOX(self.multifactorbox, wx.ID_ANY, self._on_select_model)
3642        ## check model type to show sizer
3643        if self.model != None:
3644            print "_set_model_sizer_selection: disabled."
3645            #self._set_model_sizer_selection(self.model)
3646
3647        sizer_selection.Add(self.text1)
3648        sizer_selection.Add((10, 5))
3649        sizer_selection.Add(self.formfactorbox)
3650        sizer_selection.Add((5, 5))
3651        sizer_selection.Add(self.text2)
3652        sizer_selection.Add((5, 5))
3653        sizer_selection.Add(self.structurebox)
3654
3655        mutifactor_selection.Add((13, 5))
3656        mutifactor_selection.Add(self.mutifactor_text)
3657        mutifactor_selection.Add(self.multifactorbox)
3658        mutifactor_selection.Add((5, 5))
3659        mutifactor_selection.Add(self.mutifactor_text1)
3660        mutifactor_selection.Add((10, 5))
3661        mutifactor_selection.Add(self.show_sld_button)
3662
3663        boxsizer1.Add(sizer_cat)
3664        boxsizer1.Add((10, 10))
3665        boxsizer1.Add(sizer_selection)
3666        boxsizer1.Add((10, 10))
3667        boxsizer1.Add(mutifactor_selection)
3668
3669        self._set_multfactor_combobox()
3670        self.multifactorbox.SetSelection(1)
3671        self.show_sld_button.Hide()
3672        sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3673        sizer.Layout()
3674
3675    def on_smear_helper(self, update=False):
3676        """
3677        Help for onSmear if implemented
3678
3679        :param update: force or not to update
3680        """
3681    def reset_page(self, state, first=False):
3682        """
3683        reset the state  if implemented
3684        """
3685    def onSmear(self, event):
3686        """
3687        Create a smear object if implemented
3688        """
3689    def onPinholeSmear(self, event):
3690        """
3691        Create a custom pinhole smear object if implemented
3692        """
3693    def onSlitSmear(self, event):
3694        """
3695        Create a custom slit smear object if implemented
3696        """
3697    def update_slit_smear(self):
3698        """
3699        called by kill_focus on pinhole TextCntrl
3700        to update the changes if implemented
3701        """
3702    def select_param(self, event):
3703        """
3704        Select TextCtrl  checked if implemented
3705        """
3706    def set_data(self, data=None):
3707        """
3708        Sets data if implemented
3709        """
3710    def _is_2D(self):
3711        """
3712        Check if data_name is Data2D if implemented
3713        """
3714    def _on_select_model(self, event=None):
3715        """
3716        call back for model selection if implemented
3717        """
3718    def select_all_param(self, event):
3719        """
3720        set to true or false all checkBox if implemented
3721        """
3722    def get_weight_flag(self):
3723        """
3724        Get flag corresponding to a given weighting dI data if implemented
3725        """
3726    def _set_sizer_dispersion(self):
3727        """
3728        draw sizer for dispersity if implemented
3729        """
3730    def get_all_checked_params(self):
3731        """
3732        Found all parameters current check and add them to list of parameters
3733        to fit if implemented
3734        """
3735    def show_npts2fit(self):
3736        """
3737        setValue Npts for fitting if implemented
3738        """
3739    def _onModel2D(self, event):
3740        """
3741        toggle view of model from 1D to 2D  or 2D from 1D if implemented
3742        """
3743
3744class ModelTextCtrl(wx.TextCtrl):
3745    """
3746    Text control for model and fit parameters.
3747    Binds the appropriate events for user interactions.
3748    Default callback methods can be overwritten on initialization
3749
3750    :param kill_focus_callback: callback method for EVT_KILL_FOCUS event
3751    :param set_focus_callback:  callback method for EVT_SET_FOCUS event
3752    :param mouse_up_callback:   callback method for EVT_LEFT_UP event
3753    :param text_enter_callback: callback method for EVT_TEXT_ENTER event
3754
3755    """
3756    ## Set to True when the mouse is clicked while whole string is selected
3757    full_selection = False
3758    ## Call back for EVT_SET_FOCUS events
3759    _on_set_focus_callback = None
3760
3761    def __init__(self, parent, id=-1,
3762                 value=wx.EmptyString,
3763                 pos=wx.DefaultPosition,
3764                 size=wx.DefaultSize,
3765                 style=0,
3766                 validator=wx.DefaultValidator,
3767                 name=wx.TextCtrlNameStr,
3768                 kill_focus_callback=None,
3769                 set_focus_callback=None,
3770                 mouse_up_callback=None,
3771                 text_enter_callback=None):
3772
3773        wx.TextCtrl.__init__(self, parent, id, value, pos,
3774                             size, style, validator, name)
3775
3776        # Bind appropriate events
3777        self._on_set_focus_callback = parent.onSetFocus \
3778            if set_focus_callback is None else set_focus_callback
3779        self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
3780        self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus \
3781            if kill_focus_callback is None else kill_focus_callback)
3782        self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter \
3783            if text_enter_callback is None else text_enter_callback)
3784        if not ON_MAC:
3785            self.Bind(wx.EVT_LEFT_UP, self._highlight_text \
3786                if mouse_up_callback is None else mouse_up_callback)
3787
3788    def _on_set_focus(self, event):
3789        """
3790        Catch when the text control is set in focus to highlight the whole
3791        text if necessary
3792
3793        :param event: mouse event
3794
3795        """
3796        event.Skip()
3797        self.full_selection = True
3798        return self._on_set_focus_callback(event)
3799
3800    def _highlight_text(self, event):
3801        """
3802        Highlight text of a TextCtrl only of no text has be selected
3803
3804        :param event: mouse event
3805
3806        """
3807        # Make sure the mouse event is available to other listeners
3808        event.Skip()
3809        control = event.GetEventObject()
3810        if self.full_selection:
3811            self.full_selection = False
3812            # Check that we have a TextCtrl
3813            if issubclass(control.__class__, wx.TextCtrl):
3814                # Check whether text has been selected,
3815                # if not, select the whole string
3816                (start, end) = control.GetSelection()
3817                if start == end:
3818                    control.SetSelection(-1, -1)
3819
3820    def _silent_kill_focus(self, event):
3821        """
3822        Save the state of the page
3823        """
3824
3825        event.Skip()
3826        #pass
Note: See TracBrowser for help on using the repository browser.