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

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

fix 'invalid Q range' when parameter value is min or max. Fixes #636

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