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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 8359bbf7 was ddfe3f17, checked in by Piotr Rozyczko <rozyczko@…>, 8 years ago

support old style custom formula models (but not sum or product)

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