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

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 3bec50a was 3bec50a, checked in by ajj, 8 years ago

Really fixes #753 (proper call to wx.CallAfter? that works outside if a debugger)

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