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

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 4b402a1 was 4b402a1, checked in by Tim Snow <tim.snow@…>, 7 years ago

PEP-8 Compliance

Tiding up code ready for the pull request.

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