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

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 8dec7e7 was 9c0f3c17, checked in by Ricardo Ferraz Leal <ricleal@…>, 8 years ago

After merge conflict

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