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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 2469df7 was 2469df7, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

lint: update 'if x==True/False?' to 'if x/not x:'

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