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

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 ad476d1 was f80b416e, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

Merge branch 'master' into ticket-639-katex

  • 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        # Sort the models
1844        mlist_sorted = sorted(mlist)
1845        for item in mlist_sorted:
1846            combobox.Append(item[0], item[1])
1847        return 0
1848
1849    def _onQrangeEnter(self, event):
1850        """
1851        Check validity of value enter in the Q range field
1852
1853        """
1854        tcrtl = event.GetEventObject()
1855        # Clear msg if previously shown.
1856        msg = ""
1857        wx.PostEvent(self.parent, StatusEvent(status=msg))
1858        # Flag to register when a parameter has changed.
1859        if tcrtl.GetValue().lstrip().rstrip() != "":
1860            try:
1861                float(tcrtl.GetValue())
1862                tcrtl.SetBackgroundColour(wx.WHITE)
1863                # If qmin and qmax have been modified, update qmin and qmax
1864                if self._validate_qrange(self.qmin, self.qmax):
1865                    tempmin = float(self.qmin.GetValue())
1866                    if tempmin != self.qmin_x:
1867                        self.qmin_x = tempmin
1868                    tempmax = float(self.qmax.GetValue())
1869                    if tempmax != self.qmax_x:
1870                        self.qmax_x = tempmax
1871                else:
1872                    tcrtl.SetBackgroundColour("pink")
1873                    msg = "Model Error: wrong value entered: %s" % \
1874                          sys.exc_info()[1]
1875                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1876                    return
1877            except:
1878                tcrtl.SetBackgroundColour("pink")
1879                msg = "Model Error: wrong value entered: %s" % sys.exc_info()[1]
1880                wx.PostEvent(self.parent, StatusEvent(status=msg))
1881                return
1882            # Check if # of points for theory model are valid(>0).
1883            if self.npts is not None:
1884                if check_float(self.npts):
1885                    temp_npts = float(self.npts.GetValue())
1886                    if temp_npts != self.num_points:
1887                        self.num_points = temp_npts
1888                else:
1889                    msg = "Cannot plot: No points in Q range!!!  "
1890                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1891        else:
1892            tcrtl.SetBackgroundColour("pink")
1893            msg = "Model Error: wrong value entered!!!"
1894            wx.PostEvent(self.parent, StatusEvent(status=msg))
1895        self.save_current_state()
1896        event = PageInfoEvent(page=self)
1897        wx.PostEvent(self.parent, event)
1898        self.state_change = False
1899        # Draw the model for a different range
1900        if not self.data.is_data:
1901            self.create_default_data()
1902        self._draw_model()
1903
1904    def _theory_qrange_enter(self, event):
1905        """
1906        Check validity of value enter in the Q range field
1907        """
1908
1909        tcrtl = event.GetEventObject()
1910        # Clear msg if previously shown.
1911        msg = ""
1912        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1913        # Flag to register when a parameter has changed.
1914        is_modified = False
1915        if tcrtl.GetValue().lstrip().rstrip() != "":
1916            try:
1917                value = float(tcrtl.GetValue())
1918                tcrtl.SetBackgroundColour(wx.WHITE)
1919
1920                # If qmin and qmax have been modified, update qmin and qmax
1921                if self._validate_qrange(self.theory_qmin, self.theory_qmax):
1922                    tempmin = float(self.theory_qmin.GetValue())
1923                    if tempmin != self.theory_qmin_x:
1924                        self.theory_qmin_x = tempmin
1925                    tempmax = float(self.theory_qmax.GetValue())
1926                    if tempmax != self.qmax_x:
1927                        self.theory_qmax_x = tempmax
1928                else:
1929                    tcrtl.SetBackgroundColour("pink")
1930                    msg = "Model Error: wrong value entered: %s" % \
1931                          sys.exc_info()[1]
1932                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1933                    return
1934            except:
1935                tcrtl.SetBackgroundColour("pink")
1936                msg = "Model Error: wrong value entered: %s" % sys.exc_info()[1]
1937                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1938                return
1939            # Check if # of points for theory model are valid(>0).
1940            if self.Npts_total.IsEditable():
1941                if check_float(self.Npts_total):
1942                    temp_npts = float(self.Npts_total.GetValue())
1943                    if temp_npts != self.num_points:
1944                        self.num_points = temp_npts
1945                        is_modified = True
1946                else:
1947                    msg = "Cannot Plot: No points in Q range!!!  "
1948                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1949        else:
1950            tcrtl.SetBackgroundColour("pink")
1951            msg = "Model Error: wrong value entered!!!"
1952            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
1953        self.save_current_state()
1954        event = PageInfoEvent(page=self)
1955        wx.PostEvent(self.parent, event)
1956        self.state_change = False
1957        # Draw the model for a different range
1958        self.create_default_data()
1959        self._draw_model()
1960
1961    def _on_select_model_helper(self):
1962        """
1963        call back for model selection
1964        """
1965        # reset dictionary containing reference to dispersion
1966        self._disp_obj_dict = {}
1967        self.disp_cb_dict = {}
1968        self.temp_multi_functional = False
1969        f_id = self.formfactorbox.GetCurrentSelection()
1970        # For MAC
1971        form_factor = None
1972        if f_id >= 0:
1973            form_factor = self.formfactorbox.GetClientData(f_id)
1974
1975        if form_factor is None or \
1976            not hasattr(form_factor, 'is_form_factor') or \
1977                not form_factor.is_form_factor:
1978            self.structurebox.Hide()
1979            self.text2.Hide()
1980            self.structurebox.Disable()
1981            self.structurebox.SetSelection(0)
1982            self.text2.Disable()
1983        else:
1984            self.structurebox.Show()
1985            self.text2.Show()
1986            self.structurebox.Enable()
1987            self.text2.Enable()
1988
1989        if form_factor is not None:
1990            # set multifactor for Mutifunctional models
1991            if form_factor.is_multiplicity_model:
1992                m_id = self.multifactorbox.GetCurrentSelection()
1993                multiplicity = form_factor.multiplicity_info[0]
1994                self.multifactorbox.Clear()
1995                self._set_multfactor_combobox(multiplicity)
1996                self._show_multfactor_combobox()
1997                # ToDo: this info should be called directly from the model
1998                text = form_factor.multiplicity_info[1]  # 'No. of Shells: '
1999
2000                self.mutifactor_text.SetLabel(text)
2001                if m_id > multiplicity - 1:
2002                    # default value
2003                    m_id = 1
2004
2005                self.multi_factor = self.multifactorbox.GetClientData(m_id)
2006                if self.multi_factor is None:
2007                    self.multi_factor = 0
2008                self.multifactorbox.SetSelection(m_id)
2009                # Check len of the text1 and max_multiplicity
2010                text = ''
2011                if form_factor.multiplicity_info[0] == \
2012                        len(form_factor.multiplicity_info[2]):
2013                    text = form_factor.multiplicity_info[2][self.multi_factor]
2014                self.mutifactor_text1.SetLabel(text)
2015                # Check if model has  get sld profile.
2016                if len(form_factor.multiplicity_info[3]) > 0:
2017                    self.sld_axes = form_factor.multiplicity_info[3]
2018                    self.show_sld_button.Show(True)
2019                else:
2020                    self.sld_axes = ""
2021            else:
2022                self._hide_multfactor_combobox()
2023                self.show_sld_button.Hide()
2024                self.multi_factor = None
2025        else:
2026            self._hide_multfactor_combobox()
2027            self.show_sld_button.Hide()
2028            self.multi_factor = None
2029
2030        s_id = self.structurebox.GetCurrentSelection()
2031        struct_factor = self.structurebox.GetClientData(s_id)
2032
2033        if struct_factor is not None:
2034            from sasmodels.sasview_model import MultiplicationModel
2035            self.model = MultiplicationModel(form_factor(self.multi_factor),
2036                                             struct_factor())
2037            # multifunctional form factor
2038            if len(form_factor.non_fittable) > 0:
2039                self.temp_multi_functional = True
2040        elif form_factor is not None:
2041            if self.multi_factor is not None:
2042                self.model = form_factor(self.multi_factor)
2043            else:
2044                # old style plugin models do not accept a multiplicity argument
2045                self.model = form_factor()
2046        else:
2047            self.model = None
2048            return
2049
2050        # check if model has magnetic parameters
2051        if len(self.model.magnetic_params) > 0:
2052            self._has_magnetic = True
2053        else:
2054            self._has_magnetic = False
2055        # post state to fit panel
2056        self.state.parameters = []
2057        self.state.model = self.model
2058        self.state.qmin = self.qmin_x
2059        self.state.multi_factor = self.multi_factor
2060        self.disp_list = self.model.getDispParamList()
2061        self.state.disp_list = self.disp_list
2062        self.on_set_focus(None)
2063        self.Layout()
2064
2065    def _validate_qrange(self, qmin_ctrl, qmax_ctrl):
2066        """
2067        Verify that the Q range controls have valid values
2068        and that Qmin < Qmax.
2069
2070        :param qmin_ctrl: text control for Qmin
2071        :param qmax_ctrl: text control for Qmax
2072
2073        :return: True is the Q range is value, False otherwise
2074
2075        """
2076        qmin_validity = check_float(qmin_ctrl)
2077        qmax_validity = check_float(qmax_ctrl)
2078        if not (qmin_validity and qmax_validity):
2079            return False
2080        else:
2081            qmin = float(qmin_ctrl.GetValue())
2082            qmax = float(qmax_ctrl.GetValue())
2083            if qmin < qmax:
2084                # Make sure to set both colours white.
2085                qmin_ctrl.SetBackgroundColour(wx.WHITE)
2086                qmin_ctrl.Refresh()
2087                qmax_ctrl.SetBackgroundColour(wx.WHITE)
2088                qmax_ctrl.Refresh()
2089            else:
2090                qmin_ctrl.SetBackgroundColour("pink")
2091                qmin_ctrl.Refresh()
2092                qmax_ctrl.SetBackgroundColour("pink")
2093                qmax_ctrl.Refresh()
2094                msg = "Invalid Q range: Q min must be smaller than Q max"
2095                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2096                return False
2097        return True
2098
2099    def _validate_Npts(self):
2100        """
2101        Validate the number of points for fitting is more than 10 points.
2102        If valid, setvalues Npts_fit otherwise post msg.
2103        """
2104        # default flag
2105        flag = True
2106        # Theory
2107        if self.data is None and self.enable2D:
2108            return flag
2109        for data in self.data_list:
2110            # q value from qx and qy
2111            radius = np.sqrt(data.qx_data * data.qx_data +
2112                                data.qy_data * data.qy_data)
2113            # get unmasked index
2114            index_data = (float(self.qmin.GetValue()) <= radius) & \
2115                         (radius <= float(self.qmax.GetValue()))
2116            index_data = (index_data) & (data.mask)
2117            index_data = (index_data) & (np.isfinite(data.data))
2118
2119            if len(index_data[index_data]) < 10:
2120                # change the color pink.
2121                self.qmin.SetBackgroundColour("pink")
2122                self.qmin.Refresh()
2123                self.qmax.SetBackgroundColour("pink")
2124                self.qmax.Refresh()
2125                msg = "Data Error: "
2126                msg += "Too few points in %s." % data.name
2127                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2128                self.fitrange = False
2129                flag = False
2130            else:
2131                self.Npts_fit.SetValue(str(len(index_data[index_data])))
2132                self.fitrange = True
2133
2134        return flag
2135
2136    def _validate_Npts_1D(self):
2137        """
2138        Validate the number of points for fitting is more than 5 points.
2139        If valid, setvalues Npts_fit otherwise post msg.
2140        """
2141        # default flag
2142        flag = True
2143        # Theory
2144        if self.data is None:
2145            return flag
2146        for data in self.data_list:
2147            # q value from qx and qy
2148            radius = data.x
2149            # get unmasked index
2150            index_data = (float(self.qmin.GetValue()) <= radius) & \
2151                         (radius <= float(self.qmax.GetValue()))
2152            index_data = (index_data) & (np.isfinite(data.y))
2153
2154            if len(index_data[index_data]) < 5:
2155                # change the color pink.
2156                self.qmin.SetBackgroundColour("pink")
2157                self.qmin.Refresh()
2158                self.qmax.SetBackgroundColour("pink")
2159                self.qmax.Refresh()
2160                msg = "Data Error: "
2161                msg += "Too few points in %s." % data.name
2162                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2163                self.fitrange = False
2164                flag = False
2165            else:
2166                self.Npts_fit.SetValue(str(len(index_data[index_data])))
2167                self.fitrange = True
2168
2169        return flag
2170
2171    def _check_value_enter(self, list):
2172        """
2173        :param list: model parameter and panel info
2174        :Note: each item of the list should be as follow:
2175            item=[check button state, parameter's name,
2176                paramater's value, string="+/-",
2177                parameter's error of fit,
2178                parameter's minimum value,
2179                parameter's maximum value ,
2180                parameter's units]
2181
2182        Returns True if the model parameters have changed.
2183        """
2184        is_modified = False
2185        for item in list:
2186            # skip angle parameters for 1D
2187            if not self.enable2D and item in self.orientation_params:
2188                continue
2189
2190            value_ctrl = item[2]
2191            if not value_ctrl.IsEnabled():
2192                # ArrayDispersion disables PD, Min, Max, Npts, Nsigs
2193                continue
2194
2195            name = item[1]
2196            value_str = value_ctrl.GetValue().strip()
2197            if name.endswith(".npts"):
2198                validity = check_int(value_ctrl)
2199                if not validity:
2200                    continue
2201                value = int(value_str)
2202
2203            elif name.endswith(".nsigmas"):
2204                validity = check_float(value_ctrl)
2205                if not validity:
2206                    continue
2207                value = float(value_str)
2208
2209            else:  # value or polydispersity
2210
2211                # Check that min, max and value are floats
2212                min_ctrl, max_ctrl = item[5], item[6]
2213                min_str = min_ctrl.GetValue().strip()
2214                max_str = max_ctrl.GetValue().strip()
2215                validity = check_float(value_ctrl)
2216                if min_str != "":
2217                    validity = validity and check_float(min_ctrl)
2218                if max_str != "":
2219                    validity = validity and check_float(max_ctrl)
2220                if not validity:
2221                    continue
2222
2223                # Check that min is less than max
2224                low = -np.inf if min_str == "" else float(min_str)
2225                high = np.inf if max_str == "" else float(max_str)
2226                if high < low:
2227                    min_ctrl.SetBackgroundColour("pink")
2228                    min_ctrl.Refresh()
2229                    max_ctrl.SetBackgroundColour("pink")
2230                    max_ctrl.Refresh()
2231                    # msg = "Invalid fit range for %s: min must be smaller
2232                    # than max"%name
2233                    # wx.PostEvent(self._manager.parent,
2234                    # StatusEvent(status=msg))
2235                    continue
2236
2237                # Force value between min and max
2238                value = float(value_str)
2239                if value < low:
2240                    value = low
2241                    value_ctrl.SetValue(format_number(value))
2242                elif value > high:
2243                    value = high
2244                    value_ctrl.SetValue(format_number(value))
2245
2246                if name not in self.model.details.keys():
2247                    self.model.details[name] = ["", None, None]
2248                old_low, old_high = self.model.details[name][1:3]
2249                if old_low != low or old_high != high:
2250                    # The configuration has changed but it won't change the
2251                    # computed curve so no need to set is_modified to True
2252                    # is_modified = True
2253                    self.model.details[name][1:3] = low, high
2254
2255            # Update value in model if it has changed
2256            if value != self.model.getParam(name):
2257                self.model.setParam(name, value)
2258                is_modified = True
2259
2260        return is_modified
2261
2262    def _set_dipers_Param(self, event):
2263        """
2264        respond to self.enable_disp and self.disable_disp radio box.
2265        The dispersity object is reset inside the model into Gaussian.
2266        When the user select yes , this method display a combo box for
2267        more selection when the user selects No,the combo box disappears.
2268        Redraw the model with the default dispersity (Gaussian)
2269        """
2270        # On selction if no model exists.
2271        if self.model is None:
2272            self.disable_disp.SetValue(True)
2273            msg = "Please select a Model first..."
2274            wx.MessageBox(msg, 'Info')
2275            wx.PostEvent(self._manager.parent,
2276                         StatusEvent(status="Polydispersion: %s" % msg))
2277            return
2278
2279        self._reset_dispersity()
2280
2281        if self.model is None:
2282            self.model_disp.Hide()
2283            self.sizer4_4.Clear(True)
2284            return
2285
2286        if self.enable_disp.GetValue():
2287            # layout for model containing no dispersity parameters
2288
2289            self.disp_list = self.model.getDispParamList()
2290
2291            if len(self.disp_list) == 0 and len(self.disp_cb_dict) == 0:
2292                self._layout_sizer_noDipers()
2293            else:
2294                # set gaussian sizer
2295                self._on_select_Disp(event=None)
2296        else:
2297            self.sizer4_4.Clear(True)
2298
2299        # post state to fit panel
2300        self.save_current_state()
2301        if event is not None:
2302            event = PageInfoEvent(page=self)
2303            wx.PostEvent(self.parent, event)
2304        # draw the model with the current dispersity
2305
2306        # Wojtek P, Oct 8, 2016: Calling draw_model seems to be unessecary.
2307        # By comenting it we save an extra Iq calculation
2308        # self._draw_model()
2309
2310        # Need to use FitInside again here to replace the next four lines.
2311        # Otherwised polydispersity off does not resize the scrollwindow.
2312        # PDB Nov 28, 2015
2313        self.FitInside()
2314#        self.sizer4_4.Layout()
2315#        self.sizer5.Layout()
2316#        self.Layout()
2317#        self.Refresh()
2318
2319    def _layout_sizer_noDipers(self):
2320        """
2321        Draw a sizer with no dispersity info
2322        """
2323        ix = 0
2324        iy = 1
2325        self.fittable_param = []
2326        self.fixed_param = []
2327        self.orientation_params_disp = []
2328
2329        self.sizer4_4.Clear(True)
2330        text = "No polydispersity available for this model"
2331        model_disp = wx.StaticText(self, wx.ID_ANY, text)
2332        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
2333                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
2334        self.sizer4_4.Layout()
2335        self.sizer4.Layout()
2336
2337    def _reset_dispersity(self):
2338        """
2339        put gaussian dispersity into current model
2340        """
2341        if len(self.param_toFit) > 0:
2342            for item in self.fittable_param:
2343                if item in self.param_toFit:
2344                    self.param_toFit.remove(item)
2345
2346            for item in self.orientation_params_disp:
2347                if item in self.param_toFit:
2348                    self.param_toFit.remove(item)
2349
2350        self.fittable_param = []
2351        self.fixed_param = []
2352        self.orientation_params_disp = []
2353        self.values = {}
2354        self.weights = {}
2355
2356        # from sas.models.dispersion_models import GaussianDispersion
2357        from sasmodels.weights import GaussianDispersion
2358        if len(self.disp_cb_dict) == 0:
2359            self.save_current_state()
2360            self.sizer4_4.Clear(True)
2361            self.Layout()
2362            return
2363        if (len(self.disp_cb_dict) > 0):
2364            for p in self.disp_cb_dict:
2365                # The parameter was un-selected.
2366                # Go back to Gaussian model (with 0 pts)
2367                disp_model = GaussianDispersion()
2368
2369                self._disp_obj_dict[p] = disp_model
2370                # Set the new model as the dispersion object
2371                # for the selected parameter
2372                try:
2373                    self.model.set_dispersion(p, disp_model)
2374                except Exception:
2375                    logger.error(traceback.format_exc())
2376
2377        # save state into
2378        self.save_current_state()
2379        self.Layout()
2380        self.Refresh()
2381
2382    def _on_select_Disp(self, event):
2383        """
2384        allow selecting different dispersion
2385        self.disp_list should change type later .now only gaussian
2386        """
2387        self._set_sizer_dispersion()
2388
2389        # Redraw the model
2390        #  Wojtek P. Nov 7, 2016: Redrawing seems to be unnecessary here
2391        # self._draw_model()
2392        # self._undo.Enable(True)
2393        event = PageInfoEvent(page=self)
2394        wx.PostEvent(self.parent, event)
2395
2396        self.sizer4_4.Layout()
2397        self.sizer4.Layout()
2398        self.SetupScrolling()
2399
2400    def _on_disp_func(self, event=None):
2401        """
2402        Select a distribution function for the polydispersion
2403
2404        :Param event: ComboBox event
2405        """
2406        # get ready for new event
2407        if event is not None:
2408            event.Skip()
2409        # Get event object
2410        disp_box = event.GetEventObject()
2411
2412        # Try to select a Distr. function
2413        try:
2414            disp_box.SetBackgroundColour("white")
2415            selection = disp_box.GetCurrentSelection()
2416            param_name = disp_box.Name.split('.')[0]
2417            disp_name = disp_box.GetValue()
2418            dispersity = disp_box.GetClientData(selection)
2419
2420            # disp_model =  GaussianDispersion()
2421            disp_model = dispersity()
2422            # Get param names to reset the values of the param
2423            name1 = param_name + ".width"
2424            name2 = param_name + ".npts"
2425            name3 = param_name + ".nsigmas"
2426            # Check Disp. function whether or not it is 'array'
2427            if disp_name.lower() == "array":
2428                value2 = ""
2429                value3 = ""
2430                value1 = self._set_array_disp(name=name1, disp=disp_model)
2431            else:
2432                self._del_array_values(name1)
2433                # self._reset_array_disp(param_name)
2434                self._disp_obj_dict[name1] = disp_model
2435                self.model.set_dispersion(param_name, disp_model)
2436                self.state._disp_obj_dict[name1] = disp_model.type
2437
2438                value1 = str(format_number(self.model.getParam(name1), True))
2439                value2 = str(format_number(self.model.getParam(name2)))
2440                value3 = str(format_number(self.model.getParam(name3)))
2441            # Reset fittable polydispersin parameter value
2442            for item in self.fittable_param:
2443                if item[1] == name1:
2444                    item[2].SetValue(value1)
2445                    item[5].SetValue("")
2446                    item[6].SetValue("")
2447                    # Disable for array
2448                    if disp_name.lower() == "array":
2449                        item[0].SetValue(False)
2450                        item[0].Disable()
2451                        item[2].Disable()
2452                        item[3].Show(False)
2453                        item[4].Show(False)
2454                        item[5].Disable()
2455                        item[6].Disable()
2456                    else:
2457                        item[0].Enable()
2458                        item[2].Enable()
2459                        item[3].Show(True)
2460                        item[4].Show(True)
2461                        item[5].Enable()
2462                        item[6].Enable()
2463                    break
2464            # Reset fixed polydispersion params
2465            for item in self.fixed_param:
2466                if item[1] == name2:
2467                    item[2].SetValue(value2)
2468                    # Disable Npts for array
2469                    if disp_name.lower() == "array":
2470                        item[2].Disable()
2471                    else:
2472                        item[2].Enable()
2473                if item[1] == name3:
2474                    item[2].SetValue(value3)
2475                    # Disable Nsigs for array
2476                    if disp_name.lower() == "array":
2477                        item[2].Disable()
2478                    else:
2479                        item[2].Enable()
2480
2481            # Make sure the check box updated
2482            self.get_all_checked_params()
2483
2484            # update params
2485            self._update_paramv_on_fit()
2486            # draw
2487            self._draw_model()
2488            self.Refresh()
2489        except Exception:
2490            logger.error(traceback.format_exc())
2491            # Error msg
2492            msg = "Error occurred:"
2493            msg += " Could not select the distribution function..."
2494            msg += " Please select another distribution function."
2495            disp_box.SetBackgroundColour("pink")
2496            # Focus on Fit button so that users can see the pinky box
2497            self.btFit.SetFocus()
2498            wx.PostEvent(self._manager.parent,
2499                         StatusEvent(status=msg, info="error"))
2500
2501    def _set_array_disp(self, name=None, disp=None):
2502        """
2503        Set array dispersion
2504
2505        :param name: name of the parameter for the dispersion to be set
2506        :param disp: the polydisperion object
2507        """
2508        # The user wants this parameter to be averaged.
2509        # Pop up the file selection dialog.
2510        path = self._selectDlg()
2511        # Array data
2512        values = []
2513        weights = []
2514        # If nothing was selected, just return
2515        if path is None:
2516            self.disp_cb_dict[name].SetValue(False)
2517            # self.noDisper_rbox.SetValue(True)
2518            return
2519        self._default_save_location = os.path.dirname(path)
2520        if self._manager is not None:
2521            self._manager.parent._default_save_location = \
2522                             self._default_save_location
2523
2524        basename = os.path.basename(path)
2525        values, weights = self.read_file(path)
2526
2527        # If any of the two arrays is empty, notify the user that we won't
2528        # proceed
2529        if len(self.param_toFit) > 0:
2530            if name in self.param_toFit:
2531                self.param_toFit.remove(name)
2532
2533        # Tell the user that we are about to apply the distribution
2534        msg = "Applying loaded %s distribution: %s" % (name, path)
2535        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2536        self._set_array_disp_model(name=name, disp=disp,
2537                                   values=values, weights=weights)
2538        return basename
2539
2540    def _set_array_disp_model(self, name=None, disp=None,
2541                              values=[], weights=[]):
2542        """
2543        Set array dispersion model
2544
2545        :param name: name of the parameter for the dispersion to be set
2546        :param disp: the polydisperion object
2547        """
2548        disp.set_weights(values, weights)
2549        self._disp_obj_dict[name] = disp
2550        self.model.set_dispersion(name.split('.')[0], disp)
2551        self.state._disp_obj_dict[name] = disp.type
2552        self.values[name] = values
2553        self.weights[name] = weights
2554        # Store the object to make it persist outside the
2555        # scope of this method
2556        # TODO: refactor model to clean this up?
2557        self.state.values = {}
2558        self.state.weights = {}
2559        self.state.values = copy.deepcopy(self.values)
2560        self.state.weights = copy.deepcopy(self.weights)
2561
2562        # Set the new model as the dispersion object for the
2563        # selected parameter
2564        # self.model.set_dispersion(p, disp_model)
2565        # Store a reference to the weights in the model object
2566        # so that
2567        # it's not lost when we use the model within another thread.
2568        self.state.model = self.model.clone()
2569        self.model._persistency_dict[name.split('.')[0]] = \
2570            [values, weights]
2571        self.state.model._persistency_dict[name.split('.')[0]] = \
2572            [values, weights]
2573
2574    def _del_array_values(self, name=None):
2575        """
2576        Reset array dispersion
2577
2578        :param name: name of the parameter for the dispersion to be set
2579        """
2580        # Try to delete values and weight of the names array dic if exists
2581        try:
2582            if name in self.values:
2583                del self.values[name]
2584                del self.weights[name]
2585                # delete all other dic
2586                del self.state.values[name]
2587                del self.state.weights[name]
2588                del self.model._persistency_dict[name.split('.')[0]]
2589                del self.state.model._persistency_dict[name.split('.')[0]]
2590        except Exception:
2591            logger.error(traceback.format_exc())
2592
2593    def _lay_out(self):
2594        """
2595        returns self.Layout
2596
2597        :Note: Mac seems to like this better when self.
2598            Layout is called after fitting.
2599        """
2600        self.Layout()
2601        return
2602
2603    def _find_polyfunc_selection(self, disp_func=None):
2604        """
2605        FInd Comboox selection from disp_func
2606
2607        :param disp_function: dispersion distr. function
2608        """
2609        # Find the selection
2610        if disp_func is not None:
2611            try:
2612                return POLYDISPERSITY_MODELS.values().index(disp_func.__class__)
2613            except ValueError:
2614                pass  # Fall through to default class
2615        return POLYDISPERSITY_MODELS.keys().index('gaussian')
2616
2617    def on_reset_clicked(self, event):
2618        """
2619        On 'Reset' button  for Q range clicked
2620        """
2621        flag = True
2622        # For 3 different cases: Data2D, Data1D, and theory
2623        if self.model is None:
2624            msg = "Please select a model first..."
2625            wx.MessageBox(msg, 'Info')
2626            flag = False
2627            return
2628
2629        elif self.data.__class__.__name__ == "Data2D":
2630            data_min = 0
2631            x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax))
2632            y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
2633            self.qmin_x = data_min
2634            self.qmax_x = math.sqrt(x * x + y * y)
2635            # self.data.mask = np.ones(len(self.data.data),dtype=bool)
2636            # check smearing
2637            if not self.disable_smearer.GetValue():
2638                # set smearing value whether or
2639                # not the data contain the smearing info
2640                if self.pinhole_smearer.GetValue():
2641                    flag = self.update_pinhole_smear()
2642                else:
2643                    flag = True
2644
2645        elif self.data is None:
2646            self.qmin_x = _QMIN_DEFAULT
2647            self.qmax_x = _QMAX_DEFAULT
2648            self.num_points = _NPTS_DEFAULT
2649            self.state.npts = self.num_points
2650
2651        elif self.data.__class__.__name__ != "Data2D":
2652            self.qmin_x = min(self.data.x)
2653            self.qmax_x = max(self.data.x)
2654            # check smearing
2655            if not self.disable_smearer.GetValue():
2656                # set smearing value whether or
2657                # not the data contain the smearing info
2658                if self.slit_smearer.GetValue():
2659                    flag = self.update_slit_smear()
2660                elif self.pinhole_smearer.GetValue():
2661                    flag = self.update_pinhole_smear()
2662                else:
2663                    flag = True
2664        else:
2665            flag = False
2666
2667        if flag is False:
2668            msg = "Cannot Plot :Must enter a number!!!  "
2669            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
2670        else:
2671            # set relative text ctrs.
2672            self.qmin.SetValue(str(self.qmin_x))
2673            self.qmax.SetValue(str(self.qmax_x))
2674            self.show_npts2fit()
2675            # At this point, some button and variables satatus (disabled?)
2676            # should be checked such as color that should be reset to
2677            # white in case that it was pink.
2678            self._onparamEnter_helper()
2679
2680        self.save_current_state()
2681        self.state.qmin = self.qmin_x
2682        self.state.qmax = self.qmax_x
2683
2684        # reset the q range values
2685        self._reset_plotting_range(self.state)
2686        self._draw_model()
2687
2688    def select_log(self, event):
2689        """
2690        Log checked to generate log spaced points for theory model
2691        """
2692
2693    def get_images(self):
2694        """
2695        Get the images of the plots corresponding this panel for report
2696
2697        : return graphs: list of figures
2698        : Need Move to guiframe
2699        """
2700        # set list of graphs
2701        graphs = []
2702        canvases = []
2703        res_item = None
2704        # call gui_manager
2705        gui_manager = self._manager.parent
2706        # loops through the panels [dic]
2707        for _, item2 in gui_manager.plot_panels.iteritems():
2708            data_title = self.data.group_id
2709            # try to get all plots belonging to this control panel
2710            try:
2711                g_id = item2.group_id
2712                if g_id == data_title or \
2713                        str(g_id).count("res" + str(self.graph_id)) or \
2714                        str(g_id).count(str(self.uid)) > 0:
2715                    if str(g_id).count("res" + str(self.graph_id)) > 0:
2716                        res_item = [item2.figure, item2.canvas]
2717                    else:
2718                        # append to the list
2719                        graphs.append(item2.figure)
2720                        canvases.append(item2.canvas)
2721            except Exception:
2722                # Not for control panels
2723                logger.error(traceback.format_exc())
2724        # Make sure the resduals plot goes to the last
2725        if res_item is not None:
2726            graphs.append(res_item[0])
2727            canvases.append(res_item[1])
2728        # return the list of graphs
2729        return graphs, canvases
2730
2731    def on_function_help_clicked(self, event):
2732        """
2733        Function called when 'Help' button is pressed next to model
2734        of interest.  This calls DocumentationWindow from
2735        documentation_window.py. It will load the top level of the model
2736        help documenation sphinx generated html if no model is presented.
2737        If a model IS present then if documention for that model exists
2738        it will load to that  point otherwise again it will go to the top.
2739        For Wx2.8 and below is used (i.e. non-released through installer)
2740        a browser is loaded and the top of the model documentation only is
2741        accessible because webbrowser module does not pass anything after
2742        the # to the browser.
2743
2744        :param event: on Help Button pressed event
2745        """
2746
2747        if self.model is not None:
2748            name = self.formfactorbox.GetValue()
2749            _TreeLocation = 'user/models/' + name.lower()+'.html'
2750            _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2751                                              "", name + " Help")
2752        else:
2753            _TreeLocation = 'user/index.html'
2754            _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2755                                              "", "General Model Help")
2756
2757    def on_model_help_clicked(self, event):
2758        """
2759        Function called when 'Description' button is pressed next to model
2760        of interest.  This calls the Description embedded in the model. This
2761        should work with either Wx2.8 and lower or higher. If no model is
2762        selected it will give the message that a model must be chosen first
2763        in the box that would normally contain the description.  If a badly
2764        behaved model is encountered which has no description then it will
2765        give the message that none is available.
2766
2767        :param event: on Description Button pressed event
2768        """
2769
2770        if self.model is None:
2771            name = 'index.html'
2772        else:
2773            name = self.formfactorbox.GetValue()
2774
2775        msg = 'Model description:\n'
2776        info = "Info"
2777        if self.model is not None:
2778            # frame.Destroy()
2779            if str(self.model.description).rstrip().lstrip() == '':
2780                msg += "Sorry, no information is available for this model."
2781            else:
2782                msg += self.model.description + '\n'
2783            wx.MessageBox(msg, info)
2784        else:
2785            msg += "You must select a model to get information on this"
2786            wx.MessageBox(msg, info)
2787
2788    def _on_mag_angle_help(self, event):
2789        """
2790        Bring up Magnetic Angle definition.png image whenever the ? button
2791        is clicked. Calls DocumentationWindow with the path of the location
2792        within the documentation tree (after /doc/ ....". When using old
2793        versions of Wx (i.e. before 2.9 and therefore not part of release
2794        versions distributed via installer) it brings up an image viewer
2795        box which allows the user to click through the rest of the images in
2796        the directory.  Not ideal but probably better than alternative which
2797        would bring up the entire discussion of how magnetic models work?
2798        Specially since it is not likely to be accessed.  The normal release
2799        versions bring up the normal image box.
2800
2801        :param evt: Triggers on clicking ? in Magnetic Angles? box
2802        """
2803
2804        _TreeLocation = "_images/M_angles_pic.png"
2805        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
2806                                          "Magnetic Angle Defintions")
2807
2808    def _on_mag_help(self, event):
2809        """
2810        Bring up Magnetic Angle definition.png image whenever the ? button
2811        is clicked. Calls DocumentationWindow with the path of the location
2812        within the documentation tree (after /doc/ ....". When using old
2813        versions of Wx (i.e. before 2.9 and therefore not part of release
2814        versions distributed via installer) it brings up an image viewer
2815        box which allows the user to click through the rest of the images in
2816        the directory.  Not ideal but probably better than alternative which
2817        would bring up the entire discussion of how magnetic models work?
2818        Specially since it is not likely to be accessed.  The normal release
2819        versions bring up the normal image box.
2820
2821        :param evt: Triggers on clicking ? in Magnetic Angles? box
2822        """
2823
2824        _TreeLocation = "user/magnetism.html"
2825        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
2826                                          "Polarized Beam/Magnetc Help")
2827
2828    def _on_mag_on(self, event):
2829        """
2830        Magnetic Parameters ON/OFF
2831        """
2832        button = event.GetEventObject()
2833
2834        if button.GetLabel().count('ON') > 0:
2835            self.magnetic_on = True
2836            button.SetLabel("Magnetic OFF")
2837            m_value = 1.0e-06
2838            for key in self.model.magnetic_params:
2839                if key.count('M0') > 0:
2840                    self.model.setParam(key, m_value)
2841                    m_value += 0.5e-06
2842        else:
2843            self.magnetic_on = False
2844            button.SetLabel("Magnetic ON")
2845            for key in self.model.magnetic_params:
2846                if key.count('M0') > 0:
2847                    # reset mag value to zero fo safety
2848                    self.model.setParam(key, 0.0)
2849
2850        self.Show(False)
2851        self.set_model_param_sizer(self.model)
2852        # self._set_sizer_dispersion()
2853        self.state.magnetic_on = self.magnetic_on
2854        self.SetupScrolling()
2855        self.Show(True)
2856
2857    def on_pd_help_clicked(self, event):
2858        """
2859        Bring up Polydispersity Documentation whenever the ? button is clicked.
2860        Calls DocumentationWindow with the path of the location within the
2861        documentation tree (after /doc/ ....".  Note that when using old
2862        versions of Wx (before 2.9) and thus not the release version of
2863        istallers, the help comes up at the top level of the file as
2864        webbrowser does not pass anything past the # to the browser when it is
2865        running "file:///...."
2866
2867        :param event: Triggers on clicking ? in polydispersity box
2868        """
2869
2870        _TreeLocation = "user/sasgui/perspectives/fitting/pd_help.html"
2871        _PageAnchor = ""
2872        _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation,
2873                                          _PageAnchor, "Polydispersity Help")
2874
2875    def on_left_down(self, event):
2876        """
2877        Get key stroke event
2878        """
2879        # Figuring out key combo: Cmd for copy, Alt for paste
2880        if event.CmdDown() and event.ShiftDown():
2881            self.get_paste()
2882        elif event.CmdDown():
2883            self.get_copy()
2884        else:
2885            event.Skip()
2886            return
2887        # make event free
2888        event.Skip()
2889
2890    def get_copy(self):
2891        """
2892        Get copy params to clipboard
2893        """
2894        content = self.get_copy_params()
2895        flag = self.set_clipboard(content)
2896        self._copy_info(flag)
2897        return flag
2898
2899    def get_copy_params(self):
2900        """
2901        Get the string copies of the param names and values in the tap
2902        """
2903        content = 'sasview_parameter_values:'
2904        # Do it if params exist
2905        if self.parameters:
2906
2907            # go through the parameters
2908            strings = self._get_copy_helper(self.parameters,
2909                                           self.orientation_params)
2910            content += strings
2911
2912            # go through the fittables
2913            strings = self._get_copy_helper(self.fittable_param,
2914                                           self.orientation_params_disp)
2915            content += strings
2916
2917            # go through the fixed params
2918            strings = self._get_copy_helper(self.fixed_param,
2919                                           self.orientation_params_disp)
2920            content += strings
2921
2922            # go through the str params
2923            strings = self._get_copy_helper(self.str_parameters,
2924                                           self.orientation_params)
2925            content += strings
2926            return content
2927        else:
2928            return False
2929
2930
2931    def _get_copy_params_details(self):
2932        """
2933        Combines polydisperse parameters with self.parameters so that they can
2934        be written to the clipboard (for Excel or LaTeX). Also returns a list of
2935        the names of parameters that have been fitted
2936
2937        :returns: all_params - A list of all parameters, in the format of
2938        self.parameters
2939        :returns: fitted_par_names - A list of the names of parameters that have
2940        been fitted
2941        """
2942        # Names of params that are being fitted
2943        fitted_par_names = [param[1] for param in self.param_toFit]
2944        # Names of params with associated polydispersity
2945        disp_params = [param[1].split('.')[0] for param in self.fittable_param]
2946
2947        # Create array of all parameters
2948        all_params = copy.copy(self.parameters)
2949        for param in self.parameters:
2950            if param[1] in disp_params:
2951                # Polydisperse params aren't in self.parameters, so need adding
2952                # to all_params
2953                name = param[1] + ".width"
2954                index = all_params.index(param) + 1
2955                to_insert = []
2956                if name in fitted_par_names:
2957                    # Param is fitted, so already has a param list in self.param_toFit
2958                    to_insert = self.param_toFit[fitted_par_names.index(name)]
2959                else:
2960                    # Param isn't fitted, so mockup a param list
2961                    to_insert = [None, name, self.model.getParam(name), None, None]
2962                all_params.insert(index, to_insert)
2963        return all_params, fitted_par_names
2964
2965    def get_copy_excel(self):
2966        """
2967        Get copy params to clipboard
2968        """
2969        content = self.get_copy_params_excel()
2970        flag = self.set_clipboard(content)
2971        self._copy_info(flag)
2972        return flag
2973
2974    def get_copy_params_excel(self):
2975        """
2976        Get the string copies of the param names and values in the tap
2977        """
2978        if not self.parameters:
2979            # Do nothing if parameters doesn't exist
2980            return False
2981
2982        content = ''
2983        crlf = chr(13) + chr(10)
2984        tab = chr(9)
2985
2986        all_params, fitted_param_names = self._get_copy_params_details()
2987
2988        # Construct row of parameter names
2989        for param in all_params:
2990            name = param[1] # Parameter name
2991            content += name
2992            content += tab
2993            if name in fitted_param_names:
2994                # Only print errors for fitted parameters
2995                content += name + "_err"
2996                content += tab
2997
2998        content += crlf
2999
3000        # Construct row of parameter values and errors
3001        for param in all_params:
3002            value = param[2]
3003            if hasattr(value, 'GetValue'):
3004                # param[2] is a text box
3005                value = value.GetValue()
3006            else:
3007                # param[2] is a float (from our self._get_copy_params_details)
3008                value = str(value)
3009            content += value
3010            content += tab
3011            if param[1] in fitted_param_names:
3012                # Only print errors for fitted parameters
3013                content += param[4].GetValue()
3014                content += tab
3015
3016        return content
3017
3018    def get_copy_latex(self):
3019        """
3020        Get copy params to clipboard
3021        """
3022        content = self.get_copy_params_latex()
3023        flag = self.set_clipboard(content)
3024        self._copy_info(flag)
3025        return flag
3026
3027    def get_copy_params_latex(self):
3028        """
3029        Get the string copies of the param names and values in the tap
3030        """
3031        if not self.parameters:
3032            # Do nothing if self.parameters doesn't exist
3033            return False
3034       
3035        content = '\\begin{table}'
3036        content += '\\begin{tabular}[h]'
3037
3038        crlf = chr(13) + chr(10)
3039        tab = chr(9)
3040
3041        all_params, fitted_param_names = self._get_copy_params_details()
3042
3043        content += '{|'
3044        for param in all_params:
3045            content += 'l|l|'
3046        content += '}\hline'
3047        content += crlf
3048
3049        # Construct row of parameter names
3050        for index, param in enumerate(all_params):
3051            name = param[1] # Parameter name
3052            content += name.replace('_', '\_')  # Escape underscores
3053            if name in fitted_param_names:
3054                # Only print errors for fitted parameters
3055                content += ' & '
3056                content += name.replace('_', '\_') + "\_err"
3057            if index < len(all_params) - 1:
3058                content += ' & '
3059
3060        content += '\\\\ \\hline'
3061        content += crlf
3062
3063        # Construct row of values and errors
3064        for index, param in enumerate(all_params):
3065            value = param[2]
3066            if hasattr(value, "GetValue"):
3067                # value is a text box
3068                value = value.GetValue()
3069            else:
3070                # value is a float (from self._get_copy_params_details)
3071                value = str(value)
3072            content += value
3073            if param[1] in fitted_param_names:
3074                # Only print errors for fitted params
3075                content += ' & '
3076                content += param[4].GetValue()
3077            if index < len(all_params) - 1:
3078                content += ' & '
3079       
3080        content += '\\\\ \\hline'
3081        content += crlf
3082        content += '\\end{tabular}'
3083        content += '\\end{table}'
3084
3085        return content
3086
3087    def set_clipboard(self, content=None):
3088        """
3089        Put the string to the clipboard
3090        """
3091        if not content:
3092            return False
3093        if wx.TheClipboard.Open():
3094            wx.TheClipboard.SetData(wx.TextDataObject(str(content)))
3095            wx.TheClipboard.Close()
3096            return True
3097        return None
3098
3099    def _get_copy_helper(self, param, orient_param):
3100        """
3101        Helping get value and name of the params
3102
3103        : param param:  parameters
3104        : param orient_param = oritational params
3105        : return content: strings [list] [name,value:....]
3106        """
3107        content = ''
3108        bound_hi = ''
3109        bound_lo = ''
3110        # go through the str params
3111        for item in param:
3112            # copy only the params shown
3113            if not item[2].IsShown():
3114                continue
3115            disfunc = ''
3116            try:
3117                if item[7].__class__.__name__ == 'ComboBox':
3118                    disfunc = str(item[7].GetValue())
3119            except Exception:
3120                logger.error(traceback.format_exc())
3121
3122            # 2D
3123            if self.data.__class__.__name__ == "Data2D":
3124                try:
3125                    check = item[0].GetValue()
3126                except Exception:
3127                    check = None
3128                name = item[1]
3129                value = item[2].GetValue()
3130            # 1D
3131            else:
3132                # for 1D all parameters except orientation
3133                if not item[1] in orient_param:
3134                    try:
3135                        check = item[0].GetValue()
3136                    except:
3137                        check = None
3138                    name = item[1]
3139                    value = item[2].GetValue()
3140
3141            # Bounds
3142            try:
3143                bound_lo = item[5].GetValue()
3144                bound_hi = item[6].GetValue()
3145            except Exception:
3146                # harmless - need to just pass
3147                pass
3148
3149            # add to the content
3150            if disfunc != '':
3151
3152                disfunc = ',' + disfunc
3153            # Need to support array func for copy/paste
3154            try:
3155                if disfunc.count('array') > 0:
3156                    disfunc += ','
3157                    for val in self.values[name]:
3158                        disfunc += ' ' + str(val)
3159                    disfunc += ','
3160                    for weight in self.weights[name]:
3161                        disfunc += ' ' + str(weight)
3162            except Exception:
3163                logger.error(traceback.format_exc())
3164            content += name + ',' + str(check) + ',' + value + disfunc + ',' + \
3165                       bound_lo + ',' + bound_hi + ':'
3166
3167        return content
3168
3169    def get_clipboard(self):
3170        """
3171        Get strings in the clipboard
3172        """
3173        text = ""
3174        # Get text from the clip board
3175        if wx.TheClipboard.Open():
3176            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
3177                data = wx.TextDataObject()
3178                # get wx dataobject
3179                success = wx.TheClipboard.GetData(data)
3180                # get text
3181                if success:
3182                    text = data.GetText()
3183                else:
3184                    text = ''
3185            # close clipboard
3186            wx.TheClipboard.Close()
3187        return text
3188
3189    def get_paste(self):
3190        """
3191        Paste params from the clipboard
3192        """
3193        text = self.get_clipboard()
3194        flag = self.get_paste_params(text)
3195        self._copy_info(flag)
3196        return flag
3197
3198    def get_paste_params(self, text=''):
3199        """
3200        Get the string copies of the param names and values in the tap
3201        """
3202        context = {}
3203        # put the text into dictionary
3204        lines = text.split(':')
3205        if lines[0] != 'sasview_parameter_values':
3206            self._copy_info(False)
3207            return False
3208        for line in lines[1:-1]:
3209            if len(line) != 0:
3210                item = line.split(',')
3211                check = item[1]
3212                name = item[0]
3213                value = item[2]
3214                # Transfer the text to content[dictionary]
3215                context[name] = [check, value]
3216
3217                # limits
3218                limit_lo = item[3]
3219                context[name].append(limit_lo)
3220                limit_hi = item[4]
3221                context[name].append(limit_hi)
3222
3223            # ToDo: PlugIn this poly disp function for pasting
3224            try:
3225                poly_func = item[5]
3226                context[name].append(poly_func)
3227                try:
3228                    # take the vals and weights for  array
3229                    array_values = item[6].split(' ')
3230                    array_weights = item[7].split(' ')
3231                    val = [float(a_val) for a_val in array_values[1:]]
3232                    weit = [float(a_weit) for a_weit in array_weights[1:]]
3233
3234                    context[name].append(val)
3235                    context[name].append(weit)
3236                except:
3237                    raise
3238            except:
3239                poly_func = ''
3240                context[name].append(poly_func)
3241
3242        # Do it if params exist
3243        if self.parameters:
3244            # go through the parameters
3245            self._get_paste_helper(self.parameters,
3246                                   self.orientation_params, context)
3247
3248            # go through the fittables
3249            self._get_paste_helper(self.fittable_param,
3250                                   self.orientation_params_disp,
3251                                   context)
3252
3253            # go through the fixed params
3254            self._get_paste_helper(self.fixed_param,
3255                                   self.orientation_params_disp, context)
3256
3257            # go through the str params
3258            self._get_paste_helper(self.str_parameters,
3259                                   self.orientation_params, context)
3260
3261            return True
3262        return None
3263
3264    def _get_paste_helper(self, param, orient_param, content):
3265        """
3266        Helping set values of the params
3267
3268        : param param:  parameters
3269        : param orient_param: oritational params
3270        : param content: dictionary [ name, value: name1.value1,...]
3271        """
3272        # go through the str params
3273        for item in param:
3274            # 2D
3275            if self.data.__class__.__name__ == "Data2D":
3276                name = item[1]
3277                if name in content.keys():
3278                    values = content[name]
3279                    check = values[0]
3280                    pd = values[1]
3281
3282                    if name.count('.') > 0:
3283                        # If this is parameter.width, then pd may be a floating
3284                        # point value or it may be an array distribution.
3285                        # Nothing to do for parameter.npts or parameter.nsigmas.
3286                        try:
3287                            float(pd)
3288                            if name.endswith('.npts'):
3289                                pd = int(pd)
3290                        except Exception:
3291                            # continue
3292                            if not pd and pd != '':
3293                                continue
3294                    item[2].SetValue(str(pd))
3295                    if item in self.fixed_param and pd == '':
3296                        # Only array func has pd == '' case.
3297                        item[2].Enable(False)
3298                    else:
3299                        item[2].Enable(True)
3300                    if item[2].__class__.__name__ == "ComboBox":
3301                        if content[name][1] in self.model.fun_list:
3302                            fun_val = self.model.fun_list[content[name][1]]
3303                            self.model.setParam(name, fun_val)
3304                    try:
3305                        item[5].SetValue(str(values[-3]))
3306                        item[6].SetValue(str(values[-2]))
3307                    except Exception:
3308                        # passing as harmless non-update
3309                        pass
3310
3311                    value = content[name][1:]
3312                    self._paste_poly_help(item, value)
3313                    if check == 'True':
3314                        is_true = True
3315                    elif check == 'False':
3316                        is_true = False
3317                    else:
3318                        is_true = None
3319                    if is_true is not None:
3320                        item[0].SetValue(is_true)
3321            # 1D
3322            else:
3323                # for 1D all parameters except orientation
3324                if not item[1] in orient_param:
3325                    name = item[1]
3326                    if name in content.keys():
3327                        check = content[name][0]
3328                        # Avoid changing combox content
3329                        value = content[name][1:]
3330                        pd = value[0]
3331                        if name.count('.') > 0:
3332                            # If this is parameter.width, then pd may be a
3333                            # floating point value or it may be an array
3334                            # distribution. Nothing to do for parameter.npts or
3335                            # parameter.nsigmas.
3336                            try:
3337                                pd = float(pd)
3338                                if name.endswith('.npts'):
3339                                    pd = int(pd)
3340                            except:
3341                                # continue
3342                                if not pd and pd != '':
3343                                    continue
3344                        item[2].SetValue(str(pd))
3345                        if item in self.fixed_param and pd == '':
3346                            # Only array func has pd == '' case.
3347                            item[2].Enable(False)
3348                        else:
3349                            item[2].Enable(True)
3350                        if item[2].__class__.__name__ == "ComboBox":
3351                            if value[0] in self.model.fun_list:
3352                                fun_val = self.model.fun_list[value[0]]
3353                                self.model.setParam(name, fun_val)
3354                                # save state
3355                        try:
3356                            item[5].SetValue(str(value[-3]))
3357                            item[6].SetValue(str(value[-2]))
3358                        except Exception:
3359                            # passing as harmless non-update
3360                            pass
3361
3362                        self._paste_poly_help(item, value)
3363                        if check == 'True':
3364                            is_true = True
3365                        elif check == 'False':
3366                            is_true = False
3367                        else:
3368                            is_true = None
3369                        if is_true is not None:
3370                            item[0].SetValue(is_true)
3371
3372        self.select_param(event=None)
3373        self.Refresh()
3374
3375    def _paste_poly_help(self, item, value):
3376        """
3377        Helps get paste for poly function
3378
3379        *item* is the parameter name
3380
3381        *value* depends on which parameter is being processed, and whether it
3382        has array polydispersity.
3383
3384        For parameters without array polydispersity:
3385
3386            parameter => ['FLOAT', '']
3387            parameter.width => ['FLOAT', 'DISTRIBUTION', '']
3388            parameter.npts => ['FLOAT', '']
3389            parameter.nsigmas => ['FLOAT', '']
3390
3391        For parameters with array polydispersity:
3392
3393            parameter => ['FLOAT', '']
3394            parameter.width => ['FILENAME', 'array', [x1, ...], [w1, ...]]
3395            parameter.npts => ['FLOAT', '']
3396            parameter.nsigmas => ['FLOAT', '']
3397        """
3398        # Do nothing if not setting polydispersity
3399        if len(value[3]) == 0:
3400            return
3401
3402        try:
3403            name = item[7].Name
3404            param_name = name.split('.')[0]
3405            item[7].SetValue(value[1])
3406            selection = item[7].GetCurrentSelection()
3407            dispersity = item[7].GetClientData(selection)
3408            disp_model = dispersity()
3409
3410            if value[1] == 'array':
3411                pd_vals = np.array(value[2])
3412                pd_weights = np.array(value[3])
3413                if len(pd_vals) == 0 or len(pd_vals) != len(pd_weights):
3414                    msg = ("bad array distribution parameters for %s"
3415                           % param_name)
3416                    raise ValueError(msg)
3417                self._set_disp_cb(True, item=item)
3418                self._set_array_disp_model(name=name,
3419                                           disp=disp_model,
3420                                           values=pd_vals,
3421                                           weights=pd_weights)
3422            else:
3423                self._set_disp_cb(False, item=item)
3424                self._disp_obj_dict[name] = disp_model
3425                self.model.set_dispersion(param_name, disp_model)
3426                self.state._disp_obj_dict[name] = disp_model.type
3427                # TODO: It's not an array, why update values and weights?
3428                self.model._persistency_dict[param_name] = \
3429                    [self.values, self.weights]
3430                self.state.values = self.values
3431                self.state.weights = self.weights
3432
3433        except Exception:
3434            logger.error(traceback.format_exc())
3435            print("Error in BasePage._paste_poly_help: %s" % \
3436                  sys.exc_info()[1])
3437
3438    def _set_disp_cb(self, isarray, item):
3439        """
3440        Set cb for array disp
3441        """
3442        if isarray:
3443            item[0].SetValue(False)
3444            item[0].Enable(False)
3445            item[2].Enable(False)
3446            item[3].Show(False)
3447            item[4].Show(False)
3448            item[5].SetValue('')
3449            item[5].Enable(False)
3450            item[6].SetValue('')
3451            item[6].Enable(False)
3452        else:
3453            item[0].Enable()
3454            item[2].Enable()
3455            item[3].Show(True)
3456            item[4].Show(True)
3457            item[5].Enable()
3458            item[6].Enable()
3459
3460    def update_pinhole_smear(self):
3461        """
3462            Method to be called by sub-classes
3463            Moveit; This method doesn't belong here
3464        """
3465        print("BasicPage.update_pinhole_smear was called: skipping")
3466        return
3467
3468    def _read_category_info(self):
3469        """
3470        Reads the categories in from file
3471        """
3472        # # ILL mod starts here - July 2012 kieranrcampbell@gmail.com
3473        self.master_category_dict = defaultdict(list)
3474        self.by_model_dict = defaultdict(list)
3475        self.model_enabled_dict = defaultdict(bool)
3476        categorization_file = CategoryInstaller.get_user_file()
3477        with open(categorization_file, 'rb') as f:
3478            self.master_category_dict = json.load(f)
3479        self._regenerate_model_dict()
3480
3481    def _regenerate_model_dict(self):
3482        """
3483        regenerates self.by_model_dict which has each model name as the
3484        key and the list of categories belonging to that model
3485        along with the enabled mapping
3486        """
3487        self.by_model_dict = defaultdict(list)
3488        for category in self.master_category_dict:
3489            for (model, enabled) in self.master_category_dict[category]:
3490                self.by_model_dict[model].append(category)
3491                self.model_enabled_dict[model] = enabled
3492
3493    def _populate_listbox(self):
3494        """
3495        fills out the category list box
3496        """
3497        uncat_str = 'Plugin Models'
3498        self._read_category_info()
3499
3500        self.categorybox.Clear()
3501        cat_list = sorted(self.master_category_dict.keys())
3502        if uncat_str not in cat_list:
3503            cat_list.append(uncat_str)
3504
3505        for category in cat_list:
3506            if category != '':
3507                self.categorybox.Append(category)
3508
3509        if self.categorybox.GetSelection() == wx.NOT_FOUND:
3510            self.categorybox.SetSelection(0)
3511        else:
3512            self.categorybox.SetSelection(
3513                self.categorybox.GetSelection())
3514        # self._on_change_cat(None)
3515
3516    def _on_change_cat(self, event):
3517        """
3518        Callback for category change action
3519        """
3520        self.model_name = None
3521        category = self.categorybox.GetStringSelection()
3522        if category is None:
3523            return
3524        self.model_box.Clear()
3525
3526        if category == 'Plugin Models':
3527            for model in self.model_list_box[category]:
3528                str_m = str(model).split(".")[0]
3529                self.model_box.Append(str_m)
3530
3531        else:
3532            for (model, enabled) in sorted(self.master_category_dict[category],
3533                                           key=lambda name: name[0]):
3534                if(enabled):
3535                    self.model_box.Append(model)
3536
3537    def _fill_model_sizer(self, sizer):
3538        """
3539        fill sizer containing model info
3540        """
3541        # This should only be called once per fit tab
3542        # print "==== Entering _fill_model_sizer"
3543        # Add model function Details button in fitpanel.
3544        # The following 3 lines are for Mac. Let JHC know before modifying...
3545        title = "Model"
3546        self.formfactorbox = None
3547        self.multifactorbox = None
3548        self.mbox_description = wx.StaticBox(self, wx.ID_ANY, str(title))
3549        boxsizer1 = wx.StaticBoxSizer(self.mbox_description, wx.VERTICAL)
3550        sizer_cat = wx.BoxSizer(wx.HORIZONTAL)
3551        self.mbox_description.SetForegroundColour(wx.RED)
3552        wx_id = self._ids.next()
3553        self.model_func = wx.Button(self, wx_id, 'Help', size=(80, 23))
3554        self.model_func.Bind(wx.EVT_BUTTON, self.on_function_help_clicked,
3555                             id=wx_id)
3556        self.model_func.SetToolTipString("Full Model Function Help")
3557        wx_id = self._ids.next()
3558        self.model_help = wx.Button(self, wx_id, 'Description', size=(80, 23))
3559        self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked,
3560                             id=wx_id)
3561        self.model_help.SetToolTipString("Short Model Function Description")
3562        wx_id = self._ids.next()
3563        self.model_view = wx.Button(self, wx_id, "Show 2D", size=(80, 23))
3564        self.model_view.Bind(wx.EVT_BUTTON, self._onModel2D, id=wx_id)
3565        hint = "toggle view of model from 1D to 2D  or 2D to 1D"
3566        self.model_view.SetToolTipString(hint)
3567
3568        cat_set_box = wx.StaticBox(self, wx.ID_ANY, 'Category')
3569        sizer_cat_box = wx.StaticBoxSizer(cat_set_box, wx.HORIZONTAL)
3570        sizer_cat_box.SetMinSize((200, 50))
3571        self.categorybox = wx.ComboBox(self, wx.ID_ANY,
3572                                       style=wx.CB_READONLY)
3573        self.categorybox.SetToolTip(wx.ToolTip("Select a Category/Type"))
3574        self._populate_listbox()
3575        wx.EVT_COMBOBOX(self.categorybox, wx.ID_ANY, self._show_combox)
3576        # self.shape_rbutton = wx.RadioButton(self, wx.ID_ANY, 'Shapes',
3577        #                                     style=wx.RB_GROUP)
3578        # self.shape_indep_rbutton = wx.RadioButton(self, wx.ID_ANY,
3579        #                                          "Shape-Independent")
3580        # self.struct_rbutton = wx.RadioButton(self, wx.ID_ANY,
3581        #                                     "Structure Factor ")
3582        # self.plugin_rbutton = wx.RadioButton(self, wx.ID_ANY,
3583        #                                     "Uncategorized")
3584
3585        # self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3586        #                   id=self.shape_rbutton.GetId())
3587        # self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3588        #                    id=self.shape_indep_rbutton.GetId())
3589        # self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3590        #                    id=self.struct_rbutton.GetId())
3591        # self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
3592        #                    id=self.plugin_rbutton.GetId())
3593        # MAC needs SetValue
3594
3595        show_cat_button = wx.Button(self, wx.ID_ANY, "Modify")
3596        cat_tip = "Modify model categories \n"
3597        cat_tip += "(also accessible from the menu bar)."
3598        show_cat_button.SetToolTip(wx.ToolTip(cat_tip))
3599        show_cat_button.Bind(wx.EVT_BUTTON, self._on_modify_cat)
3600        sizer_cat_box.Add(self.categorybox, 1, wx.RIGHT, 3)
3601        sizer_cat_box.Add((10, 10))
3602        sizer_cat_box.Add(show_cat_button)
3603        # self.shape_rbutton.SetValue(True)
3604
3605        sizer_radiobutton = wx.GridSizer(2, 2, 5, 5)
3606        # sizer_radiobutton.Add(self.shape_rbutton)
3607        # sizer_radiobutton.Add(self.shape_indep_rbutton)
3608        sizer_radiobutton.Add((5, 5))
3609        sizer_radiobutton.Add(self.model_view, 1, wx.RIGHT, 5)
3610        # sizer_radiobutton.Add(self.plugin_rbutton)
3611        # sizer_radiobutton.Add(self.struct_rbutton)
3612        # sizer_radiobutton.Add((5,5))
3613        sizer_radiobutton.Add(self.model_help, 1, wx.RIGHT | wx.LEFT, 5)
3614        # sizer_radiobutton.Add((5,5))
3615        sizer_radiobutton.Add(self.model_func, 1, wx.RIGHT, 5)
3616        sizer_cat.Add(sizer_cat_box, 1, wx.LEFT, 2.5)
3617        sizer_cat.Add(sizer_radiobutton)
3618        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
3619        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
3620
3621        self.text1 = wx.StaticText(self, wx.ID_ANY, "")
3622        self.text2 = wx.StaticText(self, wx.ID_ANY, "P(Q)*S(Q)")
3623        self.mutifactor_text = wx.StaticText(self, wx.ID_ANY, "No. of Shells: ")
3624        self.mutifactor_text1 = wx.StaticText(self, wx.ID_ANY, "")
3625        self.show_sld_button = wx.Button(self, wx.ID_ANY, "Show SLD Profile")
3626        self.show_sld_button.Bind(wx.EVT_BUTTON, self._on_show_sld)
3627
3628        self.formfactorbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3629        self.formfactorbox.SetToolTip(wx.ToolTip("Select a Model"))
3630        if self.model is not None:
3631            self.formfactorbox.SetValue(self.model.name)
3632        self.structurebox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3633        self.multifactorbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
3634        self.initialize_combox()
3635        wx.EVT_COMBOBOX(self.formfactorbox, wx.ID_ANY, self._on_select_model)
3636
3637        wx.EVT_COMBOBOX(self.structurebox, wx.ID_ANY, self._on_select_model)
3638        wx.EVT_COMBOBOX(self.multifactorbox, wx.ID_ANY, self._on_select_model)
3639        # check model type to show sizer
3640        if self.model is not None:
3641            print("_set_model_sizer_selection: disabled.")
3642            # self._set_model_sizer_selection(self.model)
3643
3644        sizer_selection.Add(self.text1)
3645        sizer_selection.Add((10, 5))
3646        sizer_selection.Add(self.formfactorbox)
3647        sizer_selection.Add((5, 5))
3648        sizer_selection.Add(self.text2)
3649        sizer_selection.Add((5, 5))
3650        sizer_selection.Add(self.structurebox)
3651
3652        mutifactor_selection.Add((13, 5))
3653        mutifactor_selection.Add(self.mutifactor_text)
3654        mutifactor_selection.Add(self.multifactorbox)
3655        mutifactor_selection.Add((5, 5))
3656        mutifactor_selection.Add(self.mutifactor_text1)
3657        mutifactor_selection.Add((10, 5))
3658        mutifactor_selection.Add(self.show_sld_button)
3659
3660        boxsizer1.Add(sizer_cat)
3661        boxsizer1.Add((10, 10))
3662        boxsizer1.Add(sizer_selection)
3663        boxsizer1.Add((10, 10))
3664        boxsizer1.Add(mutifactor_selection)
3665
3666        self._set_multfactor_combobox()
3667        self.multifactorbox.SetSelection(1)
3668        self.show_sld_button.Hide()
3669        sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
3670        sizer.Layout()
3671
3672    def on_smear_helper(self, update=False):
3673        """
3674        Help for onSmear if implemented
3675
3676        :param update: force or not to update
3677        """
3678    def reset_page(self, state, first=False):
3679        """
3680        reset the state  if implemented
3681        """
3682    def onSmear(self, event):
3683        """
3684        Create a smear object if implemented
3685        """
3686    def onPinholeSmear(self, event):
3687        """
3688        Create a custom pinhole smear object if implemented
3689        """
3690    def onSlitSmear(self, event):
3691        """
3692        Create a custom slit smear object if implemented
3693        """
3694    def update_slit_smear(self):
3695        """
3696        called by kill_focus on pinhole TextCntrl
3697        to update the changes if implemented
3698        """
3699    def select_param(self, event):
3700        """
3701        Select TextCtrl  checked if implemented
3702        """
3703    def set_data(self, data=None):
3704        """
3705        Sets data if implemented
3706        """
3707    def _is_2D(self):
3708        """
3709        Check if data_name is Data2D if implemented
3710        """
3711    def _on_select_model(self, event=None):
3712        """
3713        call back for model selection if implemented
3714        """
3715    def get_weight_flag(self):
3716        """
3717        Get flag corresponding to a given weighting dI data if implemented
3718        """
3719    def _set_sizer_dispersion(self):
3720        """
3721        draw sizer for dispersity if implemented
3722        """
3723    def get_all_checked_params(self):
3724        """
3725        Found all parameters current check and add them to list of parameters
3726        to fit if implemented
3727        """
3728    def show_npts2fit(self):
3729        """
3730        setValue Npts for fitting if implemented
3731        """
3732    def _onModel2D(self, event):
3733        """
3734        toggle view of model from 1D to 2D  or 2D from 1D if implemented
3735        """
3736
3737
3738class ModelTextCtrl(wx.TextCtrl):
3739    """
3740    Text control for model and fit parameters.
3741    Binds the appropriate events for user interactions.
3742    Default callback methods can be overwritten on initialization
3743
3744    :param kill_focus_callback: callback method for EVT_KILL_FOCUS event
3745    :param set_focus_callback:  callback method for EVT_SET_FOCUS event
3746    :param mouse_up_callback:   callback method for EVT_LEFT_UP event
3747    :param text_enter_callback: callback method for EVT_TEXT_ENTER event
3748
3749    """
3750    # Set to True when the mouse is clicked while whole string is selected
3751    full_selection = False
3752    # Call back for EVT_SET_FOCUS events
3753    _on_set_focus_callback = None
3754
3755    def __init__(self, parent, id=-1,
3756                 value=wx.EmptyString,
3757                 pos=wx.DefaultPosition,
3758                 size=wx.DefaultSize,
3759                 style=0,
3760                 validator=wx.DefaultValidator,
3761                 name=wx.TextCtrlNameStr,
3762                 kill_focus_callback=None,
3763                 set_focus_callback=None,
3764                 mouse_up_callback=None,
3765                 text_enter_callback=None):
3766
3767        wx.TextCtrl.__init__(self, parent, id, value, pos,
3768                             size, style, validator, name)
3769
3770        # Bind appropriate events
3771        self._on_set_focus_callback = parent.onSetFocus \
3772            if set_focus_callback is None else set_focus_callback
3773        self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
3774        self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus
3775        if kill_focus_callback is None else kill_focus_callback)
3776        self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter
3777        if text_enter_callback is None else text_enter_callback)
3778        if not ON_MAC:
3779            self.Bind(wx.EVT_LEFT_UP, self._highlight_text
3780            if mouse_up_callback is None else mouse_up_callback)
3781
3782    def _on_set_focus(self, event):
3783        """
3784        Catch when the text control is set in focus to highlight the whole
3785        text if necessary
3786
3787        :param event: mouse event
3788
3789        """
3790        event.Skip()
3791        self.full_selection = True
3792        return self._on_set_focus_callback(event)
3793
3794    def _highlight_text(self, event):
3795        """
3796        Highlight text of a TextCtrl only of no text has be selected
3797
3798        :param event: mouse event
3799
3800        """
3801        # Make sure the mouse event is available to other listeners
3802        event.Skip()
3803        control = event.GetEventObject()
3804        if self.full_selection:
3805            self.full_selection = False
3806            # Check that we have a TextCtrl
3807            if issubclass(control.__class__, wx.TextCtrl):
3808                # Check whether text has been selected,
3809                # if not, select the whole string
3810                (start, end) = control.GetSelection()
3811                if start == end:
3812                    control.SetSelection(-1, -1)
3813
3814    def _silent_kill_focus(self, event):
3815        """
3816        Save the state of the page
3817        """
3818
3819        event.Skip()
3820        # pass
Note: See TracBrowser for help on using the repository browser.