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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since b76e65a was b76e65a, checked in by lewis, 7 years ago

Refactor 'Copy to Excel/LaTeX' logic

The error on fitted polydispersity parameters will now be exported
correctly, and the code that writes the values to the clipboard is
a lot easier to understand.

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