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

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

Merge branch 'ajj_sesans' into Jurrian1D

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