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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since a0373d5 was a0373d5, checked in by Paul Kienzle <pkienzle@…>, 5 years ago

Reenable processing of Npts and Nsigs for polydispersity

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