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

ticket-1094-headless
Last change on this file since cb44d66 was 44e8f48, checked in by krzywon, 6 years ago

Be sure image handler is initialized in invariant panel and code cleanup.

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