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

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 82b0b05e was 4109bd5, checked in by mathieu, 8 years ago

Fix issue with old custom models

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