source: sasview/src/sas/perspectives/invariant/invariant_panel.py @ 3db44fb

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

1) Fixed second issue that was caused by the recent cleanup of
DocumentationWindow?: loading html at anchor point for context help
(broken). In order to preserve the cleanup, the class was refactored to
take another parameter: html instruction string. This keeps it general
to accept not only the # anchor but alos queries of all sorts in the
future. Thus all modules using this class were also edited to match.

2) in process of editing the dozen or so instances did a bit of code
cleanup and pylint cleanup.

  • Property mode set to 100644
File size: 79.0 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)
831            attr.SetValue(str(value))
832        except:
833            logging.error("Invariant state: %s", sys.exc_value)
[1128bd31]834
[4e1c362]835    def get_bookmark_by_num(self, num=None):
836        """
837        Get the bookmark state given by number
[1128bd31]838
[4e1c362]839        : param num: the given bookmark number
[1128bd31]840
[4e1c362]841        """
842        current_state = {}
843        comp_state = {}
844        backup_state_list = copy.deepcopy(self.state.state_list)
845
846        # get the previous state
847        try:
[1128bd31]848            _, _, current_state, comp_state = self.state.bookmark_list[int(num)]
849        except:
850            logging.error(sys.exc_value)
851            raise ValueError, "No such bookmark exists"
[4e1c362]852
853        # set the parameters
854        for key in comp_state:
855            value = comp_state[key]
[c4f6851]856            self._set_property_value(key, value)
[4e1c362]857
858        self.compute_invariant(event=None)
859        # set the input params at the state of pre_state_num
860        for key in current_state:
861            value = current_state[key]
[c4f6851]862            self._set_property_value(key, value)
[4e1c362]863        self.state.saved_state = copy.deepcopy(current_state)
[1128bd31]864
[4e1c362]865        self._enable_high_q_section(event=None)
866        self._enable_low_q_section(event=None)
867        self.state.state_list = backup_state_list
868        #self.state.saved_state = current_state
869        #self.state.state_num = state_num
870
[f338d3b]871    def reset_panel(self):
872        """
[d7a39e5]873        set the panel at its initial state.
[f338d3b]874        """
[d0cc0bbc]875        self.background_tcl.SetValue(str(BACKGROUND))
[1128bd31]876        self.scale_tcl.SetValue(str(SCALE))
[4e74e13]877        self.contrast_tcl.SetValue(str(CONTRAST))
[1128bd31]878        self.porod_constant_tcl.SetValue('')
[d0cc0bbc]879        self.npts_low_tcl.SetValue(str(NPTS))
880        self.enable_low_cbox.SetValue(False)
881        self.fix_enable_low.SetValue(True)
882        self.power_low_tcl.SetValue(str(POWER))
883        self.guinier.SetValue(True)
884        self.power_low_tcl.Disable()
885        self.enable_high_cbox.SetValue(False)
886        self.fix_enable_high.SetValue(True)
887        self.power_high_tcl.SetValue(str(POWER))
888        self.npts_high_tcl.SetValue(str(NPTS))
889        self.button_details.Disable()
890        #Change the state of txtcrtl to enable/disable
891        self._enable_low_q_section()
892        #Change the state of txtcrtl to enable/disable
893        self._enable_high_q_section()
894        self._reset_output()
[27f3831]895        self._set_undo_flag(False)
896        self._set_redo_flag(False)
897        self._set_bookmark_flag(False)
898        self._set_preview_flag(False)
899        self._set_save_flag(False)
[d0cc0bbc]900        self.button_calculate.SetFocus()
[4e1c362]901        #self.SetupScrolling()
[1128bd31]902
[4e1c362]903    def _set_state(self, event):
904        """
905        Set the state list
[1128bd31]906
[4e1c362]907        :param event: rb/cb event
908        """
909        if event == None:
910            return
911        obj = event.GetEventObject()
912        name = str(obj.GetName())
913        value = str(obj.GetValue())
[0d417ac8]914        rb_list = [['power_law_low', 'guinier'],
915                   ['fit_enable_low', 'fix_enable_low'],
916                   ['fit_enable_high', 'fix_enable_high']]
[4e1c362]917
918        try:
[1128bd31]919            if value == None or value.lstrip().rstrip() == '':
[4e1c362]920                value = 'None'
[c4f6851]921            setattr(self.state, name, str(value))
922            self.state.saved_state[name] = str(value)
[1128bd31]923
924            # set the count part of radio button clicked
[4a2b054]925            #False for the saved_state
[699df7f]926            for title, content in rb_list:
[1128bd31]927                if name == title:
928                    name = content
929                    value = False
[4e1c362]930                elif name == content:
931                    name = title
[1128bd31]932                    value = False
[c4f6851]933            self.state.saved_state[name] = str(value)
[1128bd31]934
[4e1c362]935            # Instead of changing the future, create a new future.
[1128bd31]936            max_state_num = len(self.state.state_list) - 1
937            self.state.saved_state['state_num'] = max_state_num
938
[4a2b054]939            self.state.saved_state['state_num'] += 1
[4e1c362]940            self.state.state_num = self.state.saved_state['state_num']
[0d417ac8]941            self.state.state_list[str(self.state.state_num)] = \
942                    self.state.clone_state()
[1128bd31]943        except:
944            logging.error(sys.exc_value)
945
[27f3831]946        self._set_undo_flag(True)
947        self._set_redo_flag(False)
[d4d78c9]948        #event.Skip()
[1128bd31]949
[0d417ac8]950    def _set_compute_state(self, state=None):
[4e1c362]951        """
952        Notify the compute_invariant state to self.state
[1128bd31]953
[4a2b054]954        : param state: set 'compute' when the computation is
955        activated by the 'compute' button, else None
[1128bd31]956
[4e1c362]957        """
958        # reset the default
959        if state != 'compute':
960            self.new_state = False
961            self.is_power_out = False
962        else:
963            self.is_power_out = True
964        # Instead of changing the future, create a new future.
[1128bd31]965        max_state_num = len(self.state.state_list) - 1
966        self.state.saved_state['state_num'] = max_state_num
[4e1c362]967        # A new computation is also A state
[4a2b054]968        #copy.deepcopy(self.state.saved_state)
969        temp_saved_states = self.state.clone_state()
970        temp_saved_states['state_num'] += 1
[4e1c362]971        self.state.state_num = temp_saved_states['state_num']
972
[1128bd31]973
974        # set the state number of the computation
[4e1c362]975        if state == 'compute':
976            temp_saved_states['compute_num'] = self.state.state_num
[699df7f]977        self.state.saved_state = copy.deepcopy(temp_saved_states)
[4a2b054]978        #copy.deepcopy(self.state.saved_state)
[0d417ac8]979        self.state.state_list[str(self.state.state_num)] = \
980                                        self.state.clone_state()
[1128bd31]981
[4a2b054]982        # A computation is a new state, so delete the states with any higher
983        # state numbers
[1128bd31]984        for i in range(self.state.state_num + 1, len(self.state.state_list)):
[4e1c362]985            try:
[1128bd31]986                del self.state.state_list[str(i)]
987            except:
988                logging.error(sys.exc_value)
[4e1c362]989        # Enable the undo button if it was not
[27f3831]990        self._set_undo_flag(True)
991        self._set_redo_flag(False)
[1128bd31]992
[4a2b054]993    def _reset_state_list(self, data=None):
[4e1c362]994        """
[4a2b054]995        Reset the state_list just before data was loading:
996        Used in 'set_current_data()'
[4e1c362]997        """
998        #if data == None: return
[4a2b054]999        #temp_state = self.state.clone_state()
1000        #copy.deepcopy(self.state.saved_state)
[1128bd31]1001        # Clear the list
[4e1c362]1002        self.state.state_list.clear()
1003        self.state.bookmark_list.clear()
1004        # Set defaults
1005        self.state.saved_state['state_num'] = 0
1006        self.state.saved_state['compute_num'] = 0
1007        if self._data != None:
1008            self.state.saved_state['file'] = str(self._data.name)
1009        else:
1010            self.state.saved_state['file'] = 'None'
1011        self.state.file = self.state.saved_state['file']
1012
1013        self.state.state_num = self.state.saved_state['state_num']
[cb463b4]1014        self.state.timestamp = "('00:00:00', '00/00/0000')"
1015
[4e1c362]1016        # Put only the current state in the list
[4a2b054]1017        #copy.deepcopy(self.state.saved_state)
[699df7f]1018        self.state.state_list[str(self.state.state_num)] = \
1019                                                self.state.clone_state()
[cb69775]1020        self._set_undo_flag(False)
[be738e3]1021        self._set_redo_flag(False)
1022        self._set_bookmark_flag(False)
1023        self._set_preview_flag(False)
1024        self._set_save_flag(False)
1025
[1128bd31]1026
[4e1c362]1027    def _on_text(self, event):
1028        """
1029        Catch text change event to add the state to the state_list
[1128bd31]1030
[4e1c362]1031        :param event: txtctr event ; assumes not None
[1128bd31]1032
[4e1c362]1033        """
[1128bd31]1034        if self._data == None:
[4e1c362]1035            return
1036        # check if this event is from do/undo button
1037        if self.state.saved_state['is_time_machine'] or self.new_state:
[d4d78c9]1038            #event.Skip()
[4e1c362]1039            return
[1128bd31]1040
[4e1c362]1041        # get the object
1042        obj = event.GetEventObject()
1043        name = str(obj.GetName())
1044        value = str(obj.GetValue())
1045        state_num = self.state.saved_state['state_num']
1046
1047        # text event is a new state, so delete the states with higher state_num
1048        # i.e., change the future
[1128bd31]1049        for i in range(int(state_num) + 1, len(self.state.state_list)):
[4e1c362]1050            try:
[1128bd31]1051                del self.state.state_list[str(i)]
1052            except:
1053                logging.error(sys.exc_value)
1054
[4e1c362]1055        # try to add new state of the text changes in the state_list
1056        try:
[1128bd31]1057            if value.strip() == None:
[699df7f]1058                value = ''
[c4f6851]1059            setattr(self.state, name, str(value))
1060            self.state.saved_state[name] = str(value)
1061            self.state.input_list[name] = str(value)
[4e1c362]1062            if not self.is_power_out:
[699df7f]1063                if name != 'power_low_tcl' and name != 'power_high_tcl':
[4e1c362]1064                    self.state.saved_state['state_num'] += 1
1065            self.state.state_num = self.state.saved_state['state_num']
[c4f6851]1066            self.state.state_list[str(self.state.state_num)] = self.state.clone_state()
[4e1c362]1067        except:
[1128bd31]1068            logging.error(sys.exc_value)
[4e1c362]1069
[27f3831]1070        self._set_undo_flag(True)
1071        self._set_redo_flag(False)
1072        self._set_bookmark_flag(True)
1073        self._set_preview_flag(False)
[1128bd31]1074
1075    def _on_out_text(self, event):
[4e1c362]1076        """
[1128bd31]1077        Catch ouput text change to add the state
1078
[4e1c362]1079        :param event: txtctr event ; assumes not None
[1128bd31]1080
1081        """
[4e1c362]1082        # get the object
1083        obj = event.GetEventObject()
1084        name = str(obj.GetName())
1085        value = str(obj.GetValue())
1086        try:
[c4f6851]1087            self.state.saved_state[name] = str(value)
1088            self.state.state_list[str(self.state.state_num)] = self.state.clone_state()
[4e1c362]1089        except:
[1128bd31]1090            logging.error(sys.exc_value)
[1a3a03b]1091
[1128bd31]1092    def _get_input_list(self):
[1a3a03b]1093        """
1094        get input_list; called by set_state
[1128bd31]1095        """
[1a3a03b]1096        # get state num of the last compute state
1097        compute_num = self.state.saved_state['compute_num']
1098        # find values and put into the input list
[1128bd31]1099        for key1, value1 in self.state.state_list[str(compute_num)].iteritems():
1100            for key, _ in self.state.input_list.iteritems():
[1a3a03b]1101                if key == key1:
[699df7f]1102                    self.state.input_list[key] = value1
[1a3a03b]1103                    break
[1128bd31]1104
[4e1c362]1105    def _set_bookmark_menu(self):
1106        """
1107        Setup 'bookmark' context menu
1108        """
1109        ## Create context menu for page
1110        self.popUpMenu = wx.Menu()
1111        id = wx.NewId()
[699df7f]1112        self._bmark = wx.MenuItem(self.popUpMenu, id, "BookMark",
[4a2b054]1113                                  " Bookmark the panel to recall it later")
[4e1c362]1114        self.popUpMenu.AppendItem(self._bmark)
1115        self._bmark.Enable(True)
[27f3831]1116        wx.EVT_MENU(self, id, self.on_bookmark)
[4e1c362]1117        self.popUpMenu.AppendSeparator()
1118        self.Bind(wx.EVT_CONTEXT_MENU, self._on_context_menu)
[1128bd31]1119
[0d417ac8]1120    def on_bookmark(self, event):
[4e1c362]1121        """
[4a2b054]1122        Save the panel state in memory and add the list on
1123        the popup menu on bookmark context menu event
[1128bd31]1124        """
1125        if self._data == None:
[699df7f]1126            return
[1128bd31]1127        if event == None:
[699df7f]1128            return
[4e1c362]1129        self.bookmark_num += 1
1130        # date and time of the event
1131        my_time, date = self._get_time_stamp()
[699df7f]1132        _ = self.state.state_num
[4e1c362]1133        compute_num = self.state.saved_state['compute_num']
1134        # name and message of the bookmark list
[1128bd31]1135        msg = "State saved at %s on %s" % (my_time, date)
1136        ## post help message for the selected model
[699df7f]1137        msg += " Right click on the panel to retrieve this state"
[4e1c362]1138        #wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
[1128bd31]1139        name = "%d] bookmarked at %s on %s" % (self.bookmark_num, my_time, date)
1140
[4e1c362]1141        # append it to menu
[cefb3fb]1142        id = wx.NewId()
[0d417ac8]1143        self.popUpMenu.Append(id, name, str(msg))
[cefb3fb]1144        wx.EVT_MENU(self, id, self._back_to_bookmark)
[4e1c362]1145        state = self.state.clone_state()
1146        comp_state = copy.deepcopy(self.state.state_list[str(compute_num)])
[4a2b054]1147        self.state.bookmark_list[self.bookmark_num] = [my_time, date,
[1128bd31]1148                                                       state, comp_state]
[4e1c362]1149        self.state.toXML(self, doc=None, entry_node=None)
[1128bd31]1150
[0d417ac8]1151        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info"))
[1128bd31]1152        wx.PostEvent(self.parent,
1153                     AppendBookmarkEvent(title=name,
[cb69775]1154                                         hint=str(msg),
[1128bd31]1155                                         handler=self._back_to_bookmark))
[4e1c362]1156
[0d417ac8]1157    def _back_to_bookmark(self, event):
[4e1c362]1158        """
[4a2b054]1159        Bring the panel back to the state of bookmarked requested by
1160        context menu event
[4e1c362]1161        and set it as a new state
1162        """
[cb69775]1163        self._manager.on_perspective(event)
1164        menu = event.GetEventObject()
[1128bd31]1165        ## post help message for the selected model
[cb69775]1166        msg = menu.GetHelpString(event.GetId())
[699df7f]1167        msg += " reloaded"
[1128bd31]1168        wx.PostEvent(self.parent, StatusEvent(status=msg))
1169
[699df7f]1170        name = menu.GetLabel(event.GetId())
[1128bd31]1171
1172        num, _ = name.split(']')
1173        current_state_num = self.state.state_num
[4e1c362]1174        self.get_bookmark_by_num(num)
[0d417ac8]1175        state_num = int(current_state_num) + 1
[1128bd31]1176
[4e1c362]1177        self.state.saved_state['state_num'] = state_num
[4a2b054]1178        #copy.deepcopy(self.state.saved_state)
1179        self.state.state_list[str(state_num)] = self.state.clone_state()
[4e1c362]1180        self.state.state_num = state_num
[1128bd31]1181
[27f3831]1182        self._set_undo_flag(True)
[4e1c362]1183        self._info_bookmark_num(event)
[1128bd31]1184
[0d417ac8]1185    def _info_bookmark_num(self, event=None):
[4e1c362]1186        """
1187        print the bookmark number in info
[1128bd31]1188
[4e1c362]1189        : event: popUpMenu event
1190        """
[1128bd31]1191        if event == None:
[699df7f]1192            return
[4e1c362]1193        # get the object
[cb69775]1194        menu = event.GetEventObject()
1195        item = menu.FindItemById(event.GetId())
[4e1c362]1196        text = item.GetText()
1197        num = text.split(']')[0]
[1128bd31]1198        msg = "bookmark num = %s " % num
1199
1200        wx.PostEvent(self.parent, StatusEvent(status=msg))
1201
[4e1c362]1202    def _info_state_num(self):
1203        """
1204        print the current state number in info
1205        """
1206        msg = "state num = "
1207        msg += self.state.state_num
[1128bd31]1208
1209        wx.PostEvent(self.parent, StatusEvent(status=msg))
1210
[4e1c362]1211    def _get_time_stamp(self):
1212        """
1213        return time and date stings
1214        """
[1128bd31]1215        # date and time
[699df7f]1216        year, month, day, hour, minute, second, _, _, _ = \
[0d417ac8]1217                                    time.localtime()
1218        my_time = str(hour) + ":" + str(minute) + ":" + str(second)
[1128bd31]1219        date = str(month) + "/" + str(day) + "/" + str(year)
[4e1c362]1220        return my_time, date
[1128bd31]1221
1222
1223    def on_save(self, evt=None):
[4e1c362]1224        """
1225        Save invariant state into a file
1226        """
1227        # Ask the user the location of the file to write to.
1228        path = None
[c4ae1c2]1229        if self.parent != None:
[699df7f]1230            self._default_save_location = self.parent.get_save_location()
[c4ae1c2]1231        if self._default_save_location == None:
1232            self._default_save_location = os.getcwd()
[4a2b054]1233        dlg = wx.FileDialog(self, "Choose a file",
[c4ae1c2]1234                            self._default_save_location, \
1235                            self.window_caption, "*.inv", wx.SAVE)
[4e1c362]1236        if dlg.ShowModal() == wx.ID_OK:
1237            path = dlg.GetPath()
1238            self._default_save_location = os.path.dirname(path)
[c4ae1c2]1239            if self.parent != None:
[0d417ac8]1240                self.parent._default_save_location = \
[c4ae1c2]1241                    self._default_save_location
[4e1c362]1242        else:
1243            return None
[1128bd31]1244
[4e1c362]1245        dlg.Destroy()
[23cdeab]1246        # MAC always needs the extension for saving
1247        extens = ".inv"
1248        # Make sure the ext included in the file name
1249        fName = os.path.splitext(path)[0] + extens
1250        self._manager.save_file(filepath=fName, state=self.state)
[1128bd31]1251
[0d417ac8]1252    def _show_message(self, mssg='', msg='Warning'):
[4e1c362]1253        """
1254        Show warning message when resetting data
1255        """
[f24925ab]1256        # no message for now
1257        return True
[1128bd31]1258
[c128284]1259    def _reset_output(self):
1260        """
[d7a39e5]1261        clear outputs textcrtl
[c128284]1262        """
[9ce7641c]1263        self.invariant_total_tcl.Clear()
1264        self.invariant_total_err_tcl.Clear()
1265        self.volume_tcl.Clear()
1266        self.volume_err_tcl.Clear()
1267        self.surface_tcl.Clear()
1268        self.surface_err_tcl.Clear()
[a0a4486]1269        #prepare a new container to put result of invariant
1270        self.inv_container = InvariantContainer()
[4e1c362]1271
[1128bd31]1272
[0d417ac8]1273    def _on_context_menu(self, event):
[699df7f]1274        """
1275        On context menu
1276        """
[4e1c362]1277        pos = event.GetPosition()
1278        pos = self.ScreenToClient(pos)
[1128bd31]1279
1280        self.PopupMenu(self.popUpMenu, pos)
1281
[9ce7641c]1282    def _define_structure(self):
[c128284]1283        """
[d7a39e5]1284        Define main sizers needed for this panel
[c128284]1285        """
[1128bd31]1286        ## Box sizers must be defined first before
[4a2b054]1287        #defining buttons/textctrls (MAC).
[9ce7641c]1288        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
1289        #Sizer related to outputs
1290        outputs_box = wx.StaticBox(self, -1, "Outputs")
1291        self.outputs_sizer = wx.StaticBoxSizer(outputs_box, wx.VERTICAL)
[699df7f]1292        self.outputs_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[9ce7641c]1293        #Sizer related to data
1294        data_name_box = wx.StaticBox(self, -1, "I(q) Data Source")
1295        self.data_name_boxsizer = wx.StaticBoxSizer(data_name_box, wx.VERTICAL)
[1128bd31]1296        self.data_name_boxsizer.SetMinSize((_STATICBOX_WIDTH, -1))
[9ce7641c]1297        self.hint_msg_sizer = wx.BoxSizer(wx.HORIZONTAL)
[210ff4f]1298        self.data_name_sizer = wx.BoxSizer(wx.HORIZONTAL)
[1128bd31]1299
[9ce7641c]1300        self.data_range_sizer = wx.BoxSizer(wx.HORIZONTAL)
[7d16278]1301        #Sizer related to inputs
[1128bd31]1302        self.sizer_input = wx.FlexGridSizer(2, 6, 0, 0)
[d0cc0bbc]1303        #Sizer related to inputs
1304        inputs_box = wx.StaticBox(self, -1, "Customized Inputs")
1305        self.inputs_sizer = wx.StaticBoxSizer(inputs_box, wx.VERTICAL)
[699df7f]1306        self.inputs_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[d0cc0bbc]1307        #Sizer related to extrapolation
[d5f0dcb9]1308        extrapolation_box = wx.StaticBox(self, -1, "Extrapolation")
[9ce7641c]1309        self.extrapolation_sizer = wx.StaticBoxSizer(extrapolation_box,
[1128bd31]1310                                                     wx.VERTICAL)
[699df7f]1311        self.extrapolation_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[9ce7641c]1312        self.extrapolation_range_sizer = wx.BoxSizer(wx.HORIZONTAL)
1313        self.extrapolation_low_high_sizer = wx.BoxSizer(wx.HORIZONTAL)
1314        #Sizer related to extrapolation at low q range
[d5f0dcb9]1315        low_q_box = wx.StaticBox(self, -1, "Low Q")
[9ce7641c]1316        self.low_extrapolation_sizer = wx.StaticBoxSizer(low_q_box, wx.VERTICAL)
[1128bd31]1317
[699df7f]1318        self.low_q_sizer = wx.GridBagSizer(5, 5)
[9ce7641c]1319        #Sizer related to extrapolation at low q range
1320        high_q_box = wx.StaticBox(self, -1, "High Q")
[4a2b054]1321        self.high_extrapolation_sizer = wx.StaticBoxSizer(high_q_box,
1322                                                          wx.VERTICAL)
[0d417ac8]1323        self.high_q_sizer = wx.GridBagSizer(5, 5)
[9ce7641c]1324        #sizer to define outputs
[0d417ac8]1325        self.volume_surface_sizer = wx.GridBagSizer(5, 5)
[9ce7641c]1326        #Sizer related to invariant output
[277fad8]1327        self.invariant_sizer = wx.GridBagSizer(5, 5)
[9ce7641c]1328        #Sizer related to button
1329        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
[0d417ac8]1330        self.button_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[4e1c362]1331        #Sizer related to save button
1332        self.save_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
[1128bd31]1333
[9ce7641c]1334    def _layout_data_name(self):
1335        """
[d7a39e5]1336        Draw widgets related to data's name
[9ce7641c]1337        """
[1128bd31]1338        #Sizer hint
[cb69775]1339        hint_msg = ""
[1128bd31]1340
1341        self.hint_msg_txt = wx.StaticText(self, -1, hint_msg)
[277fad8]1342        self.hint_msg_txt.SetForegroundColour("red")
[4a2b054]1343        msg = "Highlight = mouse the mouse's cursor on the data until"
1344        msg += " the plot's color changes to yellow"
[6848131]1345        self.hint_msg_txt.SetToolTipString(msg)
[277fad8]1346        self.hint_msg_sizer.Add(self.hint_msg_txt)
[210ff4f]1347        #Data name [string]
[1128bd31]1348        data_name_txt = wx.StaticText(self, -1, 'Name:')
1349
1350        self.data_name_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH * 4, 20),
1351                                            style=0)
[210ff4f]1352        self.data_name_tcl.SetToolTipString("Data's name.")
[1128bd31]1353        self.data_name_sizer.AddMany([(data_name_txt, 0, wx.LEFT | wx.RIGHT, 10),
1354                                      (self.data_name_tcl, 0, wx.EXPAND)])
[9ce7641c]1355        #Data range [string]
[1128bd31]1356        data_range_txt = wx.StaticText(self, -1, 'Total Q Range (1/A): ')
1357        data_min_txt = wx.StaticText(self, -1, 'Min : ')
[4a2b054]1358        self.data_min_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1359                                           style=0, name='data_min_tcl')
[9ce7641c]1360        self.data_min_tcl.SetToolTipString("The minimum value of q range.")
[1128bd31]1361        data_max_txt = wx.StaticText(self, -1, 'Max : ')
[4a2b054]1362        self.data_max_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
[1128bd31]1363                                           style=0, name='data_max_tcl')
[9ce7641c]1364        self.data_max_tcl.SetToolTipString("The maximum value of q range.")
[7d16278]1365        self.data_range_sizer.AddMany([(data_range_txt, 0, wx.RIGHT, 5),
1366                                       (data_min_txt, 0, wx.RIGHT, 5),
[eaab2ad]1367                                       (self.data_min_tcl, 0, wx.RIGHT, 20),
[7d16278]1368                                       (data_max_txt, 0, wx.RIGHT, 5),
[eaab2ad]1369                                       (self.data_max_tcl, 0, wx.RIGHT, 10)])
[1128bd31]1370        self.data_name_boxsizer.AddMany([(self.hint_msg_sizer, 0, wx.ALL, 5),
1371                                         (self.data_name_sizer, 0, wx.ALL, 10),
1372                                         (self.data_range_sizer, 0, wx.ALL, 10)])
1373
[518d35d]1374    def _enable_fit_power_law_low(self, event=None):
1375        """
[d7a39e5]1376        Enable and disable the power value editing
[518d35d]1377        """
[1128bd31]1378        if event != None:
[27f3831]1379            self._set_bookmark_flag(True)
1380            self._set_preview_flag(False)
[1128bd31]1381
[518d35d]1382        if self.fix_enable_low.IsEnabled():
[1128bd31]1383
[518d35d]1384            if self.fix_enable_low.GetValue():
[4e1c362]1385                self.fit_enable_low.SetValue(False)
[518d35d]1386                self.power_low_tcl.Enable()
1387            else:
[4e1c362]1388                self.fit_enable_low.SetValue(True)
[518d35d]1389                self.power_low_tcl.Disable()
[4e1c362]1390        self._set_state(event=event)
[1128bd31]1391
[518d35d]1392    def _enable_low_q_section(self, event=None):
1393        """
[d7a39e5]1394        Disable or enable some button if the user enable low q extrapolation
[518d35d]1395        """
[1128bd31]1396        if event != None:
[27f3831]1397            self._set_bookmark_flag(True)
1398            self._set_preview_flag(False)
[1128bd31]1399
[518d35d]1400        if self.enable_low_cbox.GetValue():
1401            self.npts_low_tcl.Enable()
1402            self.fix_enable_low.Enable()
1403            self.fit_enable_low.Enable()
1404            self.guinier.Enable()
1405            self.power_law_low.Enable()
1406
1407        else:
1408            self.npts_low_tcl.Disable()
1409            self.fix_enable_low.Disable()
1410            self.fit_enable_low.Disable()
1411            self.guinier.Disable()
1412            self.power_law_low.Disable()
[1128bd31]1413
[518d35d]1414        self._enable_power_law_low()
1415        self._enable_fit_power_law_low()
[4e1c362]1416        self._set_state(event=event)
[d0cc0bbc]1417        self.button_calculate.SetFocus()
[1128bd31]1418
[518d35d]1419    def _enable_power_law_low(self, event=None):
1420        """
[d7a39e5]1421        Enable editing power law section at low q range
[518d35d]1422        """
[1128bd31]1423        if event != None:
[27f3831]1424            self._set_bookmark_flag(True)
1425            self._set_preview_flag(False)
[518d35d]1426        if self.guinier.GetValue():
[4e1c362]1427            self.power_law_low.SetValue(False)
[518d35d]1428            self.fix_enable_low.Disable()
1429            self.fit_enable_low.Disable()
1430            self.power_low_tcl.Disable()
1431        else:
[4e1c362]1432            self.power_law_low.SetValue(True)
[518d35d]1433            self.fix_enable_low.Enable()
1434            self.fit_enable_low.Enable()
1435            self.power_low_tcl.Enable()
1436        self._enable_fit_power_law_low()
[4e1c362]1437        self._set_state(event=event)
[1128bd31]1438
[9ce7641c]1439    def _layout_extrapolation_low(self):
1440        """
[d7a39e5]1441        Draw widgets related to extrapolation at low q range
[9ce7641c]1442        """
[4a2b054]1443        self.enable_low_cbox = wx.CheckBox(self, -1,
1444                                           "Enable Extrapolate Low Q",
1445                                           name='enable_low_cbox')
[518d35d]1446        wx.EVT_CHECKBOX(self, self.enable_low_cbox.GetId(),
[1128bd31]1447                        self._enable_low_q_section)
[518d35d]1448        self.fix_enable_low = wx.RadioButton(self, -1, 'Fix',
[1128bd31]1449                                             (10, 10), style=wx.RB_GROUP,
1450                                             name='fix_enable_low')
[518d35d]1451        self.Bind(wx.EVT_RADIOBUTTON, self._enable_fit_power_law_low,
[1128bd31]1452                  id=self.fix_enable_low.GetId())
[4a2b054]1453        self.fit_enable_low = wx.RadioButton(self, -1, 'Fit', (10, 10),
1454                                             name='fit_enable_low')
[1128bd31]1455        self.Bind(wx.EVT_RADIOBUTTON, self._enable_fit_power_law_low,
1456                  id=self.fit_enable_low.GetId())
[c128284]1457        self.guinier = wx.RadioButton(self, -1, 'Guinier',
[1128bd31]1458                                      (10, 10), style=wx.RB_GROUP,
1459                                      name='guinier')
[518d35d]1460        self.Bind(wx.EVT_RADIOBUTTON, self._enable_power_law_low,
[1128bd31]1461                  id=self.guinier.GetId())
[4a2b054]1462        self.power_law_low = wx.RadioButton(self, -1, 'Power Law',
1463                                            (10, 10), name='power_law_low')
[1128bd31]1464        self.Bind(wx.EVT_RADIOBUTTON, self._enable_power_law_low,
1465                  id=self.power_law_low.GetId())
1466
[c128284]1467        npts_low_txt = wx.StaticText(self, -1, 'Npts')
[4a2b054]1468        self.npts_low_tcl = InvTextCtrl(self, -1,
[1128bd31]1469                                        size=(_BOX_WIDTH * 2 / 3, -1),
[4a2b054]1470                                        name='npts_low_tcl')
[4e1c362]1471        wx.EVT_TEXT(self, self.npts_low_tcl.GetId(), self._on_text)
[2661d8b]1472        msg_hint = "Number of Q points to consider"
[699df7f]1473        msg_hint += "while extrapolating the low-Q region"
[9ce7641c]1474        self.npts_low_tcl.SetToolTipString(msg_hint)
1475        power_txt = wx.StaticText(self, -1, 'Power')
[1128bd31]1476        self.power_low_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH * 2 / 3, -1),
[4a2b054]1477                                         name='power_low_tcl')
[4e1c362]1478        wx.EVT_TEXT(self, self.power_low_tcl.GetId(), self._on_text)
[1128bd31]1479
[9ce7641c]1480        power_hint_txt = "Exponent to apply to the Power_law function."
1481        self.power_low_tcl.SetToolTipString(power_hint_txt)
[c128284]1482        iy = 0
1483        ix = 0
[699df7f]1484        self.low_q_sizer.Add(self.enable_low_cbox, (iy, ix), (1, 5),
[1128bd31]1485                             wx.TOP | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[518d35d]1486        iy += 1
1487        ix = 0
[699df7f]1488        self.low_q_sizer.Add(npts_low_txt, (iy, ix), (1, 1),
[1128bd31]1489                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[518d35d]1490        ix += 1
[1128bd31]1491        self.low_q_sizer.Add(self.npts_low_tcl, (iy, ix), (1, 1),
1492                             wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[518d35d]1493        iy += 1
1494        ix = 0
[1128bd31]1495        self.low_q_sizer.Add(self.guinier, (iy, ix), (1, 2),
1496                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[c128284]1497        iy += 1
1498        ix = 0
[699df7f]1499        self.low_q_sizer.Add(self.power_law_low, (iy, ix), (1, 2),
[1128bd31]1500                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[2661d8b]1501        # Parameter controls for power law
1502        ix = 1
1503        iy += 1
[699df7f]1504        self.low_q_sizer.Add(self.fix_enable_low, (iy, ix), (1, 1),
[1128bd31]1505                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[518d35d]1506        ix += 1
[1128bd31]1507        self.low_q_sizer.Add(self.fit_enable_low, (iy, ix), (1, 1),
1508                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[9ce7641c]1509        ix = 1
1510        iy += 1
[699df7f]1511        self.low_q_sizer.Add(power_txt, (iy, ix), (1, 1),
[1128bd31]1512                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[c128284]1513        ix += 1
[4a2b054]1514        self.low_q_sizer.Add(self.power_low_tcl, (iy, ix), (1, 1),
[1128bd31]1515                             wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[7d16278]1516        self.low_extrapolation_sizer.Add(self.low_q_sizer)
[1128bd31]1517
[518d35d]1518    def _enable_fit_power_law_high(self, event=None):
1519        """
[d7a39e5]1520        Enable and disable the power value editing
[518d35d]1521        """
[1128bd31]1522        if event != None:
[27f3831]1523            self._set_bookmark_flag(True)
[1128bd31]1524
[27f3831]1525            self._set_preview_flag(False)
[518d35d]1526        if self.fix_enable_high.IsEnabled():
1527            if self.fix_enable_high.GetValue():
[4e1c362]1528                self.fit_enable_high.SetValue(False)
[518d35d]1529                self.power_high_tcl.Enable()
1530            else:
[4e1c362]1531                self.fit_enable_high.SetValue(True)
[518d35d]1532                self.power_high_tcl.Disable()
[4e1c362]1533        self._set_state(event=event)
[1128bd31]1534
[518d35d]1535    def _enable_high_q_section(self, event=None):
1536        """
[d7a39e5]1537        Disable or enable some button if the user enable high q extrapolation
[518d35d]1538        """
[1128bd31]1539        if event != None:
[27f3831]1540            self._set_bookmark_flag(True)
1541            self._set_preview_flag(False)
[518d35d]1542        if self.enable_high_cbox.GetValue():
1543            self.npts_high_tcl.Enable()
1544            self.power_law_high.Enable()
1545            self.power_high_tcl.Enable()
1546            self.fix_enable_high.Enable()
1547            self.fit_enable_high.Enable()
1548        else:
1549            self.npts_high_tcl.Disable()
1550            self.power_law_high.Disable()
1551            self.power_high_tcl.Disable()
1552            self.fix_enable_high.Disable()
1553            self.fit_enable_high.Disable()
1554        self._enable_fit_power_law_high()
[4e1c362]1555        self._set_state(event=event)
[d0cc0bbc]1556        self.button_calculate.SetFocus()
[1128bd31]1557
[9ce7641c]1558    def _layout_extrapolation_high(self):
1559        """
[d7a39e5]1560        Draw widgets related to extrapolation at high q range
[9ce7641c]1561        """
[4a2b054]1562        self.enable_high_cbox = wx.CheckBox(self, -1,
1563                                            "Enable Extrapolate high-Q",
1564                                            name='enable_high_cbox')
[518d35d]1565        wx.EVT_CHECKBOX(self, self.enable_high_cbox.GetId(),
[1128bd31]1566                        self._enable_high_q_section)
[518d35d]1567        self.fix_enable_high = wx.RadioButton(self, -1, 'Fix',
[1128bd31]1568                                              (10, 10), style=wx.RB_GROUP,
1569                                              name='fix_enable_high')
[518d35d]1570        self.Bind(wx.EVT_RADIOBUTTON, self._enable_fit_power_law_high,
[1128bd31]1571                  id=self.fix_enable_high.GetId())
[4a2b054]1572        self.fit_enable_high = wx.RadioButton(self, -1, 'Fit', (10, 10),
[1128bd31]1573                                              name='fit_enable_high')
1574        self.Bind(wx.EVT_RADIOBUTTON, self._enable_fit_power_law_high,
1575                  id=self.fit_enable_high.GetId())
1576
[277fad8]1577        self.power_law_high = wx.StaticText(self, -1, 'Power Law')
[699df7f]1578        msg_hint = "Check to extrapolate data at high-Q"
[d0cc0bbc]1579        self.power_law_high.SetToolTipString(msg_hint)
[c128284]1580        npts_high_txt = wx.StaticText(self, -1, 'Npts')
[1128bd31]1581        self.npts_high_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH * 2 / 3, -1),
[4a2b054]1582                                         name='npts_high_tcl')
[4e1c362]1583        wx.EVT_TEXT(self, self.npts_high_tcl.GetId(), self._on_text)
[2661d8b]1584        msg_hint = "Number of Q points to consider"
1585        msg_hint += "while extrapolating the high-Q region"
[9ce7641c]1586        self.npts_high_tcl.SetToolTipString(msg_hint)
1587        power_txt = wx.StaticText(self, -1, 'Power')
[1128bd31]1588        self.power_high_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH * 2 / 3, -1),
[4a2b054]1589                                          name='power_high_tcl')
[4e1c362]1590        wx.EVT_TEXT(self, self.power_high_tcl.GetId(), self._on_text)
[9ce7641c]1591        power_hint_txt = "Exponent to apply to the Power_law function."
1592        self.power_high_tcl.SetToolTipString(power_hint_txt)
[518d35d]1593        iy = 0
1594        ix = 0
[4a2b054]1595        self.high_q_sizer.Add(self.enable_high_cbox, (iy, ix), (1, 5),
[1128bd31]1596                              wx.TOP | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[518d35d]1597        iy += 1
1598        ix = 0
[4a2b054]1599        self.high_q_sizer.Add(npts_high_txt, (iy, ix), (1, 1),
[1128bd31]1600                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[518d35d]1601        ix += 1
[4a2b054]1602        self.high_q_sizer.Add(self.npts_high_tcl, (iy, ix), (1, 1),
[1128bd31]1603                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[7d16278]1604        iy += 1
[c128284]1605        ix = 0
[1128bd31]1606        self.high_q_sizer.Add(self.power_law_high, (iy, ix), (1, 2),
1607                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
1608
[518d35d]1609        # Parameter controls for power law
[2661d8b]1610        ix = 1
1611        iy += 1
[1128bd31]1612        self.high_q_sizer.Add(self.fix_enable_high, (iy, ix), (1, 1),
1613                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[518d35d]1614        ix += 1
[1128bd31]1615        self.high_q_sizer.Add(self.fit_enable_high, (iy, ix), (1, 1),
1616                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
[9ce7641c]1617        ix = 1
1618        iy += 1
[1128bd31]1619        self.high_q_sizer.Add(power_txt, (iy, ix), (1, 1),
1620                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[c128284]1621        ix += 1
[1128bd31]1622        self.high_q_sizer.Add(self.power_high_tcl, (iy, ix), (1, 1),
1623                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1624        self.high_extrapolation_sizer.Add(self.high_q_sizer, 0,
1625                                          wx.BOTTOM, 20)
1626
[9ce7641c]1627    def _layout_extrapolation(self):
1628        """
[d7a39e5]1629        Draw widgets related to extrapolation
[9ce7641c]1630        """
[7d16278]1631        extra_hint = "Extrapolation \nMaximum Q Range [1/A]:"
[9ce7641c]1632        extra_hint_txt = wx.StaticText(self, -1, extra_hint)
1633        #Extrapolation range [string]
[1128bd31]1634        extrapolation_min_txt = wx.StaticText(self, -1, 'Min:')
1635        self.extrapolation_min_tcl = OutputTextCtrl(self, -1,
1636                                                    size=(_BOX_WIDTH, 20), style=0,
1637                                                    name='extrapolation_min_tcl')
[9ce7641c]1638        self.extrapolation_min_tcl.SetValue(str(Q_MINIMUM))
[4a2b054]1639        hint_msg = "The minimum extrapolated q value."
1640        self.extrapolation_min_tcl.SetToolTipString(hint_msg)
[1128bd31]1641        extrapolation_max_txt = wx.StaticText(self, -1, 'Max:')
[518d35d]1642        self.extrapolation_max_tcl = OutputTextCtrl(self, -1,
[1128bd31]1643                                                    size=(_BOX_WIDTH, 20),
1644                                                    style=0,
1645                                                    name='extrapolation_max_tcl')
[9ce7641c]1646        self.extrapolation_max_tcl.SetValue(str(Q_MAXIMUM))
[4a2b054]1647        hint_msg = "The maximum extrapolated q value."
1648        self.extrapolation_max_tcl.SetToolTipString(hint_msg)
[1128bd31]1649        self.extrapolation_range_sizer.AddMany([(extra_hint_txt, 0,
[7d16278]1650                                                 wx.LEFT, 5),
[4a2b054]1651                                                (extrapolation_min_txt, 0,
[eaab2ad]1652                                                 wx.LEFT, 10),
[9ce7641c]1653                                                (self.extrapolation_min_tcl,
[1128bd31]1654                                                 0, wx.LEFT, 5),
[4a2b054]1655                                                (extrapolation_max_txt, 0,
[eaab2ad]1656                                                 wx.LEFT, 20),
[9ce7641c]1657                                                (self.extrapolation_max_tcl,
[1128bd31]1658                                                 0, wx.LEFT, 5)])
[9ce7641c]1659        self._layout_extrapolation_low()
1660        self._layout_extrapolation_high()
[1128bd31]1661        self.extrapolation_low_high_sizer.AddMany([(self.low_extrapolation_sizer,
1662                                                    0, wx.LEFT | wx.BOTTOM | wx.TOP, 5),
1663                                                   (self.high_extrapolation_sizer,
1664                                                    0, wx.LEFT | wx.BOTTOM | wx.TOP, 5)])
[7d16278]1665        self.extrapolation_sizer.AddMany([(self.extrapolation_range_sizer),
[1128bd31]1666                                          (self.extrapolation_low_high_sizer)])
1667
[9ce7641c]1668    def _layout_volume_surface_sizer(self):
1669        """
[d7a39e5]1670        Draw widgets related to volume and surface
[9ce7641c]1671        """
1672        unit_volume = ''
[3df5ecf]1673        unit_surface = '[1/A]'
[1128bd31]1674        uncertainty = "+/-"
[7d16278]1675        volume_txt = wx.StaticText(self, -1, 'Volume Fraction')
[4a2b054]1676        self.volume_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1),
1677                                         name='volume_tcl')
[4e1c362]1678        wx.EVT_TEXT(self, self.volume_tcl.GetId(), self._on_out_text)
[9ce7641c]1679        self.volume_tcl.SetToolTipString("Volume fraction.")
[4a2b054]1680        self.volume_err_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1),
1681                                             name='volume_err_tcl')
[4e1c362]1682        wx.EVT_TEXT(self, self.volume_err_tcl.GetId(), self._on_out_text)
[4a2b054]1683        hint_msg = "Uncertainty on the volume fraction."
1684        self.volume_err_tcl.SetToolTipString(hint_msg)
[c128284]1685        volume_units_txt = wx.StaticText(self, -1, unit_volume)
[1128bd31]1686
[277fad8]1687        surface_txt = wx.StaticText(self, -1, 'Specific Surface')
[4a2b054]1688        self.surface_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1),
1689                                          name='surface_tcl')
[4e1c362]1690        wx.EVT_TEXT(self, self.surface_tcl.GetId(), self._on_out_text)
[9ce7641c]1691        self.surface_tcl.SetToolTipString("Specific surface value.")
[4a2b054]1692        self.surface_err_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1),
1693                                              name='surface_err_tcl')
[4e1c362]1694        wx.EVT_TEXT(self, self.surface_err_tcl.GetId(), self._on_out_text)
[4a2b054]1695        hint_msg = "Uncertainty on the specific surface."
1696        self.surface_err_tcl.SetToolTipString(hint_msg)
[c128284]1697        surface_units_txt = wx.StaticText(self, -1, unit_surface)
1698        iy = 0
1699        ix = 0
[4a2b054]1700        self.volume_surface_sizer.Add(volume_txt, (iy, ix), (1, 1),
[1128bd31]1701                                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[9ce7641c]1702        ix += 1
[4a2b054]1703        self.volume_surface_sizer.Add(self.volume_tcl, (iy, ix), (1, 1),
[1128bd31]1704                                      wx.EXPAND | wx.ADJUST_MINSIZE, 20)
[9ce7641c]1705        ix += 1
1706        self.volume_surface_sizer.Add(wx.StaticText(self, -1, uncertainty),
[1128bd31]1707                                      (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[9ce7641c]1708        ix += 1
[4a2b054]1709        self.volume_surface_sizer.Add(self.volume_err_tcl, (iy, ix), (1, 1),
[1128bd31]1710                                      wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[9ce7641c]1711        ix += 1
[4a2b054]1712        self.volume_surface_sizer.Add(volume_units_txt, (iy, ix), (1, 1),
[1128bd31]1713                                      wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1714        iy += 1
1715        ix = 0
[4a2b054]1716        self.volume_surface_sizer.Add(surface_txt, (iy, ix), (1, 1),
[1128bd31]1717                                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[c128284]1718        ix += 1
[4a2b054]1719        self.volume_surface_sizer.Add(self.surface_tcl, (iy, ix), (1, 1),
[1128bd31]1720                                      wx.EXPAND | wx.ADJUST_MINSIZE, 20)
[c128284]1721        ix += 1
[9ce7641c]1722        self.volume_surface_sizer.Add(wx.StaticText(self, -1, uncertainty),
[1128bd31]1723                                      (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1724        ix += 1
[4a2b054]1725        self.volume_surface_sizer.Add(self.surface_err_tcl, (iy, ix), (1, 1),
[1128bd31]1726                                      wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1727        ix += 1
[4a2b054]1728        self.volume_surface_sizer.Add(surface_units_txt, (iy, ix), (1, 1),
[1128bd31]1729                                      wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[7d16278]1730        static_line = wx.StaticLine(self, -1)
1731        iy += 1
1732        ix = 0
[1128bd31]1733
[9ce7641c]1734    def _layout_invariant_sizer(self):
1735        """
[d7a39e5]1736        Draw widgets related to invariant
[9ce7641c]1737        """
[1128bd31]1738        uncertainty = "+/-"
[f686259]1739        unit_invariant = '[1/(cm*A^3)]'
[eed601e]1740        invariant_total_txt = wx.StaticText(self, -1, 'Invariant Total [Q*]')
[4a2b054]1741        self.invariant_total_tcl = OutputTextCtrl(self, -1,
[0d417ac8]1742                                                  size=(_BOX_WIDTH, -1),
[4a2b054]1743                                                  name='invariant_total_tcl')
[eed601e]1744        msg_hint = "Total invariant [Q*], including extrapolated regions."
[9ce7641c]1745        self.invariant_total_tcl.SetToolTipString(msg_hint)
[4a2b054]1746        self.invariant_total_err_tcl = OutputTextCtrl(self, -1,
[0d417ac8]1747                                                      size=(_BOX_WIDTH, -1),
[1128bd31]1748                                                      name='invariant_total_err_tcl')
[4a2b054]1749        hint_msg = "Uncertainty on invariant."
1750        self.invariant_total_err_tcl.SetToolTipString(hint_msg)
[1128bd31]1751        invariant_total_units_txt = wx.StaticText(self, -1, unit_invariant,
1752                                                  size=(80, -1))
1753
[9ce7641c]1754        #Invariant total
1755        iy = 0
[c128284]1756        ix = 0
[4a2b054]1757        self.invariant_sizer.Add(invariant_total_txt, (iy, ix), (1, 1),
[1128bd31]1758                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
[9ce7641c]1759        ix += 1
[4a2b054]1760        self.invariant_sizer.Add(self.invariant_total_tcl, (iy, ix), (1, 1),
[1128bd31]1761                                 wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1762        ix += 1
[1128bd31]1763        self.invariant_sizer.Add(wx.StaticText(self, -1, uncertainty),
1764                                 (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1765        ix += 1
[4a2b054]1766        self.invariant_sizer.Add(self.invariant_total_err_tcl, (iy, ix), (1, 1),
[1128bd31]1767                                 wx.EXPAND | wx.ADJUST_MINSIZE, 10)
[c128284]1768        ix += 1
[1128bd31]1769        self.invariant_sizer.Add(invariant_total_units_txt, (iy, ix), (1, 1),
1770                                 wx.EXPAND | wx.ADJUST_MINSIZE, 10)
1771
[d0cc0bbc]1772    def _layout_inputs_sizer(self):
1773        """
[d7a39e5]1774        Draw widgets related to inputs
[d0cc0bbc]1775        """
[1128bd31]1776        contrast_txt = wx.StaticText(self, -1, 'Contrast:')
[7d16278]1777        self.contrast_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
[0d417ac8]1778                                        style=0, name='contrast_tcl')
[7d16278]1779        wx.EVT_TEXT(self, self.contrast_tcl.GetId(), self._on_text)
1780        contrast_hint_txt = "Contrast"
1781        self.contrast_tcl.SetToolTipString(contrast_hint_txt)
[1128bd31]1782        contrast_unit_txt = wx.StaticText(self, -1, '[1/A^2]', size=(40, -1))
1783        porod_const_txt = wx.StaticText(self, -1,
1784                                        'Porod\nConstant:\n(optional)\n')
1785        porod_unit_txt = wx.StaticText(self, -1, '[1/(cm*A^4)]', size=(80, -1))
1786        self.porod_constant_tcl = InvTextCtrl(self, -1,
[7d16278]1787                                              size=(_BOX_WIDTH, 20), style=0,
[1128bd31]1788                                              name='porod_constant_tcl')
[7d16278]1789        wx.EVT_TEXT(self, self.porod_constant_tcl.GetId(), self._on_text)
1790        porod_const_hint_txt = "Porod Constant"
1791        self.porod_constant_tcl.SetToolTipString(porod_const_hint_txt)
[1128bd31]1792
1793        background_txt = wx.StaticText(self, -1, 'Background:')
[7d16278]1794        self.background_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
[1128bd31]1795                                          style=0, name='background_tcl')
[7d16278]1796        wx.EVT_TEXT(self, self.background_tcl.GetId(), self._on_text)
1797        background_hint_txt = "Background"
1798        self.background_tcl.SetToolTipString(background_hint_txt)
[1128bd31]1799        background_unit_txt = wx.StaticText(self, -1, '[1/cm]')
1800        scale_txt = wx.StaticText(self, -1, 'Scale:')
[7d16278]1801        self.scale_tcl = InvTextCtrl(self, -1, size=(_BOX_WIDTH, 20), style=0,
1802                                     name='scale_tcl')
1803        wx.EVT_TEXT(self, self.scale_tcl.GetId(), self._on_text)
1804        scale_hint_txt = "Scale"
1805        self.scale_tcl.SetToolTipString(scale_hint_txt)
[1128bd31]1806        self.sizer_input.AddMany([(background_txt, 0, wx.LEFT | wx.BOTTOM, 5),
1807                                  (self.background_tcl, 0, wx.LEFT | wx.BOTTOM, 5),
1808                                  (background_unit_txt, 0, wx.LEFT | wx.BOTTOM, 5),
1809                                  (scale_txt, 0, wx.LEFT | wx.BOTTOM, 10),
1810                                  (self.scale_tcl, 0, wx.LEFT | wx.BOTTOM | wx.RIGHT, 5),
1811                                  (10, 10),
1812                                  (contrast_txt, 0, wx.LEFT | wx.BOTTOM, 5),
1813                                  (self.contrast_tcl, 0, wx.LEFT | wx.BOTTOM, 5),
1814                                  (contrast_unit_txt, 0, wx.LEFT | wx.BOTTOM, 5),
1815                                  (porod_const_txt, 0, wx.LEFT, 10),
1816                                  (self.porod_constant_tcl, 0, wx.LEFT | wx.BOTTOM | wx.RIGHT, 5),
1817                                  (porod_unit_txt, 0, wx.LEFT | wx.BOTTOM, 5)])
[7d16278]1818        self.inputs_sizer.Add(self.sizer_input)
[1128bd31]1819
[9ce7641c]1820    def _layout_outputs_sizer(self):
1821        """
[d7a39e5]1822        Draw widgets related to outputs
[9ce7641c]1823        """
1824        self._layout_volume_surface_sizer()
1825        self._layout_invariant_sizer()
1826        static_line = wx.StaticLine(self, -1)
[7d16278]1827        self.outputs_sizer.AddMany([(self.volume_surface_sizer,
[1128bd31]1828                                     0, wx.TOP | wx.BOTTOM, 10),
[9ce7641c]1829                                    (static_line, 0, wx.EXPAND, 0),
[1128bd31]1830                                    (self.invariant_sizer, 0, wx.TOP | wx.BOTTOM, 10)])
1831    def _layout_button(self):
[9ce7641c]1832        """
[d7a39e5]1833        Do the layout for the button widgets
[1128bd31]1834        """
[9ce7641c]1835        #compute button
[c128284]1836        id = wx.NewId()
[4a2b054]1837        self.button_calculate = wx.Button(self, id, "Compute",
1838                                          name='compute_invariant')
[d0cc0bbc]1839        self.button_calculate.SetToolTipString("Compute invariant")
[1128bd31]1840        self.Bind(wx.EVT_BUTTON, self.compute_invariant, id=id)
[9ce7641c]1841        #detail button
1842        id = wx.NewId()
[d0cc0bbc]1843        self.button_details = wx.Button(self, id, "Details?")
[cce0ad3]1844        hint_msg = "Get more details of computation such as fraction from extrapolation"
[4a2b054]1845        self.button_details.SetToolTipString(hint_msg)
[9ce7641c]1846        self.Bind(wx.EVT_BUTTON, self.display_details, id=id)
[cce0ad3]1847        #help button
1848        id = wx.NewId()
1849        self.button_help = wx.Button(self, id, "HELP")
1850        self.button_help.SetToolTipString("Invariant Documentation")
1851        self.Bind(wx.EVT_BUTTON, self.on_help, id=id)
1852        self.button_sizer.AddMany([((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0),
[1128bd31]1853                                   (self.button_details, 0, wx.ALL, 10),
1854                                   (self.button_calculate, 0,
[cce0ad3]1855                                    wx.RIGHT | wx.TOP | wx.BOTTOM, 10),
1856                                   (self.button_help, 0, 
1857                                    wx.RIGHT | wx.TOP | wx.BOTTOM, 10),])
[9ce7641c]1858    def _do_layout(self):
1859        """
[d7a39e5]1860        Draw window content
[9ce7641c]1861        """
1862        self._define_structure()
1863        self._layout_data_name()
1864        self._layout_extrapolation()
[d0cc0bbc]1865        self._layout_inputs_sizer()
[9ce7641c]1866        self._layout_outputs_sizer()
1867        self._layout_button()
[699df7f]1868        self.main_sizer.AddMany([(self.data_name_boxsizer, 0, wx.ALL, 10),
[1128bd31]1869                                 (self.outputs_sizer, 0,
1870                                  wx.LEFT | wx.RIGHT | wx.BOTTOM, 10),
1871                                 (self.button_sizer, 0, wx.LEFT | wx.RIGHT, 15),
[d0cc0bbc]1872                                 (self.inputs_sizer, 0,
[1128bd31]1873                                  wx.LEFT | wx.RIGHT | wx.BOTTOM, 10),
1874                                 (self.extrapolation_sizer, 0,
1875                                  wx.LEFT | wx.RIGHT | wx.BOTTOM, 10)])
[9ce7641c]1876        self.SetSizer(self.main_sizer)
[355b684]1877        self.SetAutoLayout(True)
[cce0ad3]1878       
1879    def on_help(self, event):
1880        """
1881        Bring up the Invariant Documentation whenever the HELP button is
1882        clicked.
1883
1884        Calls DocumentationWindow with the path of the location within the
1885        documentation tree (after /doc/ ....".  Note that when using old
1886        versions of Wx (before 2.9) and thus not the release version of
1887        installers, the help comes up at the top level of the file as
1888        webbrowser does not pass anything past the # to the browser when it is
1889        running "file:///...."
1890
1891    :param evt: Triggers on clicking the help button
1892    """
1893
1894        _TreeLocation = "user/perspectives/invariant/invariant_help.html"
[3db44fb]1895        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "",
1896                                          "Invariant Help")
[1128bd31]1897
1898
[c128284]1899class InvariantDialog(wx.Dialog):
[d7a39e5]1900    """
[699df7f]1901    Invariant Dialog
[d7a39e5]1902    """
[0d417ac8]1903    def __init__(self, parent=None, id=1, graph=None,
1904                 data=None, title="Invariant", base=None):
[272d91e]1905        wx.Dialog.__init__(self, parent, id, title, size=(PANEL_WIDTH,
[1128bd31]1906                                                          PANEL_HEIGHT))
[272d91e]1907        self.panel = InvariantPanel(self)
[c128284]1908        self.Centre()
1909        self.Show(True)
[1128bd31]1910
[c128284]1911class InvariantWindow(wx.Frame):
[d7a39e5]1912    """
[699df7f]1913    Invariant Window
[d7a39e5]1914    """
[1128bd31]1915    def __init__(self, parent=None, id=1, graph=None,
[4a2b054]1916                 data=None, title="Invariant", base=None):
[1128bd31]1917
1918        wx.Frame.__init__(self, parent, id, title, size=(PANEL_WIDTH + 100,
1919                                                         PANEL_HEIGHT + 100))
[79492222]1920        from sas.dataloader.loader import  Loader
[4e1c362]1921        self.loader = Loader()
[b9a5f0e]1922        path = "C:/ECLPS/workspace/trunk/sasdataloader/test/ascii_test_3.txt"
[699df7f]1923        data = self.loader.load(path)
[272d91e]1924        self.panel = InvariantPanel(self)
[4e1c362]1925
1926        data.name = data.filename
[210ff4f]1927        self.panel.set_data(data)
[c128284]1928        self.Centre()
1929        self.Show(True)
[1128bd31]1930
[c128284]1931class MyApp(wx.App):
[699df7f]1932    """
1933    Test App
1934    """
[c128284]1935    def OnInit(self):
[699df7f]1936        """
1937        Init
1938        """
[c128284]1939        wx.InitAllImageHandlers()
1940        frame = InvariantWindow()
1941        frame.Show(True)
1942        self.SetTopWindow(frame)
[1128bd31]1943
[c128284]1944        return True
[1128bd31]1945
[c128284]1946# end of class MyApp
1947
1948if __name__ == "__main__":
1949    app = MyApp(0)
[1128bd31]1950    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.