source: sasview/src/sas/perspectives/fitting/basepage.py @ 7116dffd

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 7116dffd was 7116dffd, checked in by butler, 9 years ago

Added Polarized beam help button (next to mag angle definition button)

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