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

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

use product model from sasmodels rather than sasview. Fixes #548, #760

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