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

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

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

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