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

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 47f2b5d was 47f2b5d, checked in by wojciech, 7 years ago

Skipping extra Iq evaluation by not calling draw_model function from set polydisperisty parameters routine, ref #624 and #576

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