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

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

Replaced fitpage to include transform box

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