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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since d2143ab was bac3988, checked in by ajj, 9 years ago

Fixing category lists and list of models in GUI

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