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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since ee6d4ad was c12f9b4, checked in by butler, 9 years ago

Linked new help to invariant panel

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