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

Last change on this file since 6d7b252b was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

improved support for py37 in sasgui

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