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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since cf67a45 was 18b7ecb9, checked in by Piotr Rozyczko <rozyczko@…>, 8 years ago

Save Analysis improvements. Fixed #629

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