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

Last change on this file since 65d1d04 was 18b7ecb9, checked in by Piotr Rozyczko <rozyczko@…>, 8 years ago

Save Analysis improvements. Fixed #629

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