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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249unittest-saveload
Last change on this file since d0ce666f was fa412df, checked in by krzywon, 6 years ago

Simplify calling image handler and add documentation.

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