source: sasview/src/sas/sasgui/perspectives/invariant/invariant_panel.py @ ca3f89b

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since ca3f89b was ca3f89b, checked in by Piotr Rozyczko <rozyczko@…>, 8 years ago

Resolves #644 and #642: Improved loading performance for cansas XML data. Loading invariants and fits from save states no longer throw errors or open multiple windows.

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