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

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 9087214 was 9087214, checked in by jhbakker, 8 years ago

Merge branch 'master' into Jurrian1D, fingers crossed!

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