source: sasview/src/sas/perspectives/invariant/invariant_panel.py @ 686fd10

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 686fd10 was 0e33a8d, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Minor fixes to state loading

  • Property mode set to 100644
File size: 79.2 KB
RevLine 
[c128284]1"""
[cce0ad3]2This module provides the GUI for the invariant perspective panel
[d7a39e5]3
[c128284]4"""
[4a2b054]5import copy
6import time
7import sys
8import os
[c128284]9import wx
[1128bd31]10import logging
[9abec44]11from wx.lib.scrolledpanel import ScrolledPanel
[79492222]12from sas.invariant import invariant
13from sas.guiframe.utils import format_number
14from sas.guiframe.utils import check_float
15from sas.guiframe.events import StatusEvent
16from sas.guiframe.events import AppendBookmarkEvent
[b9a5f0e]17from sas.perspectives.invariant.invariant_details import InvariantDetailsPanel
18from sas.perspectives.invariant.invariant_details import InvariantContainer
19from sas.perspectives.invariant.invariant_widgets import OutputTextCtrl
20from sas.perspectives.invariant.invariant_widgets import InvTextCtrl
21from sas.perspectives.invariant.invariant_state import InvariantState as IState
[79492222]22from sas.guiframe.panel_base import PanelBase
[cce0ad3]23from sas.guiframe.documentation_window import DocumentationWindow
24
[c128284]25# The minimum q-value to be used when extrapolating
[1128bd31]26Q_MINIMUM = 1e-5
[c128284]27# The maximum q-value to be used when extrapolating
[1128bd31]28Q_MAXIMUM = 10
[90b9a17]29# the ratio of maximum q value/(qmax of data) to plot the theory data
30Q_MAXIMUM_PLOT = 3
[c128284]31# the number of points to consider during fit
32NPTS = 10
33#Default value for background
34BACKGROUND = 0.0
35#default value for the scale
36SCALE = 1.0
[9ce7641c]37#default value of the contrast
38CONTRAST = 1.0
[d0cc0bbc]39#default value of the power used for power law
40POWER = 4.0
[1128bd31]41#Invariant panel size
[c128284]42_BOX_WIDTH = 76
43
[4e1c362]44
[b8ff610]45if sys.platform.count("win32") > 0:
[f686259]46    _STATICBOX_WIDTH = 450
[1128bd31]47    PANEL_WIDTH = 500
[c128284]48    PANEL_HEIGHT = 700
49    FONT_VARIANT = 0
50else:
[f686259]51    _STATICBOX_WIDTH = 490
[c128284]52    PANEL_WIDTH = 530
53    PANEL_HEIGHT = 700
54    FONT_VARIANT = 1
[d0cc0bbc]55
56
[6a0cbcf4]57class InvariantPanel(ScrolledPanel, PanelBase):
[c128284]58    """
[cce0ad3]59    Main class defining the sizers (wx "panels") used to draw the
60    Invariant GUI.
[c128284]61    """
62    ## Internal nickname for the window, used by the AUI manager
63    window_name = "Invariant"
64    ## Name to appear on the window title bar
65    window_caption = "Invariant"
66    ## Flag to tell the AUI manager to put this panel in the center pane
67    CENTER_PANE = True
[4a2b054]68    def __init__(self, parent, data=None, manager=None, *args, **kwds):
69        kwds["size"] = (PANEL_WIDTH, PANEL_HEIGHT)
70        kwds["style"] = wx.FULL_REPAINT_ON_RESIZE
[355b684]71        ScrolledPanel.__init__(self, parent=parent, *args, **kwds)
[27f3831]72        PanelBase.__init__(self, parent)
[3de66c1]73        self.SetupScrolling()
[1128bd31]74        #Font size
[c128284]75        self.SetWindowVariant(variant=FONT_VARIANT)
76        #Object that receive status event
[ae84427]77        self.parent = parent.parent
[272d91e]78        #plug-in using this panel
[1128bd31]79        self._manager = manager
[272d91e]80        #Data uses for computation
81        self._data = data
[1128bd31]82        self._scale = SCALE
[6d55d81]83        self._background = BACKGROUND
[4e1c362]84        self._bmark = None
85        self.bookmark_num = 0
[699df7f]86        self.state = None
87        self.popUpMenu = None
[cefb3fb]88        self._set_bookmark_menu()
[4e1c362]89        #Init state
90        self.set_state()
91        # default flags for state
92        self.new_state = False
[d318616]93        self.is_state_data = False
[f24925ab]94        self.is_power_out = False
[cef847c]95
[9ce7641c]96        #container of invariant value
97        self.inv_container = None
[699df7f]98        #sizers
99        self.main_sizer = None
100        self.outputs_sizer = None
101        self.data_name_boxsizer = None
102        self.hint_msg_sizer = None
103        self.data_name_sizer = None
104        self.data_range_sizer = None
105        self.sizer_input = None
106        self.inputs_sizer = None
107        self.extrapolation_sizer = None
108        self.extrapolation_range_sizer = None
109        self.extrapolation_low_high_sizer = None
110        self.low_extrapolation_sizer = None
111        self.low_q_sizer = None
112        self.high_extrapolation_sizer = None
113        self.high_q_sizer = None
114        self.volume_surface_sizer = None
115        self.invariant_sizer = None
116        self.button_sizer = None
117        self.save_button_sizer = None
118        self.hint_msg_txt = None
119        self.data_name_tcl = None
120        self.data_min_tcl = None
121        self.data_max_tcl = None
[c128284]122        #Draw the panel
123        self._do_layout()
[d0cc0bbc]124        self.reset_panel()
[4e1c362]125        self._reset_state_list()
[c4ae1c2]126        ## Default file location for save
[1128bd31]127        self._default_save_location = os.getcwd()
[a0a4486]128        if self.parent is not None:
129            msg = ""
[0d417ac8]130            wx.PostEvent(self.parent, StatusEvent(status=msg, info="info"))
131            self._default_save_location = \
[699df7f]132                        self.parent.get_save_location()
[1128bd31]133
[cb69775]134        self._set_bookmark_flag(False)
[1128bd31]135
[35f2f49]136    def get_data(self):
137        """
138        """
139        return self._manager.get_data()
[1128bd31]140
[35f2f49]141    def get_state(self):
142        """
143        """
144        return self.state
[1128bd31]145
[210ff4f]146    def set_data(self, data):
[343fdb6]147        """
[210ff4f]148        Set the data
[c128284]149        """
[272d91e]150        self._data = data
151        #edit the panel
[210ff4f]152        if self._data is not None:
[fadf925]153            self._delete_bookmark_items()
[9b18735]154            self.get_state_by_num(0)
[272d91e]155            data_name = self._data.name
[1128bd31]156            data_qmin = min(self._data.x)
157            data_qmax = max(self._data.x)
[210ff4f]158            self.data_name_tcl.SetValue(str(data_name))
[effce1d]159            self.data_min_tcl.SetValue(str(data_qmin))
160            self.data_max_tcl.SetValue(str(data_qmax))
[d0cc0bbc]161            self.reset_panel()
162            self.compute_invariant(event=None)
[b35d3d1]163            self.state.file = self._data.name
[9b18735]164            #Reset the list of states
165            self.state.data = copy.deepcopy(data)
[f932c02]166            self._set_save_flag(True)
[7065641]167            self._set_preview_flag(False)
[9b18735]168            self._reset_state_list()
[cefb3fb]169            self._set_bookmark_flag(True)
[1128bd31]170        return True
171
[fadf925]172    def _delete_bookmark_items(self):
173        """
174        Delete bookmark menu items
175        """
176        # delete toolbar menu
177        self.parent.reset_bookmark_menu(self)
178        self.parent._update_toolbar_helper()
179        # delete popUpMenu items
180        pos = 0
181        for item in self.popUpMenu.GetMenuItems():
182            pos += 1
183            if pos < 3:
184                continue
185            self.popUpMenu.DestroyItem(item)
[1128bd31]186
[d0cc0bbc]187    def set_message(self):
188        """
[d7a39e5]189        Display warning message if available
[d0cc0bbc]190        """
191        if self.inv_container is not None:
192            if self.inv_container.existing_warning:
193                msg = "Warning! Computations on invariant require your "
[f6518f8]194                msg += "attention.\nPlease click on Details button."
[d0cc0bbc]195                self.hint_msg_txt.SetForegroundColour("red")
[1128bd31]196
[4a2b054]197                wx.PostEvent(self.parent,
[1128bd31]198                             StatusEvent(status=msg, info="warning"))
[d0cc0bbc]199            else:
200                msg = "For more information, click on Details button."
201                self.hint_msg_txt.SetForegroundColour("black")
[4a2b054]202                wx.PostEvent(self.parent,
[1128bd31]203                             StatusEvent(status=msg, info="info"))
[d0cc0bbc]204            self.hint_msg_txt.SetLabel(msg)
[f6518f8]205        self.Layout()
[1128bd31]206
[c128284]207    def set_manager(self, manager):
208        """
[d7a39e5]209        set value for the manager
[c128284]210        """
[1128bd31]211        self._manager = manager
212
[ed4e122]213    def save_project(self, doc=None):
214        """
215        return an xml node containing state of the panel
216         that guiframe can write to file
217        """
218        data = self.get_data()
219        state = self.get_state()
220        if data is not None:
221            new_doc = self._manager.state_reader.write_toXML(data, state)
222            if new_doc is not None:
223                if doc is not None and hasattr(doc, "firstChild"):
[92a2ecd]224                    child = new_doc.getElementsByTagName("SASentry")
225                    for item in child:
226                        doc.firstChild.appendChild(item)
[ed4e122]227                else:
228                    doc = new_doc
[1128bd31]229        return doc
[ed4e122]230
[0d417ac8]231    def set_state(self, state=None, data=None):
[4e1c362]232        """
[9b18735]233        set state when loading it from a .inv/.svs file
[4e1c362]234        """
[1128bd31]235
[645f9b6]236        if state == None and data == None:
[4e1c362]237            self.state = IState()
[1128bd31]238        elif state == None or data == None:
[0d417ac8]239            return
[4e1c362]240        else:
[04295ea]241            new_state = copy.deepcopy(state)
[f24925ab]242            self.new_state = True
[210ff4f]243            if not self.set_data(data):
[4e1c362]244                return
[04295ea]245
[1128bd31]246            self.state = new_state
247            self.state.file = data.name
[645f9b6]248
[4e1c362]249            num = self.state.saved_state['state_num']
[1128bd31]250            if num > 0:
[27f3831]251                self._set_undo_flag(True)
[4a2b054]252            if num < len(state.state_list) - 1:
[27f3831]253                self._set_redo_flag(True)
[1128bd31]254
[4e1c362]255            # get bookmarks
256            self.bookmark_num = len(self.state.bookmark_list)
[4a2b054]257            total_bookmark_num = self.bookmark_num + 1
[04295ea]258
[0d417ac8]259            for ind in range(1, total_bookmark_num):
[4e1c362]260                #bookmark_num = ind
261                value = self.state.bookmark_list[ind]
[1128bd31]262                name = "%d] bookmarked at %s on %s" % (ind, value[0], value[1])
[4e1c362]263                # append it to menu
264                id = wx.NewId()
[0d417ac8]265                self.popUpMenu.Append(id, name, str(''))
[1128bd31]266                wx.EVT_MENU(self, id, self._back_to_bookmark)
267                wx.PostEvent(self.parent,
268                             AppendBookmarkEvent(title=name,
269                                                 hint='',
270                                                 handler=self._back_to_bookmark))
[cefb3fb]271
[cb463b4]272            self.get_state_by_num(state_num=str(num))
[1128bd31]273
274            self._get_input_list()
[4a2b054]275            #make sure that the data is reset (especially
276            # when loaded from a inv file)
[cb274d9e]277            self.state.data = self._data
[cefb3fb]278            self._set_preview_flag(False)
[1128bd31]279            self.new_state = False
[f24925ab]280            self.is_state_data = False
[0399c78]281
[cb69775]282    def clear_panel(self):
[9b18735]283        """
284        Clear panel to defaults, used by set_state of manager
285        """
[1128bd31]286
[cb69775]287        self._data = None
288        # default data testctrl
289        self.hint_msg_txt.SetLabel('')
290        data_name = ''
291        data_qmin = ''
292        data_qmax = ''
293        self.data_name_tcl.SetValue(str(data_name))
294        self.data_min_tcl.SetValue(str(data_qmin))
295        self.data_max_tcl.SetValue(str(data_qmax))
296        #reset output textctrl
297        self._reset_output()
298        #reset panel
299        self.reset_panel()
300        #reset state w/o data
301        self.set_state()
302        # default flags for state
303        self.new_state = False
304        self.is_state_data = False
305        self.is_power_out = False
[4e1c362]306
[9ce7641c]307    def get_background(self):
[c128284]308        """
[d7a39e5]309        return the background textcrtl value as a float
[c128284]310        """
[9ce7641c]311        background = self.background_tcl.GetValue().lstrip().rstrip()
[c128284]312        if background == "":
[9ce7641c]313            raise ValueError, "Need a background"
314        if check_float(self.background_tcl):
315            return float(background)
316        else:
[4a2b054]317            msg = "Receive invalid value for background : %s" % (background)
318            raise ValueError, msg
[1128bd31]319
[9ce7641c]320    def get_scale(self):
321        """
[d7a39e5]322        return the scale textcrtl value as a float
[9ce7641c]323        """
324        scale = self.scale_tcl.GetValue().lstrip().rstrip()
[c128284]325        if scale == "":
[9ce7641c]326            raise ValueError, "Need a background"
327        if check_float(self.scale_tcl):
[4a2b054]328            if float(scale) <= 0.0:
[6d55d81]329                self.scale_tcl.SetBackgroundColour("pink")
330                self.scale_tcl.Refresh()
[4a2b054]331                msg = "Receive invalid value for scale: %s" % (scale)
332                raise ValueError, msg
[9ce7641c]333            return float(scale)
334        else:
[4a2b054]335            raise ValueError, "Receive invalid value for scale : %s" % (scale)
[1128bd31]336
[9ce7641c]337    def get_contrast(self):
338        """
[d7a39e5]339        return the contrast textcrtl value as a float
[9ce7641c]340        """
341        par_str = self.contrast_tcl.GetValue().strip()
342        contrast = None
[1128bd31]343        if par_str != " " and check_float(self.contrast_tcl):
[9ce7641c]344            contrast = float(par_str)
345        return contrast
[1128bd31]346
[9ce7641c]347    def get_extrapolation_type(self, low_q, high_q):
348        """
[0d417ac8]349        get extrapolation type
[9ce7641c]350        """
351        extrapolation = None
352        if low_q  and not high_q:
353            extrapolation = "low"
354        elif not low_q  and high_q:
355            extrapolation = "high"
356        elif low_q and high_q:
357            extrapolation = "both"
358        return extrapolation
[1128bd31]359
[9ce7641c]360    def get_porod_const(self):
361        """
[d7a39e5]362        return the porod constant textcrtl value as a float
[9ce7641c]363        """
364        par_str = self.porod_constant_tcl.GetValue().strip()
365        porod_const = None
[4a2b054]366        if par_str != "" and check_float(self.porod_constant_tcl):
[9ce7641c]367            porod_const = float(par_str)
368        return porod_const
[1128bd31]369
[9ce7641c]370    def get_volume(self, inv, contrast, extrapolation):
371        """
[0d417ac8]372        get volume fraction
[9ce7641c]373        """
374        if contrast is not None:
[c128284]375            try:
[1128bd31]376                v, dv = inv.get_volume_fraction_with_error(contrast=contrast,
377                                                           extrapolation=extrapolation)
[9ce7641c]378                self.volume_tcl.SetValue(format_number(v))
379                self.volume_err_tcl.SetValue(format_number(dv))
[c128284]380            except:
[0a8759f]381                self.volume_tcl.SetValue(format_number(None))
382                self.volume_err_tcl.SetValue(format_number(None))
[4a2b054]383                msg = "Error occurred computing volume "
384                msg += " fraction: %s" % sys.exc_value
[e3f721e4]385                wx.PostEvent(self.parent, StatusEvent(status=msg,
386                                                      info="error",
387                                                      type="stop"))
[1128bd31]388
[9ce7641c]389    def get_surface(self, inv, contrast, porod_const, extrapolation):
390        """
[0d417ac8]391        get surface value
[9ce7641c]392        """
393        if contrast is not None and porod_const is not None:
[c128284]394            try:
[9ce7641c]395                s, ds = inv.get_surface_with_error(contrast=contrast,
[1128bd31]396                                                   porod_const=porod_const,
397                                                   extrapolation=extrapolation)
[9ce7641c]398                self.surface_tcl.SetValue(format_number(s))
399                self.surface_err_tcl.SetValue(format_number(ds))
[c128284]400            except:
[0a8759f]401                self.surface_tcl.SetValue(format_number(None))
402                self.surface_err_tcl.SetValue(format_number(None))
[4a2b054]403                msg = "Error occurred computing "
404                msg += "specific surface: %s" % sys.exc_value
[e3f721e4]405                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
[1128bd31]406                                                      type="stop"))
407
[9ce7641c]408    def get_total_qstar(self, inv, extrapolation):
409        """
[0d417ac8]410        get total qstar
[9ce7641c]411        """
412        try:
[699df7f]413            qstar_total, qstar_total_err = \
414                                    inv.get_qstar_with_error(extrapolation)
[9ce7641c]415            self.invariant_total_tcl.SetValue(format_number(qstar_total))
[699df7f]416            self.invariant_total_err_tcl.SetValue(\
417                                    format_number(qstar_total_err))
[9ce7641c]418            self.inv_container.qstar_total = qstar_total
419            self.inv_container.qstar_total_err = qstar_total_err
420        except:
[0a8759f]421            self.inv_container.qstar_total = "Error"
422            self.inv_container.qstar_total_err = "Error"
423            self.invariant_total_tcl.SetValue(format_number(None))
424            self.invariant_total_err_tcl.SetValue(format_number(None))
[4a2b054]425            msg = "Error occurred computing invariant using"
426            msg += " extrapolation: %s" % sys.exc_value
[1128bd31]427            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
428
[9ce7641c]429    def get_low_qstar(self, inv, npts_low, low_q=False):
430        """
[0d417ac8]431        get low qstar
[9ce7641c]432        """
433        if low_q:
[1128bd31]434            try:
[9ce7641c]435                qstar_low, qstar_low_err = inv.get_qstar_low()
436                self.inv_container.qstar_low = qstar_low
437                self.inv_container.qstar_low_err = qstar_low_err
[1128bd31]438                extrapolated_data = inv.get_extra_data_low(npts_in=npts_low)
439                power_low = inv.get_extrapolation_power(range='low')
[518d35d]440                if self.power_law_low.GetValue():
[d0cc0bbc]441                    self.power_low_tcl.SetValue(format_number(power_low))
[9ce7641c]442                self._manager.plot_theory(data=extrapolated_data,
[1128bd31]443                                          name="Low-Q extrapolation")
[9ce7641c]444            except:
[0a8759f]445                self.inv_container.qstar_low = "ERROR"
446                self.inv_container.qstar_low_err = "ERROR"
447                self._manager.plot_theory(name="Low-Q extrapolation")
[4a2b054]448                msg = "Error occurred computing low-Q "
449                msg += "invariant: %s" % sys.exc_value
450                wx.PostEvent(self.parent,
451                             StatusEvent(status=msg, type="stop"))
[699df7f]452                raise
[c128284]453        else:
[4e1c362]454            try:
455                self._manager.plot_theory(name="Low-Q extrapolation")
[1128bd31]456            except:
457                logging.error(sys.exc_value)
458
[9ce7641c]459    def get_high_qstar(self, inv, high_q=False):
460        """
[0d417ac8]461        get high qstar
[9ce7641c]462        """
463        if high_q:
[1128bd31]464            try:
[90b9a17]465                qmax_plot = Q_MAXIMUM_PLOT * max(self._data.x)
[1128bd31]466                if qmax_plot > Q_MAXIMUM:
[699df7f]467                    qmax_plot = Q_MAXIMUM
[9ce7641c]468                qstar_high, qstar_high_err = inv.get_qstar_high()
469                self.inv_container.qstar_high = qstar_high
470                self.inv_container.qstar_high_err = qstar_high_err
[1128bd31]471                power_high = inv.get_extrapolation_power(range='high')
[d0cc0bbc]472                self.power_high_tcl.SetValue(format_number(power_high))
[4a2b054]473                high_out_data = inv.get_extra_data_high(q_end=qmax_plot,
474                                                        npts=500)
[9ce7641c]475                self._manager.plot_theory(data=high_out_data,
[1128bd31]476                                          name="High-Q extrapolation")
[9ce7641c]477            except:
[7065641]478                #raise
[0a8759f]479                self.inv_container.qstar_high = "ERROR"
480                self.inv_container.qstar_high_err = "ERROR"
481                self._manager.plot_theory(name="High-Q extrapolation")
[4a2b054]482                msg = "Error occurred computing high-Q "
483                msg += "invariant: %s" % sys.exc_value
484                wx.PostEvent(self.parent, StatusEvent(status=msg,
485                                                      type="stop"))
[07eb396]486                raise
[9ce7641c]487        else:
[4e1c362]488            try:
489                self._manager.plot_theory(name="High-Q extrapolation")
[1128bd31]490            except:
491                logging.error(sys.exc_value)
[4e1c362]492
[9ce7641c]493    def get_qstar(self, inv):
494        """
[0d417ac8]495        get qstar
[9ce7641c]496        """
497        qstar, qstar_err = inv.get_qstar_with_error()
498        self.inv_container.qstar = qstar
499        self.inv_container.qstar_err = qstar_err
[1128bd31]500
[9ce7641c]501    def set_extrapolation_low(self, inv, low_q=False):
502        """
[d7a39e5]503        return float value necessary to compute invariant a low q
[9ce7641c]504        """
505        #get funtion
506        if self.guinier.GetValue():
507            function_low = "guinier"
508        # get the function
[518d35d]509        power_low = None #2.0/3.0
[9ce7641c]510        if self.power_law_low.GetValue():
511            function_low = "power_law"
[518d35d]512            if self.fit_enable_low.GetValue():
[9ce7641c]513                #set value of power_low to none to allow fitting
514                power_low = None
515            else:
516                power_low = self.power_low_tcl.GetValue().lstrip().rstrip()
517                if check_float(self.power_low_tcl):
518                    power_low = float(power_low)
519                else:
[1128bd31]520                    if low_q:
[9ce7641c]521                        #Raise error only when qstar at low q is requested
[4a2b054]522                        msg = "Expect float for power at low q, "
523                        msg += " got %s" % (power_low)
[1128bd31]524                        wx.PostEvent(self.parent,
525                                     StatusEvent(status=msg,
526                                                 info="error",
527                                                 type="stop"))
528
[9ce7641c]529        #Get the number of points to extrapolated
[1128bd31]530        npts_low = self.npts_low_tcl.GetValue().lstrip().rstrip()
[9ce7641c]531        if check_float(self.npts_low_tcl):
532            npts_low = float(npts_low)
533        else:
534            if low_q:
[4a2b054]535                msg = "Expect float for number of points at low q,"
536                msg += " got %s" % (npts_low)
[1128bd31]537                wx.PostEvent(self.parent,
538                             StatusEvent(status=msg,
539                                         info="error",
540                                         type="stop"))
[9ce7641c]541        #Set the invariant calculator
542        inv.set_extrapolation(range="low", npts=npts_low,
[1128bd31]543                              function=function_low, power=power_low)
544        return inv, npts_low
545
[4e1c362]546
[9ce7641c]547    def set_extrapolation_high(self, inv, high_q=False):
548        """
[d7a39e5]549        return float value necessary to compute invariant a high q
[9ce7641c]550        """
551        power_high = None
[277fad8]552        #if self.power_law_high.GetValue():
553        function_high = "power_law"
554        if self.fit_enable_high.GetValue():
555            #set value of power_high to none to allow fitting
556            power_high = None
557        else:
558            power_high = self.power_high_tcl.GetValue().lstrip().rstrip()
559            if check_float(self.power_high_tcl):
560                power_high = float(power_high)
[9ce7641c]561            else:
[0d417ac8]562                if high_q:
[277fad8]563                    #Raise error only when qstar at high q is requested
[4a2b054]564                    msg = "Expect float for power at high q,"
565                    msg += " got %s" % (power_high)
[1128bd31]566                    wx.PostEvent(self.parent,
567                                 StatusEvent(status=msg,
568                                             info="error",
569                                             type="stop"))
570
571        npts_high = self.npts_high_tcl.GetValue().lstrip().rstrip()
[9ce7641c]572        if check_float(self.npts_high_tcl):
573            npts_high = float(npts_high)
574        else:
575            if high_q:
[4a2b054]576                msg = "Expect float for number of points at high q,"
577                msg += " got %s" % (npts_high)
[1128bd31]578                wx.PostEvent(self.parent, StatusEvent(status=msg,
579                                                      info="error",
580                                                      type="stop"))
[9ce7641c]581        inv.set_extrapolation(range="high", npts=npts_high,
[1128bd31]582                              function=function_high, power=power_high)
[9ce7641c]583        return inv, npts_high
[1128bd31]584
[9ce7641c]585    def display_details(self, event):
586        """
[d7a39e5]587        open another panel for more details on invariant calculation
[9ce7641c]588        """
[1128bd31]589        panel = InvariantDetailsPanel(parent=self,
590                                      qstar_container=self.inv_container)
[d3fac18]591        panel.ShowModal()
592        panel.Destroy()
[d0cc0bbc]593        self.button_calculate.SetFocus()
[1128bd31]594
[d0cc0bbc]595    def compute_invariant(self, event=None):
[9ce7641c]596        """
[1128bd31]597        compute invariant
[9ce7641c]598        """
[4e1c362]599        if self._data == None:
[4a2b054]600            msg = "\n\nData must be loaded first in order"
601            msg += " to perform a compution..."
[4e1c362]602            wx.PostEvent(self.parent, StatusEvent(status=msg))
603        # set a state for this computation for saving
[1128bd31]604        elif event != None:
[4e1c362]605            self._set_compute_state(state='compute')
[27f3831]606            self._set_bookmark_flag(True)
[1128bd31]607            msg = "\n\nStarting a new invariant computation..."
[4e1c362]608            wx.PostEvent(self.parent, StatusEvent(status=msg))
[1128bd31]609
[4e1c362]610
[cb69775]611        if self._data is None:
[9ce7641c]612            return
[cb69775]613        self.button_details.Enable()
[1128bd31]614        #clear outputs textctrl
[9ce7641c]615        self._reset_output()
616        try:
617            background = self.get_background()
618            scale = self.get_scale()
619        except:
[4a2b054]620            msg = "Invariant Error: %s" % (sys.exc_value)
621            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
[9ce7641c]622            return
[1128bd31]623
[9ce7641c]624        low_q = self.enable_low_cbox.GetValue()
[1128bd31]625        high_q = self.enable_high_cbox.GetValue()
[f1e06a8e]626        temp_data = copy.deepcopy(self._data)
[1128bd31]627
628        #set invariant calculator
[f1e06a8e]629        inv = invariant.InvariantCalculator(data=temp_data,
[9ce7641c]630                                            background=background,
631                                            scale=scale)
[437e639]632        try:
633            inv, npts_low = self.set_extrapolation_low(inv=inv, low_q=low_q)
634            inv, npts_high = self.set_extrapolation_high(inv=inv, high_q=high_q)
635        except:
[4a2b054]636            msg = "Error occurred computing invariant: %s" % sys.exc_value
[e3f721e4]637            wx.PostEvent(self.parent, StatusEvent(status=msg,
[1128bd31]638                                                  info="warning", type="stop"))
[437e639]639            return
[9ce7641c]640        #check the type of extrapolation
641        extrapolation = self.get_extrapolation_type(low_q=low_q, high_q=high_q)
[1128bd31]642
[9ce7641c]643        #Compute invariant
644        try:
645            self.get_qstar(inv=inv)
646        except:
[1128bd31]647            msg = "Error occurred computing invariant: %s" % sys.exc_value
648            wx.PostEvent(self.parent, StatusEvent(status=msg,
[4a2b054]649                                                  info="warning",
650                                                  type="stop"))
[c128284]651            return
[ae84427]652        #self.Show(False)
[07eb396]653        r_msg = ''
654        try:
655            r_msg = 'Low Q: '
[1128bd31]656            #Compute qstar extrapolated to low q range
[07eb396]657            self.get_low_qstar(inv=inv, npts_low=npts_low, low_q=low_q)
658            r_msg = 'High Q: '
[1128bd31]659            #Compute qstar extrapolated to high q range
[07eb396]660            self.get_high_qstar(inv=inv, high_q=high_q)
661            r_msg = ''
[1128bd31]662            #Compute qstar extrapolated to total q range
[07eb396]663            #and set value to txtcrtl
664            self.get_total_qstar(inv=inv, extrapolation=extrapolation)
665            # Parse additional parameters
[1128bd31]666            porod_const = self.get_porod_const()
[07eb396]667            contrast = self.get_contrast()
668        except:
669            msg = r_msg + "Error occurred computing invariant: %s" % \
670                                                            sys.exc_value
[1128bd31]671            wx.PostEvent(self.parent, StatusEvent(status=msg,
[07eb396]672                                                  info="error",
673                                                  type="stop"))
[f43827cc]674        try:
675            #Compute volume and set value to txtcrtl
[4a2b054]676            self.get_volume(inv=inv, contrast=contrast,
677                            extrapolation=extrapolation)
[f43827cc]678            #compute surface and set value to txtcrtl
679        except:
[4a2b054]680            msg = "Error occurred computing invariant: %s" % sys.exc_value
[e3f721e4]681            wx.PostEvent(self.parent, StatusEvent(status=msg,
[4a2b054]682                                                  info="warning",
683                                                  type="stop"))
[f43827cc]684        try:
[4a2b054]685            self.get_surface(inv=inv, contrast=contrast,
[1128bd31]686                             porod_const=porod_const,
687                             extrapolation=extrapolation)
688
[f43827cc]689        except:
[4a2b054]690            msg = "Error occurred computing invariant: %s" % sys.exc_value
[e3f721e4]691            wx.PostEvent(self.parent, StatusEvent(status=msg,
[4a2b054]692                                                  info="warning",
693                                                  type="stop"))
[1128bd31]694
[d0cc0bbc]695        #compute percentage of each invariant
696        self.inv_container.compute_percentage()
[1128bd31]697
[d0cc0bbc]698        #display a message
699        self.set_message()
[645f9b6]700
[4e1c362]701        # reset power_out to default to get ready for another '_on_text'
702        if self.is_power_out == True:
703            self.state.container = copy.deepcopy(self.inv_container)
[699df7f]704            self.state.timestamp = self._get_time_stamp()
[4e1c362]705            msg = self.state.__str__()
[cefb3fb]706            self.state.set_report_string()
[4e1c362]707            self.is_power_out = False
[1128bd31]708            wx.PostEvent(self.parent, StatusEvent(status=msg))
[cef847c]709
[9ce7641c]710        #enable the button_ok for more details
[cb69775]711        self._set_preview_flag(True)
[1128bd31]712
713        if event != None:
[cb69775]714            self._set_preview_flag(True)
715            self._set_save_flag(True)
[4a2b054]716            wx.PostEvent(self.parent,
[1128bd31]717                         StatusEvent(status='\nFinished invariant computation...'))
[ae84427]718        #self.Show(True)
[ee54fcf0]719        self.Refresh()
[1128bd31]720
[0d417ac8]721    def on_undo(self, event=None):
[4e1c362]722        """
723        Go back to the previous state
[1128bd31]724
[4e1c362]725        : param event: undo button event
726        """
[4a2b054]727        if self.state.state_num < 0:
728            return
[4e1c362]729        self.is_power_out = True
730        # get the previous state_num
731        pre_state_num = int(self.state.saved_state['state_num']) - 1
[d4d78c9]732
[4e1c362]733        self.get_state_by_num(state_num=str(pre_state_num))
[1128bd31]734
[4a2b054]735        if float(pre_state_num) <= 0:
[27f3831]736            self._set_undo_flag(False)
[cb274d9e]737        else:
[27f3831]738            self._set_undo_flag(True)
739        self._set_redo_flag(True)
[1128bd31]740        self.is_power_out = False
[4e1c362]741        self._info_state_num()
[d4d78c9]742
[1128bd31]743
[0d417ac8]744    def on_redo(self, event=None):
[4e1c362]745        """
746        Go forward to the previous state
[1128bd31]747
[4e1c362]748        : param event: redo button event
749        """
750        self.is_power_out = True
[cb274d9e]751        # get the next state_num
[4e1c362]752        next_state_num = int(self.state.saved_state['state_num']) + 1
753
754        self.get_state_by_num(state_num=str(next_state_num))
[1128bd31]755
756        if float(next_state_num) + 2 > len(self.state.state_list):
[27f3831]757            self._set_redo_flag(False)
[178bfea]758        else:
[cb69775]759            self._set_redo_flag(True)
[1128bd31]760
[27f3831]761        self._set_undo_flag(True)
[4e1c362]762        self.is_power_out = False
763        self._info_state_num()
[1128bd31]764
[27f3831]765    def on_preview(self, event=None):
[cb463b4]766        """
767        Invoke report dialog panel
[1128bd31]768
[cb463b4]769        : param event: report button event
770        """
[b9a5f0e]771        from sas.perspectives.invariant.report_dialog import ReportDialog
[cb463b4]772
[a94c4e1]773        self.state.set_report_string()
[cb274d9e]774        report_html_str = self.state.report_str
775        report_text_str = self.state.__str__()
776        report_img = self.state.image
[0d417ac8]777        report_list = [report_html_str, report_text_str, report_img]
[cb274d9e]778        dialog = ReportDialog(report_list, None, -1, "")
[d838715]779        dialog.Show()
[1128bd31]780
[0d417ac8]781    def get_state_by_num(self, state_num=None):
[4e1c362]782        """
783        Get the state given by number
[1128bd31]784
[4e1c362]785        : param state_num: the given state number
[1128bd31]786        """
[4e1c362]787        if state_num == None:
788            return
789
790        backup_state_list = copy.deepcopy(self.state.state_list)
[1128bd31]791
[4e1c362]792        # get the previous state
793        try:
794            current_state = copy.deepcopy(self.state.state_list[str(state_num)])
[1128bd31]795            # get the previously computed state number
[4a2b054]796            #(computation before the state changes happened)
[4e1c362]797            current_compute_num = str(current_state['compute_num'])
[1128bd31]798        except:
[3641881]799            raise
[1128bd31]800
[4e1c362]801        # get the state at pre_compute_num
802        comp_state = copy.deepcopy(self.state.state_list[current_compute_num])
803
804        # set the parameters
805        for key in comp_state:
806            value = comp_state[key]
[c4f6851]807            self._set_property_value(key, value)
[1128bd31]808
[4e1c362]809        self.compute_invariant(event=None)
[1128bd31]810
[4e1c362]811        # set the input params at the state at pre_state_num
812        for key in current_state:
813            # set the inputs and boxes
814            value = current_state[key]
[c4f6851]815            self._set_property_value(key, value)
[4e1c362]816
817        self._enable_high_q_section(event=None)
818        self._enable_low_q_section(event=None)
819        self.state.state_list = backup_state_list
820        self.state.saved_state = current_state
821        self.state.state_num = state_num
[d4d78c9]822
[c4f6851]823    def _set_property_value(self, key, value):
824        """
825            Set a property value
826            :param key: property name
827            :param value: value of the property
828        """
829        try:
830            attr = getattr(self, key)
[0e33a8d]831            if attr.__class__.__name__ == "StaticText":
832                return
833            if type(value) is not bool:
834                value = str(value)
835            attr.SetValue(value)
[c4f6851]836        except:
837            logging.error("Invariant state: %s", sys.exc_value)
[1128bd31]838
[4e1c362]839    def get_bookmark_by_num(self, num=None):
840        """
841        Get the bookmark state given by number
[1128bd31]842
[4e1c362]843        : param num: the given bookmark number
[1128bd31]844
[4e1c362]845        """
846        current_state = {}
847        comp_state = {}
848        backup_state_list = copy.deepcopy(self.state.state_list)
849
850        # get the previous state
851        try:
[1128bd31]852            _, _, current_state, comp_state = self.state.bookmark_list[int(num)]
853        except:
854            logging.error(sys.exc_value)
855            raise ValueError, "No such bookmark exists"
[4e1c362]856
857        # set the parameters
858        for key in comp_state:
859            value = comp_state[key]
[c4f6851]860            self._set_property_value(key, value)
[4e1c362]861
862        self.compute_invariant(event=None)
863        # set the input params at the state of pre_state_num
864        for key in current_state:
865            value = current_state[key]
[c4f6851]866            self._set_property_value(key, value)
[4e1c362]867        self.state.saved_state = copy.deepcopy(current_state)
[1128bd31]868
[4e1c362]869        self._enable_high_q_section(event=None)
870        self._enable_low_q_section(event=None)
871        self.state.state_list = backup_state_list
872        #self.state.saved_state = current_state
873        #self.state.state_num = state_num
874
[f338d3b]875    def reset_panel(self):
876        """
[d7a39e5]877        set the panel at its initial state.
[f338d3b]878        """
[d0cc0bbc]879        self.background_tcl.SetValue(str(BACKGROUND))
[1128bd31]880        self.scale_tcl.SetValue(str(SCALE))
[4e74e13]881        self.contrast_tcl.SetValue(str(CONTRAST))
[1128bd31]882        self.porod_constant_tcl.SetValue('')
[d0cc0bbc]883        self.npts_low_tcl.SetValue(str(NPTS))
884        self.enable_low_cbox.SetValue(False)
885        self.fix_enable_low.SetValue(True)
886        self.power_low_tcl.SetValue(str(POWER))
887        self.guinier.SetValue(True)
888        self.power_low_tcl.Disable()
889        self.enable_high_cbox.SetValue(False)
890        self.fix_enable_high.SetValue(True)
891        self.power_high_tcl.SetValue(str(POWER))
892        self.npts_high_tcl.SetValue(str(NPTS))
893        self.button_details.Disable()
894        #Change the state of txtcrtl to enable/disable
895        self._enable_low_q_section()
896        #Change the state of txtcrtl to enable/disable
897        self._enable_high_q_section()
898        self._reset_output()
[27f3831]899        self._set_undo_flag(False)
900        self._set_redo_flag(False)
901        self._set_bookmark_flag(False)
902        self._set_preview_flag(False)
903        self._set_save_flag(False)
[d0cc0bbc]904        self.button_calculate.SetFocus()
[4e1c362]905        #self.SetupScrolling()
[1128bd31]906
[4e1c362]907    def _set_state(self, event):
908        """
909        Set the state list
[1128bd31]910
[4e1c362]911        :param event: rb/cb event
912        """
913        if event == None:
914            return
915        obj = event.GetEventObject()
916        name = str(obj.GetName())
917        value = str(obj.GetValue())
[0d417ac8]918        rb_list = [['power_law_low', 'guinier'],
919                   ['fit_enable_low', 'fix_enable_low'],
920                   ['fit_enable_high', 'fix_enable_high']]
[4e1c362]921
922        try:
[1128bd31]923            if value == None or value.lstrip().rstrip() == '':
[4e1c362]924                value = 'None'
[c4f6851]925            setattr(self.state, name, str(value))
926            self.state.saved_state[name] = str(value)
[1128bd31]927
928            # set the count part of radio button clicked
[4a2b054]929            #False for the saved_state
[699df7f]930            for title, content in rb_list:
[1128bd31]931                if name == title:
932                    name = content
933                    value = False
[4e1c362]934                elif name == content:
935                    name = title
[1128bd31]936                    value = False
[c4f6851]937            self.state.saved_state[name] = str(value)
[1128bd31]938
[4e1c362]939            # Instead of changing the future, create a new future.
[1128bd31]940            max_state_num = len(self.state.state_list) - 1
941            self.state.saved_state['state_num'] = max_state_num
942
[4a2b054]943            self.state.saved_state['state_num'] += 1
[4e1c362]944            self.state.state_num = self.state.saved_state['state_num']
[0d417ac8]945            self.state.state_list[str(self.state.state_num)] = \
946                    self.state.clone_state()
[1128bd31]947        except:
948            logging.error(sys.exc_value)
949
[27f3831]950        self._set_undo_flag(True)
951        self._set_redo_flag(False)
[d4d78c9]952        #event.Skip()
[1128bd31]953
[0d417ac8]954    def _set_compute_state(self, state=None):
[4e1c362]955        """
956        Notify the compute_invariant state to self.state
[1128bd31]957
[4a2b054]958        : param state: set 'compute' when the computation is
959        activated by the 'compute' button, else None
[1128bd31]960
[4e1c362]961        """
962        # reset the default
963        if state != 'compute':
964            self.new_state = False
965            self.is_power_out = False
966        else:
967            self.is_power_out = True
968        # Instead of changing the future, create a new future.
[1128bd31]969        max_state_num = len(self.state.state_list) - 1
970        self.state.saved_state['state_num'] = max_state_num
[4e1c362]971        # A new computation is also A state
[4a2b054]972        #copy.deepcopy(self.state.saved_state)
973        temp_saved_states = self.state.clone_state()
974        temp_saved_states['state_num'] += 1
[4e1c362]975        self.state.state_num = temp_saved_states['state_num']
976
[1128bd31]977
978        # set the state number of the computation
[4e1c362]979        if state == 'compute':
980            temp_saved_states['compute_num'] = self.state.state_num
[699df7f]981        self.state.saved_state = copy.deepcopy(temp_saved_states)
[4a2b054]982        #copy.deepcopy(self.state.saved_state)
[0d417ac8]983        self.state.state_list[str(self.state.state_num)] = \
984                                        self.state.clone_state()
[1128bd31]985
[4a2b054]986        # A computation is a new state, so delete the states with any higher
987        # state numbers
[1128bd31]988        for i in range(self.state.state_num + 1, len(self.state.state_list)):
[4e1c362]989            try:
[1128bd31]990                del self.state.state_list[str(i)]
991            except:
992                logging.error(sys.exc_value)
[4e1c362]993        # Enable the undo button if it was not
[27f3831]994        self._set_undo_flag(True)
995        self._set_redo_flag(False)
[1128bd31]996
[4a2b054]997    def _reset_state_list(self, data=None):
[4e1c362]998        """
[4a2b054]999        Reset the state_list just before data was loading:
1000        Used in 'set_current_data()'
[4e1c362]1001        """
1002        #if data == None: return
[4a2b054]1003        #temp_state = self.state.clone_state()
1004        #copy.deepcopy(self.state.saved_state)
[1128bd31]1005        # Clear the list
[4e1c362]1006        self.state.state_list.clear()
1007        self.state.bookmark_list.clear()
1008        # Set defaults
1009        self.state.saved_state['state_num'] = 0
1010        self.state.saved_state['compute_num'] = 0
1011        if self._data != None:
1012            self.state.saved_state['file'] = str(self._data.name)
1013        else:
1014            self.state.saved_state['file'] = 'None'
1015        self.state.file = self.state.saved_state['file']
1016
1017        self.state.state_num = self.state.saved_state['state_num']
[cb463b4]1018        self.state.timestamp = "('00:00:00', '00/00/0000')"
1019
[4e1c362]1020        # Put only the current state in the list
[4a2b054]1021        #copy.deepcopy(self.state.saved_state)
[699df7f]1022        self.state.state_list[str(self.state.state_num)] = \
1023                                                self.state.clone_state()
[cb69775]1024        self._set_undo_flag(False)
[be738e3]1025        self._set_redo_flag(False)
1026        self._set_bookmark_flag(False)
1027        self._set_preview_flag(False)
1028        self._set_save_flag(False)
1029
[1128bd31]1030
[4e1c362]1031    def _on_text(self, event):
1032        """
1033        Catch text change event to add the state to the state_list
[1128bd31]1034
[4e1c362]1035        :param event: txtctr event ; assumes not None
[1128bd31]1036
[4e1c362]1037        """
[1128bd31]1038        if self._data == None:
[4e1c362]1039            return
1040        # check if this event is from do/undo button
1041        if self.state.saved_state['is_time_machine'] or self.new_state:
[d4d78c9]1042            #event.Skip()
[4e1c362]1043            return
[1128bd31]1044
[4e1c362]1045        # get the object
1046        obj = event.GetEventObject()
1047        name = str(obj.GetName())
1048        value = str(obj.GetValue())
1049        state_num = self.state.saved_state['state_num']
1050
1051        # text event is a new state, so delete the states with higher state_num
1052        # i.e., change the future
[1128bd31]1053        for i in range(int(state_num) + 1, len(self.state.state_list)):
[4e1c362]1054            try:
[1128bd31]1055                del self.state.state_list[str(i)]
1056            except:
1057                logging.error(sys.exc_value)
1058
[4e1c362]1059        # try to add new state of the text changes in the state_list
1060        try:
[1128bd31]1061            if value.strip() == None:
[699df7f]1062                value = ''
[c4f6851]1063            setattr(self.state, name, str(value))
1064            self.state.saved_state[name] = str(value)
1065            self.state.input_list[name] = str(value)
[4e1c362]1066            if not self.is_power_out:
[699df7f]1067                if name != 'power_low_tcl' and name != 'power_high_tcl':
[4e1c362]1068                    self.state.saved_state['state_num'] += 1
1069            self.state.state_num = self.state.saved_state['state_num']
[c4f6851]1070            self.state.state_list[str(self.state.state_num)] = self.state.clone_state()
[4e1c362]1071        except:
[1128bd31]1072            logging.error(sys.exc_value)
[4e1c362]1073
[27f3831]1074        self._set_undo_flag(True)
1075        self._set_redo_flag(False)
1076        self._set_bookmark_flag(True)
1077        self._set_preview_flag(False)
[1128bd31]1078
1079    def _on_out_text(self, event):
[4e1c362]1080        """
[1128bd31]1081        Catch ouput text change to add the state
1082
[4e1c362]1083        :param event: txtctr event ; assumes not None
[1128bd31]1084
1085        """
[4e1c362]1086        # get the object
1087        obj = event.GetEventObject()
1088        name = str(obj.GetName())
1089        value = str(obj.GetValue())
1090        try:
[c4f6851]1091            self.state.saved_state[name] = str(value)
1092            self.state.state_list[str(self.state.state_num)] = self.state.clone_state()
[4e1c362]1093        except:
[1128bd31]1094            logging.error(sys.exc_value)
[1a3a03b]1095
[1128bd31]1096    def _get_input_list(self):
[1a3a03b]1097        """
1098        get input_list; called by set_state
[1128bd31]1099        """
[1a3a03b]1100        # get state num of the last compute state
1101        compute_num = self.state.saved_state['compute_num']
1102        # find values and put into the input list
[1128bd31]1103        for key1, value1 in self.state.state_list[str(compute_num)].iteritems():
1104            for key, _ in self.state.input_list.iteritems():
[1a3a03b]1105                if key == key1:
[699df7f]1106                    self.state.input_list[key] = value1
[1a3a03b]1107                    break
[1128bd31]1108
[4e1c362]1109    def _set_bookmark_menu(self):
1110        """
1111        Setup 'bookmark' context menu
1112        """
1113        ## Create context menu for page
1114        self.popUpMenu = wx.Menu()
1115        id = wx.NewId()
[699df7f]1116        self._bmark = wx.MenuItem(self.popUpMenu, id, "BookMark",
[4a2b054]1117                                  " Bookmark the panel to recall it later")
[4e1c362]1118        self.popUpMenu.AppendItem(self._bmark)
1119        self._bmark.Enable(True)
[27f3831]1120        wx.EVT_MENU(self, id, self.on_bookmark)
[4e1c362]1121        self.popUpMenu.AppendSeparator()
1122        self.Bind(wx.EVT_CONTEXT_MENU, self._on_context_menu)
[1128bd31]1123
[0d417ac8]1124    def on_bookmark(self, event):
[4e1c362]1125        """
[4a2b054]1126        Save the panel state in memory and add the list on
1127        the popup menu on bookmark context menu event
[1128bd31]1128        """
1129        if self._data == None:
[699df7f]1130            return
[1128bd31]1131        if event == None:
[699df7f]1132            return
[4e1c362]1133        self.bookmark_num += 1
1134        # date and time of the event
1135        my_time, date = self._get_time_stamp()
[699df7f]1136        _ = self.state.state_num
[4e1c362]1137        compute_num = self.state.saved_state['compute_num']
1138        # name and message of the bookmark list
[1128bd31]1139        msg = "State saved at %s on %s" % (my_time, date)
1140        ## post help message for the selected model
[699df7f]1141        msg += " Right click on the panel to retrieve this state"
[4e1c362]1142        #wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
[1128bd31]1143        name = "%d] bookmarked at %s on %s" % (self.bookmark_num, my_time, date)
1144
[4e1c362]1145        # append it to menu
[cefb3fb]1146        id = wx.NewId()
[0d417ac8]1147        self.popUpMenu.Append(id, name, str(msg))
[cefb3fb]1148        wx.EVT_MENU(self, id, self._back_to_bookmark)
[4e1c362]1149        state = self.state.clone_state()
1150        comp_state = copy.deepcopy(self.state.state_list[str(compute_num)])
[4a2b054]1151        self.state.bookmark_list[self.bookmark_num] = [my_time, date,
[1128bd31]1152                                                       state, comp_state]
[4e1c362]1153        self.state.toXML(self, doc=None, entry_node=None)
[1128bd31]1154
[0d417ac8]1155        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info"))
[1128bd31]1156        wx.PostEvent(self.parent,
1157                     AppendBookmarkEvent(title=name,
[cb69775]1158                                         hint=str(msg),
[1128bd31]1159                                         handler=self._back_to_bookmark))
[4e1c362]1160
[0d417ac8]1161    def _back_to_bookmark(self, event):
[4e1c362]1162        """
[4a2b054]1163        Bring the panel back to the state of bookmarked requested by
1164        context menu event
[4e1c362]1165        and set it as a new state
1166        """
[cb69775]1167        self._manager.on_perspective(event)
1168        menu = event.GetEventObject()
[1128bd31]1169        ## post help message for the selected model
[cb69775]1170        msg = menu.GetHelpString(event.GetId())
[699df7f]1171        msg += " reloaded"
[1128bd31]1172        wx.PostEvent(self.parent, StatusEvent(status=msg))
1173
[699df7f]1174        name = menu.GetLabel(event.GetId())
[1128bd31]1175
1176        num, _ = name.split(']')
1177        current_state_num = self.state.state_num
[4e1c362]1178        self.get_bookmark_by_num(num)
[0d417ac8]1179        state_num = int(current_state_num) + 1
[1128bd31]1180
[4e1c362]1181        self.state.saved_state['state_num'] = state_num
[4a2b054]1182        #copy.deepcopy(self.state.saved_state)
1183        self.state.state_list[str(state_num)] = self.state.clone_state()
[4e1c362]1184        self.state.state_num = state_num
[1128bd31]1185
[27f3831]1186        self._set_undo_flag(True)
[4e1c362]1187        self._info_bookmark_num(event)
[1128bd31]1188
[0d417ac8]1189    def _info_bookmark_num(self, event=None):
[4e1c362]1190        """
1191        print the bookmark number in info
[1128bd31]1192
[4e1c362]1193        : event: popUpMenu event
1194        """
[1128bd31]1195        if event == None:
[699df7f]1196            return
[4e1c362]1197        # get the object
[cb69775]1198        menu = event.GetEventObject()
1199        item = menu.FindItemById(event.GetId())
[4e1c362]1200        text = item.GetText()
1201        num = text.split(']')[0]
[1128bd31]1202        msg = "bookmark num = %s " % num
1203
1204        wx.PostEvent(self.parent, StatusEvent(status=msg))
1205
[4e1c362]1206    def _info_state_num(self):
1207        """
1208        print the current state number in info
1209        """
1210        msg = "state num = "
1211        msg += self.state.state_num
[1128bd31]1212
1213        wx.PostEvent(self.parent, StatusEvent(status=msg))
1214
[4e1c362]1215    def _get_time_stamp(self):
1216        """
1217        return time and date stings
1218        """
[1128bd31]1219        # date and time
[699df7f]1220        year, month, day, hour, minute, second, _, _, _ = \
[0d417ac8]1221                                    time.localtime()
1222        my_time = str(hour) + ":" + str(minute) + ":" + str(second)
[1128bd31]1223        date = str(month) + "/" + str(day) + "/" + str(year)
[4e1c362]1224        return my_time, date
[1128bd31]1225
1226
1227    def on_save(self, evt=None):
[4e1c362]1228        """
1229        Save invariant state into a file
1230        """
1231        # Ask the user the location of the file to write to.
1232        path = None
[c4ae1c2]1233        if self.parent != None:
[699df7f]1234            self._default_save_location = self.parent.get_save_location()
[c4ae1c2]1235        if self._default_save_location == None:
1236            self._default_save_location = os.getcwd()
[4a2b054]1237        dlg = wx.FileDialog(self, "Choose a file",
[c4ae1c2]1238                            self._default_save_location, \
1239                            self.window_caption, "*.inv", wx.SAVE)
[4e1c362]1240        if dlg.ShowModal() == wx.ID_OK:
1241            path = dlg.GetPath()
1242            self._default_save_location = os.path.dirname(path)
[c4ae1c2]1243            if self.parent != None:
[0d417ac8]1244                self.parent._default_save_location = \
[c4ae1c2]1245                    self._default_save_location
[4e1c362]1246        else:
1247            return None
[1128bd31]1248
[4e1c362]1249        dlg.Destroy()
[23cdeab]1250        # MAC always needs the extension for saving
1251        extens = ".inv"
1252        # Make sure the ext included in the file name
1253        fName = os.path.splitext(path)[0] + extens
1254        self._manager.save_file(filepath=fName, state=self.state)
[1128bd31]1255
[0d417ac8]1256    def _show_message(self, mssg='', msg='Warning'):
[4e1c362]1257        """
1258        Show warning message when resetting data
1259        """
[f24925ab]1260        # no message for now
1261        return True
[1128bd31]1262
[c128284]1263    def _reset_output(self):
1264        """
[d7a39e5]1265        clear outputs textcrtl
[c128284]1266        """
[9ce7641c]1267        self.invariant_total_tcl.Clear()
1268        self.invariant_total_err_tcl.Clear()
1269        self.volume_tcl.Clear()
1270        self.volume_err_tcl.Clear()
1271        self.surface_tcl.Clear()
1272        self.surface_err_tcl.Clear()
[a0a4486]1273        #prepare a new container to put result of invariant
1274        self.inv_container = InvariantContainer()
[4e1c362]1275
[1128bd31]1276
[0d417ac8]1277    def _on_context_menu(self, event):
[699df7f]1278        """
1279        On context menu
1280        """
[4e1c362]1281        pos = event.GetPosition()
1282        pos = self.ScreenToClient(pos)
[1128bd31]1283
1284        self.PopupMenu(self.popUpMenu, pos)
1285
[9ce7641c]1286    def _define_structure(self):
[c128284]1287        """
[d7a39e5]1288        Define main sizers needed for this panel
[c128284]1289        """
[1128bd31]1290        ## Box sizers must be defined first before
[4a2b054]1291        #defining buttons/textctrls (MAC).
[9ce7641c]1292        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
1293        #Sizer related to outputs
1294        outputs_box = wx.StaticBox(self, -1, "Outputs")
1295        self.outputs_sizer = wx.StaticBoxSizer(outputs_box, wx.VERTICAL)
[699df7f]1296        self.outputs_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[9ce7641c]1297        #Sizer related to data
1298        data_name_box = wx.StaticBox(self, -1, "I(q) Data Source")
1299        self.data_name_boxsizer = wx.StaticBoxSizer(data_name_box, wx.VERTICAL)
[1128bd31]1300        self.data_name_boxsizer.SetMinSize((_STATICBOX_WIDTH, -1))
[9ce7641c]1301        self.hint_msg_sizer = wx.BoxSizer(wx.HORIZONTAL)
[210ff4f]1302        self.data_name_sizer = wx.BoxSizer(wx.HORIZONTAL)
[1128bd31]1303
[9ce7641c]1304        self.data_range_sizer = wx.BoxSizer(wx.HORIZONTAL)
[7d16278]1305        #Sizer related to inputs
[1128bd31]1306        self.sizer_input = wx.FlexGridSizer(2, 6, 0, 0)
[d0cc0bbc]1307        #Sizer related to inputs
1308        inputs_box = wx.StaticBox(self, -1, "Customized Inputs")
1309        self.inputs_sizer = wx.StaticBoxSizer(inputs_box, wx.VERTICAL)
[699df7f]1310        self.inputs_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[d0cc0bbc]1311        #Sizer related to extrapolation
[d5f0dcb9]1312        extrapolation_box = wx.StaticBox(self, -1, "Extrapolation")
[9ce7641c]1313        self.extrapolation_sizer = wx.StaticBoxSizer(extrapolation_box,
[1128bd31]1314                                                     wx.VERTICAL)
[699df7f]1315        self.extrapolation_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[9ce7641c]1316        self.extrapolation_range_sizer = wx.BoxSizer(wx.HORIZONTAL)
1317        self.extrapolation_low_high_sizer = wx.BoxSizer(wx.HORIZONTAL)
1318        #Sizer related to extrapolation at low q range
[d5f0dcb9]1319        low_q_box = wx.StaticBox(self, -1, "Low Q")
[9ce7641c]1320        self.low_extrapolation_sizer = wx.StaticBoxSizer(low_q_box, wx.VERTICAL)
[1128bd31]1321
[699df7f]1322        self.low_q_sizer = wx.GridBagSizer(5, 5)
[9ce7641c]1323        #Sizer related to extrapolation at low q range
1324        high_q_box = wx.StaticBox(self, -1, "High Q")
[4a2b054]1325        self.high_extrapolation_sizer = wx.StaticBoxSizer(high_q_box,
1326                                                          wx.VERTICAL)
[0d417ac8]1327        self.high_q_sizer = wx.GridBagSizer(5, 5)
[9ce7641c]1328        #sizer to define outputs
[0d417ac8]1329        self.volume_surface_sizer = wx.GridBagSizer(5, 5)
[9ce7641c]1330        #Sizer related to invariant output
[277fad8]1331        self.invariant_sizer = wx.GridBagSizer(5, 5)
[9ce7641c]1332        #Sizer related to button
1333        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
[0d417ac8]1334        self.button_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[4e1c362]1335        #Sizer related to save button
1336        self.save_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
[1128bd31]1337
[9ce7641c]1338    def _layout_data_name(self):
1339        """
[d7a39e5]1340        Draw widgets related to data's name
[9ce7641c]1341        """
[1128bd31]1342        #Sizer hint
[cb69775]1343        hint_msg = ""
[1128bd31]1344
1345        self.hint_msg_txt = wx.StaticText(self, -1, hint_msg)
[277fad8]1346        self.hint_msg_txt.SetForegroundColour("red")
[4a2b054]1347        msg = "Highlight = mouse the mouse's cursor on the data until"
1348        msg += " the plot's color changes to yellow"
[6848131]1349        self.hint_msg_txt.SetToolTipString(msg)
[277fad8]1350        self.hint_msg_sizer.Add(self.hint_msg_txt)
[210ff4f]1351        #Data name [string]
[1128bd31]1352        data_name_txt = wx.StaticText(self, -1, 'Name:')
1353
1354        self.data_name_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH * 4, 20),
1355                                            style=0)
[210ff4f]1356        self.data_name_tcl.SetToolTipString("Data's name.")
[1128bd31]1357        self.data_name_sizer.AddMany([(data_name_txt, 0, wx.LEFT | wx.RIGHT, 10),
1358                                      (self.data_name_tcl, 0, wx.EXPAND)])
[9ce7641c]1359        #Data range [string]
[1128bd31]1360        data_range_txt = wx.StaticText(self, -1, 'Total Q Range (1/A): ')
1361        data_min_txt = wx.StaticText(self, -1, 'Min : ')
[4a2b054]1362        self.data_min_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1363                                           style=0, name='data_min_tcl')
[9ce7641c]1364        self.data_min_tcl.SetToolTipString("The minimum value of q range.")
[1128bd31]1365        data_max_txt = wx.StaticText(self, -1, 'Max : ')
[4a2b054]1366        self.data_max_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
[1128bd31]1367                                           style=0, name='data_max_tcl')
[9ce7641c]1368        self.data_max_tcl.SetToolTipString("The maximum value of q range.")
[7d16278]1369        self.data_range_sizer.AddMany([(data_range_txt, 0, wx.RIGHT, 5),
1370                                       (data_min_txt, 0, wx.RIGHT, 5),
[eaab2ad]1371                                       (self.data_min_tcl, 0, wx.RIGHT, 20),
[7d16278]1372                                       (data_max_txt, 0, wx.RIGHT, 5),
[eaab2ad]1373                                       (self.data_max_tcl, 0, wx.RIGHT, 10)])
[1128bd31]1374        self.data_name_boxsizer.AddMany([(self.hint_msg_sizer, 0, wx.ALL, 5),
1375                                         (self.data_name_sizer, 0, wx.ALL, 10),
1376                                         (self.data_range_sizer, 0, wx.ALL, 10)])
1377
[518d35d]1378    def _enable_fit_power_law_low(self, event=None):
1379        """
[d7a39e5]1380        Enable and disable the power value editing
[518d35d]1381        """
[1128bd31]1382        if event != None:
[27f3831]1383            self._set_bookmark_flag(True)
1384            self._set_preview_flag(False)
[1128bd31]1385
[518d35d]1386        if self.fix_enable_low.IsEnabled():
[1128bd31]1387
[518d35d]1388            if self.fix_enable_low.GetValue():
[4e1c362]1389                self.fit_enable_low.SetValue(False)
[518d35d]1390                self.power_low_tcl.Enable()
1391            else:
[4e1c362]1392                self.fit_enable_low.SetValue(True)
[518d35d]1393                self.power_low_tcl.Disable()
[4e1c362]1394        self._set_state(event=event)
[1128bd31]1395
[518d35d]1396    def _enable_low_q_section(self, event=None):
1397        """
[d7a39e5]1398        Disable or enable some button if the user enable low q extrapolation
[518d35d]1399        """
[1128bd31]1400        if event != None:
[27f3831]1401            self._set_bookmark_flag(True)
1402            self._set_preview_flag(False)
[1128bd31]1403
[518d35d]1404        if self.enable_low_cbox.GetValue():
1405            self.npts_low_tcl.Enable()
1406            self.fix_enable_low.Enable()
1407            self.fit_enable_low.Enable()
1408            self.guinier.Enable()
1409            self.power_law_low.Enable()
1410
1411        else:
1412            self.npts_low_tcl.Disable()
1413            self.fix_enable_low.Disable()
1414            self.fit_enable_low.Disable()
1415            self.guinier.Disable()
1416            self.power_law_low.Disable()
[1128bd31]1417
[518d35d]1418        self._enable_power_law_low()
1419        self._enable_fit_power_law_low()
[4e1c362]1420        self._set_state(event=event)
[d0cc0bbc]1421        self.button_calculate.SetFocus()
[1128bd31]1422
[518d35d]1423    def _enable_power_law_low(self, event=None):
1424        """
[d7a39e5]1425        Enable editing power law section at low q range
[518d35d]1426        """
[1128bd31]1427        if event != None:
[27f3831]1428            self._set_bookmark_flag(True)
1429            self._set_preview_flag(False)
[518d35d]1430        if self.guinier.GetValue():
[4e1c362]1431            self.power_law_low.SetValue(False)
[518d35d]1432            self.fix_enable_low.Disable()
1433            self.fit_enable_low.Disable()
1434            self.power_low_tcl.Disable()
1435        else:
[4e1c362]1436            self.power_law_low.SetValue(True)
[518d35d]1437            self.fix_enable_low.Enable()
1438            self.fit_enable_low.Enable()
1439            self.power_low_tcl.Enable()
1440        self._enable_fit_power_law_low()
[4e1c362]1441        self._set_state(event=event)
[1128bd31]1442
[9ce7641c]1443    def _layout_extrapolation_low(self):
1444        """
[d7a39e5]1445        Draw widgets related to extrapolation at low q range
[9ce7641c]1446        """
[4a2b054]1447        self.enable_low_cbox = wx.CheckBox(self, -1,
1448                                           "Enable Extrapolate Low Q",
1449                                           name='enable_low_cbox')
[518d35d]1450        wx.EVT_CHECKBOX(self, self.enable_low_cbox.GetId(),
[1128bd31]1451                        self._enable_low_q_section)
[518d35d]1452        self.fix_enable_low = wx.RadioButton(self, -1, 'Fix',
[1128bd31]1453                                             (10, 10), style=wx.RB_GROUP,
1454                                             name='fix_enable_low')
[518d35d]1455        self.Bind(wx.EVT_RADIOBUTTON, self._enable_fit_power_law_low,
[1128bd31]1456                  id=self.fix_enable_low.GetId())
[4a2b054]1457        self.fit_enable_low = wx.RadioButton(self, -1, 'Fit', (10, 10),
1458                                             name='fit_enable_low')
[1128bd31]1459        self.Bind(wx.EVT_RADIOBUTTON, self._enable_fit_power_law_low,
1460                  id=self.fit_enable_low.GetId())
[c128284]1461        self.guinier = wx.RadioButton(self, -1, 'Guinier',
[1128bd31]1462                                      (10, 10), style=wx.RB_GROUP,
1463                                      name='guinier')
[518d35d]1464        self.Bind(wx.EVT_RADIOBUTTON, self._enable_power_law_low,
[1128bd31]1465                  id=self.guinier.GetId())
[4a2b054]1466        self.power_law_low = wx.RadioButton(self, -1, 'Power Law',
1467                                            (10, 10), name='power_law_low')
[1128bd31]1468        self.Bind(wx.EVT_RADIOBUTTON, self._enable_power_law_low,
1469                  id=self.power_law_low.GetId())
1470
[c128284]1471        npts_low_txt = wx.StaticText(self, -1, 'Npts')
[4a2b054]1472        self.npts_low_tcl = InvTextCtrl(self, -1,
[1128bd31]1473                                        size=(_BOX_WIDTH * 2 / 3, -1),
[4a2b054]1474                                        name='npts_low_tcl')
[4e1c362]1475        wx.EVT_TEXT(self, self.npts_low_tcl.GetId(), self._on_text)
[2661d8b]1476        msg_hint = "Number of Q points to consider"
[699df7f]1477        msg_hint += "while extrapolating the low-Q region"
[9ce7641c]1478        self.npts_low_tcl.SetToolTipString(msg_hint)
1479        power_txt = wx.StaticText(self, -1, 'Power')
[1128bd31]1480        self.power_low_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH * 2 / 3, -1),
[4a2b054]1481                                         name='power_low_tcl')
[4e1c362]1482        wx.EVT_TEXT(self, self.power_low_tcl.GetId(), self._on_text)
[1128bd31]1483
[9ce7641c]1484        power_hint_txt = "Exponent to apply to the Power_law function."
1485        self.power_low_tcl.SetToolTipString(power_hint_txt)
[c128284]1486        iy = 0
1487        ix = 0
[699df7f]1488        self.low_q_sizer.Add(self.enable_low_cbox, (iy, ix), (1, 5),
[1128bd31]1489                             wx.TOP | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[518d35d]1490        iy += 1
1491        ix = 0
[699df7f]1492        self.low_q_sizer.Add(npts_low_txt, (iy, ix), (1, 1),
[1128bd31]1493                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[518d35d]1494        ix += 1
[1128bd31]1495        self.low_q_sizer.Add(self.npts_low_tcl, (iy, ix), (1, 1),
1496                             wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[518d35d]1497        iy += 1
1498        ix = 0
[1128bd31]1499        self.low_q_sizer.Add(self.guinier, (iy, ix), (1, 2),
1500                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[c128284]1501        iy += 1
1502        ix = 0
[699df7f]1503        self.low_q_sizer.Add(self.power_law_low, (iy, ix), (1, 2),
[1128bd31]1504                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[2661d8b]1505        # Parameter controls for power law
1506        ix = 1
1507        iy += 1
[699df7f]1508        self.low_q_sizer.Add(self.fix_enable_low, (iy, ix), (1, 1),
[1128bd31]1509                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[518d35d]1510        ix += 1
[1128bd31]1511        self.low_q_sizer.Add(self.fit_enable_low, (iy, ix), (1, 1),
1512                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[9ce7641c]1513        ix = 1
1514        iy += 1
[699df7f]1515        self.low_q_sizer.Add(power_txt, (iy, ix), (1, 1),
[1128bd31]1516                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[c128284]1517        ix += 1
[4a2b054]1518        self.low_q_sizer.Add(self.power_low_tcl, (iy, ix), (1, 1),
[1128bd31]1519                             wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[7d16278]1520        self.low_extrapolation_sizer.Add(self.low_q_sizer)
[1128bd31]1521
[518d35d]1522    def _enable_fit_power_law_high(self, event=None):
1523        """
[d7a39e5]1524        Enable and disable the power value editing
[518d35d]1525        """
[1128bd31]1526        if event != None:
[27f3831]1527            self._set_bookmark_flag(True)
[1128bd31]1528
[27f3831]1529            self._set_preview_flag(False)
[518d35d]1530        if self.fix_enable_high.IsEnabled():
1531            if self.fix_enable_high.GetValue():
[4e1c362]1532                self.fit_enable_high.SetValue(False)
[518d35d]1533                self.power_high_tcl.Enable()
1534            else:
[4e1c362]1535                self.fit_enable_high.SetValue(True)
[518d35d]1536                self.power_high_tcl.Disable()
[4e1c362]1537        self._set_state(event=event)
[1128bd31]1538
[518d35d]1539    def _enable_high_q_section(self, event=None):
1540        """
[d7a39e5]1541        Disable or enable some button if the user enable high q extrapolation
[518d35d]1542        """
[1128bd31]1543        if event != None:
[27f3831]1544            self._set_bookmark_flag(True)
1545            self._set_preview_flag(False)
[518d35d]1546        if self.enable_high_cbox.GetValue():
1547            self.npts_high_tcl.Enable()
1548            self.power_law_high.Enable()
1549            self.power_high_tcl.Enable()
1550            self.fix_enable_high.Enable()
1551            self.fit_enable_high.Enable()
1552        else:
1553            self.npts_high_tcl.Disable()
1554            self.power_law_high.Disable()
1555            self.power_high_tcl.Disable()
1556            self.fix_enable_high.Disable()
1557            self.fit_enable_high.Disable()
1558        self._enable_fit_power_law_high()
[4e1c362]1559        self._set_state(event=event)
[d0cc0bbc]1560        self.button_calculate.SetFocus()
[1128bd31]1561
[9ce7641c]1562    def _layout_extrapolation_high(self):
1563        """
[d7a39e5]1564        Draw widgets related to extrapolation at high q range
[9ce7641c]1565        """
[4a2b054]1566        self.enable_high_cbox = wx.CheckBox(self, -1,
1567                                            "Enable Extrapolate high-Q",
1568                                            name='enable_high_cbox')
[518d35d]1569        wx.EVT_CHECKBOX(self, self.enable_high_cbox.GetId(),
[1128bd31]1570                        self._enable_high_q_section)
[518d35d]1571        self.fix_enable_high = wx.RadioButton(self, -1, 'Fix',
[1128bd31]1572                                              (10, 10), style=wx.RB_GROUP,
1573                                              name='fix_enable_high')
[518d35d]1574        self.Bind(wx.EVT_RADIOBUTTON, self._enable_fit_power_law_high,
[1128bd31]1575                  id=self.fix_enable_high.GetId())
[4a2b054]1576        self.fit_enable_high = wx.RadioButton(self, -1, 'Fit', (10, 10),
[1128bd31]1577                                              name='fit_enable_high')
1578        self.Bind(wx.EVT_RADIOBUTTON, self._enable_fit_power_law_high,
1579                  id=self.fit_enable_high.GetId())
1580
[277fad8]1581        self.power_law_high = wx.StaticText(self, -1, 'Power Law')
[699df7f]1582        msg_hint = "Check to extrapolate data at high-Q"
[d0cc0bbc]1583        self.power_law_high.SetToolTipString(msg_hint)
[c128284]1584        npts_high_txt = wx.StaticText(self, -1, 'Npts')
[1128bd31]1585        self.npts_high_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH * 2 / 3, -1),
[4a2b054]1586                                         name='npts_high_tcl')
[4e1c362]1587        wx.EVT_TEXT(self, self.npts_high_tcl.GetId(), self._on_text)
[2661d8b]1588        msg_hint = "Number of Q points to consider"
1589        msg_hint += "while extrapolating the high-Q region"
[9ce7641c]1590        self.npts_high_tcl.SetToolTipString(msg_hint)
1591        power_txt = wx.StaticText(self, -1, 'Power')
[1128bd31]1592        self.power_high_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH * 2 / 3, -1),
[4a2b054]1593                                          name='power_high_tcl')
[4e1c362]1594        wx.EVT_TEXT(self, self.power_high_tcl.GetId(), self._on_text)
[9ce7641c]1595        power_hint_txt = "Exponent to apply to the Power_law function."
1596        self.power_high_tcl.SetToolTipString(power_hint_txt)
[518d35d]1597        iy = 0
1598        ix = 0
[4a2b054]1599        self.high_q_sizer.Add(self.enable_high_cbox, (iy, ix), (1, 5),
[1128bd31]1600                              wx.TOP | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[518d35d]1601        iy += 1
1602        ix = 0
[4a2b054]1603        self.high_q_sizer.Add(npts_high_txt, (iy, ix), (1, 1),
[1128bd31]1604                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[518d35d]1605        ix += 1
[4a2b054]1606        self.high_q_sizer.Add(self.npts_high_tcl, (iy, ix), (1, 1),
[1128bd31]1607                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[7d16278]1608        iy += 1
[c128284]1609        ix = 0
[1128bd31]1610        self.high_q_sizer.Add(self.power_law_high, (iy, ix), (1, 2),
1611                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
1612
[518d35d]1613        # Parameter controls for power law
[2661d8b]1614        ix = 1
1615        iy += 1
[1128bd31]1616        self.high_q_sizer.Add(self.fix_enable_high, (iy, ix), (1, 1),
1617                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[518d35d]1618        ix += 1
[1128bd31]1619        self.high_q_sizer.Add(self.fit_enable_high, (iy, ix), (1, 1),
1620                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[9ce7641c]1621        ix = 1
1622        iy += 1
[1128bd31]1623        self.high_q_sizer.Add(power_txt, (iy, ix), (1, 1),
1624                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[c128284]1625        ix += 1
[1128bd31]1626        self.high_q_sizer.Add(self.power_high_tcl, (iy, ix), (1, 1),
1627                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1628        self.high_extrapolation_sizer.Add(self.high_q_sizer, 0,
1629                                          wx.BOTTOM, 20)
1630
[9ce7641c]1631    def _layout_extrapolation(self):
1632        """
[d7a39e5]1633        Draw widgets related to extrapolation
[9ce7641c]1634        """
[7d16278]1635        extra_hint = "Extrapolation \nMaximum Q Range [1/A]:"
[9ce7641c]1636        extra_hint_txt = wx.StaticText(self, -1, extra_hint)
1637        #Extrapolation range [string]
[1128bd31]1638        extrapolation_min_txt = wx.StaticText(self, -1, 'Min:')
1639        self.extrapolation_min_tcl = OutputTextCtrl(self, -1,
1640                                                    size=(_BOX_WIDTH, 20), style=0,
1641                                                    name='extrapolation_min_tcl')
[9ce7641c]1642        self.extrapolation_min_tcl.SetValue(str(Q_MINIMUM))
[4a2b054]1643        hint_msg = "The minimum extrapolated q value."
1644        self.extrapolation_min_tcl.SetToolTipString(hint_msg)
[1128bd31]1645        extrapolation_max_txt = wx.StaticText(self, -1, 'Max:')
[518d35d]1646        self.extrapolation_max_tcl = OutputTextCtrl(self, -1,
[1128bd31]1647                                                    size=(_BOX_WIDTH, 20),
1648                                                    style=0,
1649                                                    name='extrapolation_max_tcl')
[9ce7641c]1650        self.extrapolation_max_tcl.SetValue(str(Q_MAXIMUM))
[4a2b054]1651        hint_msg = "The maximum extrapolated q value."
1652        self.extrapolation_max_tcl.SetToolTipString(hint_msg)
[1128bd31]1653        self.extrapolation_range_sizer.AddMany([(extra_hint_txt, 0,
[7d16278]1654                                                 wx.LEFT, 5),
[4a2b054]1655                                                (extrapolation_min_txt, 0,
[eaab2ad]1656                                                 wx.LEFT, 10),
[9ce7641c]1657                                                (self.extrapolation_min_tcl,
[1128bd31]1658                                                 0, wx.LEFT, 5),
[4a2b054]1659                                                (extrapolation_max_txt, 0,
[eaab2ad]1660                                                 wx.LEFT, 20),
[9ce7641c]1661                                                (self.extrapolation_max_tcl,
[1128bd31]1662                                                 0, wx.LEFT, 5)])
[9ce7641c]1663        self._layout_extrapolation_low()
1664        self._layout_extrapolation_high()
[1128bd31]1665        self.extrapolation_low_high_sizer.AddMany([(self.low_extrapolation_sizer,
1666                                                    0, wx.LEFT | wx.BOTTOM | wx.TOP, 5),
1667                                                   (self.high_extrapolation_sizer,
1668                                                    0, wx.LEFT | wx.BOTTOM | wx.TOP, 5)])
[7d16278]1669        self.extrapolation_sizer.AddMany([(self.extrapolation_range_sizer),
[1128bd31]1670                                          (self.extrapolation_low_high_sizer)])
1671
[9ce7641c]1672    def _layout_volume_surface_sizer(self):
1673        """
[d7a39e5]1674        Draw widgets related to volume and surface
[9ce7641c]1675        """
1676        unit_volume = ''
[3df5ecf]1677        unit_surface = '[1/A]'
[1128bd31]1678        uncertainty = "+/-"
[7d16278]1679        volume_txt = wx.StaticText(self, -1, 'Volume Fraction')
[4a2b054]1680        self.volume_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1),
1681                                         name='volume_tcl')
[4e1c362]1682        wx.EVT_TEXT(self, self.volume_tcl.GetId(), self._on_out_text)
[9ce7641c]1683        self.volume_tcl.SetToolTipString("Volume fraction.")
[4a2b054]1684        self.volume_err_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1),
1685                                             name='volume_err_tcl')
[4e1c362]1686        wx.EVT_TEXT(self, self.volume_err_tcl.GetId(), self._on_out_text)
[4a2b054]1687        hint_msg = "Uncertainty on the volume fraction."
1688        self.volume_err_tcl.SetToolTipString(hint_msg)
[c128284]1689        volume_units_txt = wx.StaticText(self, -1, unit_volume)
[1128bd31]1690
[277fad8]1691        surface_txt = wx.StaticText(self, -1, 'Specific Surface')
[4a2b054]1692        self.surface_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1),
1693                                          name='surface_tcl')
[4e1c362]1694        wx.EVT_TEXT(self, self.surface_tcl.GetId(), self._on_out_text)
[9ce7641c]1695        self.surface_tcl.SetToolTipString("Specific surface value.")
[4a2b054]1696        self.surface_err_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1),
1697                                              name='surface_err_tcl')
[4e1c362]1698        wx.EVT_TEXT(self, self.surface_err_tcl.GetId(), self._on_out_text)
[4a2b054]1699        hint_msg = "Uncertainty on the specific surface."
1700        self.surface_err_tcl.SetToolTipString(hint_msg)
[c128284]1701        surface_units_txt = wx.StaticText(self, -1, unit_surface)
1702        iy = 0
1703        ix = 0
[4a2b054]1704        self.volume_surface_sizer.Add(volume_txt, (iy, ix), (1, 1),
[1128bd31]1705                                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[9ce7641c]1706        ix += 1
[4a2b054]1707        self.volume_surface_sizer.Add(self.volume_tcl, (iy, ix), (1, 1),
[1128bd31]1708                                      wx.EXPAND | wx.ADJUST_MINSIZE, 20)
[9ce7641c]1709        ix += 1
1710        self.volume_surface_sizer.Add(wx.StaticText(self, -1, uncertainty),
[1128bd31]1711                                      (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[9ce7641c]1712        ix += 1
[4a2b054]1713        self.volume_surface_sizer.Add(self.volume_err_tcl, (iy, ix), (1, 1),
[1128bd31]1714                                      wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[9ce7641c]1715        ix += 1
[4a2b054]1716        self.volume_surface_sizer.Add(volume_units_txt, (iy, ix), (1, 1),
[1128bd31]1717                                      wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1718        iy += 1
1719        ix = 0
[4a2b054]1720        self.volume_surface_sizer.Add(surface_txt, (iy, ix), (1, 1),
[1128bd31]1721                                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[c128284]1722        ix += 1
[4a2b054]1723        self.volume_surface_sizer.Add(self.surface_tcl, (iy, ix), (1, 1),
[1128bd31]1724                                      wx.EXPAND | wx.ADJUST_MINSIZE, 20)
[c128284]1725        ix += 1
[9ce7641c]1726        self.volume_surface_sizer.Add(wx.StaticText(self, -1, uncertainty),
[1128bd31]1727                                      (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1728        ix += 1
[4a2b054]1729        self.volume_surface_sizer.Add(self.surface_err_tcl, (iy, ix), (1, 1),
[1128bd31]1730                                      wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1731        ix += 1
[4a2b054]1732        self.volume_surface_sizer.Add(surface_units_txt, (iy, ix), (1, 1),
[1128bd31]1733                                      wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[7d16278]1734        static_line = wx.StaticLine(self, -1)
1735        iy += 1
1736        ix = 0
[1128bd31]1737
[9ce7641c]1738    def _layout_invariant_sizer(self):
1739        """
[d7a39e5]1740        Draw widgets related to invariant
[9ce7641c]1741        """
[1128bd31]1742        uncertainty = "+/-"
[f686259]1743        unit_invariant = '[1/(cm*A^3)]'
[eed601e]1744        invariant_total_txt = wx.StaticText(self, -1, 'Invariant Total [Q*]')
[4a2b054]1745        self.invariant_total_tcl = OutputTextCtrl(self, -1,
[0d417ac8]1746                                                  size=(_BOX_WIDTH, -1),
[4a2b054]1747                                                  name='invariant_total_tcl')
[eed601e]1748        msg_hint = "Total invariant [Q*], including extrapolated regions."
[9ce7641c]1749        self.invariant_total_tcl.SetToolTipString(msg_hint)
[4a2b054]1750        self.invariant_total_err_tcl = OutputTextCtrl(self, -1,
[0d417ac8]1751                                                      size=(_BOX_WIDTH, -1),
[1128bd31]1752                                                      name='invariant_total_err_tcl')
[4a2b054]1753        hint_msg = "Uncertainty on invariant."
1754        self.invariant_total_err_tcl.SetToolTipString(hint_msg)
[1128bd31]1755        invariant_total_units_txt = wx.StaticText(self, -1, unit_invariant,
1756                                                  size=(80, -1))
1757
[9ce7641c]1758        #Invariant total
1759        iy = 0
[c128284]1760        ix = 0
[4a2b054]1761        self.invariant_sizer.Add(invariant_total_txt, (iy, ix), (1, 1),
[1128bd31]1762                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[9ce7641c]1763        ix += 1
[4a2b054]1764        self.invariant_sizer.Add(self.invariant_total_tcl, (iy, ix), (1, 1),
[1128bd31]1765                                 wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1766        ix += 1
[1128bd31]1767        self.invariant_sizer.Add(wx.StaticText(self, -1, uncertainty),
1768                                 (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1769        ix += 1
[4a2b054]1770        self.invariant_sizer.Add(self.invariant_total_err_tcl, (iy, ix), (1, 1),
[1128bd31]1771                                 wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1772        ix += 1
[1128bd31]1773        self.invariant_sizer.Add(invariant_total_units_txt, (iy, ix), (1, 1),
1774                                 wx.EXPAND | wx.ADJUST_MINSIZE, 10)
1775
[d0cc0bbc]1776    def _layout_inputs_sizer(self):
1777        """
[d7a39e5]1778        Draw widgets related to inputs
[d0cc0bbc]1779        """
[1128bd31]1780        contrast_txt = wx.StaticText(self, -1, 'Contrast:')
[7d16278]1781        self.contrast_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
[0d417ac8]1782                                        style=0, name='contrast_tcl')
[7d16278]1783        wx.EVT_TEXT(self, self.contrast_tcl.GetId(), self._on_text)
1784        contrast_hint_txt = "Contrast"
1785        self.contrast_tcl.SetToolTipString(contrast_hint_txt)
[1128bd31]1786        contrast_unit_txt = wx.StaticText(self, -1, '[1/A^2]', size=(40, -1))
1787        porod_const_txt = wx.StaticText(self, -1,
1788                                        'Porod\nConstant:\n(optional)\n')
1789        porod_unit_txt = wx.StaticText(self, -1, '[1/(cm*A^4)]', size=(80, -1))
1790        self.porod_constant_tcl = InvTextCtrl(self, -1,
[7d16278]1791                                              size=(_BOX_WIDTH, 20), style=0,
[1128bd31]1792                                              name='porod_constant_tcl')
[7d16278]1793        wx.EVT_TEXT(self, self.porod_constant_tcl.GetId(), self._on_text)
1794        porod_const_hint_txt = "Porod Constant"
1795        self.porod_constant_tcl.SetToolTipString(porod_const_hint_txt)
[1128bd31]1796
1797        background_txt = wx.StaticText(self, -1, 'Background:')
[7d16278]1798        self.background_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
[1128bd31]1799                                          style=0, name='background_tcl')
[7d16278]1800        wx.EVT_TEXT(self, self.background_tcl.GetId(), self._on_text)
1801        background_hint_txt = "Background"
1802        self.background_tcl.SetToolTipString(background_hint_txt)
[1128bd31]1803        background_unit_txt = wx.StaticText(self, -1, '[1/cm]')
1804        scale_txt = wx.StaticText(self, -1, 'Scale:')
[7d16278]1805        self.scale_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH, 20), style=0,
1806                                     name='scale_tcl')
1807        wx.EVT_TEXT(self, self.scale_tcl.GetId(), self._on_text)
1808        scale_hint_txt = "Scale"
1809        self.scale_tcl.SetToolTipString(scale_hint_txt)
[1128bd31]1810        self.sizer_input.AddMany([(background_txt, 0, wx.LEFT | wx.BOTTOM, 5),
1811                                  (self.background_tcl, 0, wx.LEFT | wx.BOTTOM, 5),
1812                                  (background_unit_txt, 0, wx.LEFT | wx.BOTTOM, 5),
1813                                  (scale_txt, 0, wx.LEFT | wx.BOTTOM, 10),
1814                                  (self.scale_tcl, 0, wx.LEFT | wx.BOTTOM | wx.RIGHT, 5),
1815                                  (10, 10),
1816                                  (contrast_txt, 0, wx.LEFT | wx.BOTTOM, 5),
1817                                  (self.contrast_tcl, 0, wx.LEFT | wx.BOTTOM, 5),
1818                                  (contrast_unit_txt, 0, wx.LEFT | wx.BOTTOM, 5),
1819                                  (porod_const_txt, 0, wx.LEFT, 10),
1820                                  (self.porod_constant_tcl, 0, wx.LEFT | wx.BOTTOM | wx.RIGHT, 5),
1821                                  (porod_unit_txt, 0, wx.LEFT | wx.BOTTOM, 5)])
[7d16278]1822        self.inputs_sizer.Add(self.sizer_input)
[1128bd31]1823
[9ce7641c]1824    def _layout_outputs_sizer(self):
1825        """
[d7a39e5]1826        Draw widgets related to outputs
[9ce7641c]1827        """
1828        self._layout_volume_surface_sizer()
1829        self._layout_invariant_sizer()
1830        static_line = wx.StaticLine(self, -1)
[7d16278]1831        self.outputs_sizer.AddMany([(self.volume_surface_sizer,
[1128bd31]1832                                     0, wx.TOP | wx.BOTTOM, 10),
[9ce7641c]1833                                    (static_line, 0, wx.EXPAND, 0),
[1128bd31]1834                                    (self.invariant_sizer, 0, wx.TOP | wx.BOTTOM, 10)])
1835    def _layout_button(self):
[9ce7641c]1836        """
[d7a39e5]1837        Do the layout for the button widgets
[1128bd31]1838        """
[9ce7641c]1839        #compute button
[c128284]1840        id = wx.NewId()
[4a2b054]1841        self.button_calculate = wx.Button(self, id, "Compute",
1842                                          name='compute_invariant')
[d0cc0bbc]1843        self.button_calculate.SetToolTipString("Compute invariant")
[1128bd31]1844        self.Bind(wx.EVT_BUTTON, self.compute_invariant, id=id)
[9ce7641c]1845        #detail button
1846        id = wx.NewId()
[d0cc0bbc]1847        self.button_details = wx.Button(self, id, "Details?")
[cce0ad3]1848        hint_msg = "Get more details of computation such as fraction from extrapolation"
[4a2b054]1849        self.button_details.SetToolTipString(hint_msg)
[9ce7641c]1850        self.Bind(wx.EVT_BUTTON, self.display_details, id=id)
[cce0ad3]1851        #help button
1852        id = wx.NewId()
1853        self.button_help = wx.Button(self, id, "HELP")
1854        self.button_help.SetToolTipString("Invariant Documentation")
1855        self.Bind(wx.EVT_BUTTON, self.on_help, id=id)
1856        self.button_sizer.AddMany([((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0),
[1128bd31]1857                                   (self.button_details, 0, wx.ALL, 10),
1858                                   (self.button_calculate, 0,
[cce0ad3]1859                                    wx.RIGHT | wx.TOP | wx.BOTTOM, 10),
1860                                   (self.button_help, 0, 
1861                                    wx.RIGHT | wx.TOP | wx.BOTTOM, 10),])
[9ce7641c]1862    def _do_layout(self):
1863        """
[d7a39e5]1864        Draw window content
[9ce7641c]1865        """
1866        self._define_structure()
1867        self._layout_data_name()
1868        self._layout_extrapolation()
[d0cc0bbc]1869        self._layout_inputs_sizer()
[9ce7641c]1870        self._layout_outputs_sizer()
1871        self._layout_button()
[699df7f]1872        self.main_sizer.AddMany([(self.data_name_boxsizer, 0, wx.ALL, 10),
[1128bd31]1873                                 (self.outputs_sizer, 0,
1874                                  wx.LEFT | wx.RIGHT | wx.BOTTOM, 10),
1875                                 (self.button_sizer, 0, wx.LEFT | wx.RIGHT, 15),
[d0cc0bbc]1876                                 (self.inputs_sizer, 0,
[1128bd31]1877                                  wx.LEFT | wx.RIGHT | wx.BOTTOM, 10),
1878                                 (self.extrapolation_sizer, 0,
1879                                  wx.LEFT | wx.RIGHT | wx.BOTTOM, 10)])
[9ce7641c]1880        self.SetSizer(self.main_sizer)
[355b684]1881        self.SetAutoLayout(True)
[cce0ad3]1882       
1883    def on_help(self, event):
1884        """
1885        Bring up the Invariant Documentation whenever the HELP button is
1886        clicked.
1887
1888        Calls DocumentationWindow with the path of the location within the
1889        documentation tree (after /doc/ ....".  Note that when using old
1890        versions of Wx (before 2.9) and thus not the release version of
1891        installers, the help comes up at the top level of the file as
1892        webbrowser does not pass anything past the # to the browser when it is
1893        running "file:///...."
1894
1895    :param evt: Triggers on clicking the help button
1896    """
1897
1898        _TreeLocation = "user/perspectives/invariant/invariant_help.html"
[3db44fb]1899        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "",
1900                                          "Invariant Help")
[1128bd31]1901
1902
[c128284]1903class InvariantDialog(wx.Dialog):
[d7a39e5]1904    """
[699df7f]1905    Invariant Dialog
[d7a39e5]1906    """
[0d417ac8]1907    def __init__(self, parent=None, id=1, graph=None,
1908                 data=None, title="Invariant", base=None):
[272d91e]1909        wx.Dialog.__init__(self, parent, id, title, size=(PANEL_WIDTH,
[1128bd31]1910                                                          PANEL_HEIGHT))
[272d91e]1911        self.panel = InvariantPanel(self)
[c128284]1912        self.Centre()
1913        self.Show(True)
[1128bd31]1914
[c128284]1915class InvariantWindow(wx.Frame):
[d7a39e5]1916    """
[699df7f]1917    Invariant Window
[d7a39e5]1918    """
[1128bd31]1919    def __init__(self, parent=None, id=1, graph=None,
[4a2b054]1920                 data=None, title="Invariant", base=None):
[1128bd31]1921
1922        wx.Frame.__init__(self, parent, id, title, size=(PANEL_WIDTH + 100,
1923                                                         PANEL_HEIGHT + 100))
[79492222]1924        from sas.dataloader.loader import  Loader
[4e1c362]1925        self.loader = Loader()
[b9a5f0e]1926        path = "C:/ECLPS/workspace/trunk/sasdataloader/test/ascii_test_3.txt"
[699df7f]1927        data = self.loader.load(path)
[272d91e]1928        self.panel = InvariantPanel(self)
[4e1c362]1929
1930        data.name = data.filename
[210ff4f]1931        self.panel.set_data(data)
[c128284]1932        self.Centre()
1933        self.Show(True)
[1128bd31]1934
[c128284]1935class MyApp(wx.App):
[699df7f]1936    """
1937    Test App
1938    """
[c128284]1939    def OnInit(self):
[699df7f]1940        """
1941        Init
1942        """
[c128284]1943        wx.InitAllImageHandlers()
1944        frame = InvariantWindow()
1945        frame.Show(True)
1946        self.SetTopWindow(frame)
[1128bd31]1947
[c128284]1948        return True
[1128bd31]1949
[c128284]1950# end of class MyApp
1951
1952if __name__ == "__main__":
1953    app = MyApp(0)
[1128bd31]1954    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.