summaryrefslogtreecommitdiff
path: root/gr-wxgui/src/python/forms/forms.py
diff options
context:
space:
mode:
Diffstat (limited to 'gr-wxgui/src/python/forms/forms.py')
-rw-r--r--gr-wxgui/src/python/forms/forms.py641
1 files changed, 641 insertions, 0 deletions
diff --git a/gr-wxgui/src/python/forms/forms.py b/gr-wxgui/src/python/forms/forms.py
new file mode 100644
index 000000000..10f6a4823
--- /dev/null
+++ b/gr-wxgui/src/python/forms/forms.py
@@ -0,0 +1,641 @@
+#
+# Copyright 2009 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+"""
+The forms module contains general purpose wx-gui forms for gnuradio apps.
+
+The forms follow a layered model:
+ * internal layer
+ * deals with the wxgui objects directly
+ * implemented in event handler and update methods
+ * translation layer
+ * translates the between the external and internal layers
+ * handles parsing errors between layers
+ * external layer
+ * provided external access to the user
+ * set_value, get_value, and optional callback
+ * set and get through optional pubsub and key
+
+Known problems:
+ * An empty label in the radio box still consumes space.
+ * The static text cannot resize the parent at runtime.
+"""
+
+EXT_KEY = 'external'
+INT_KEY = 'internal'
+
+import wx
+import sys
+from gnuradio.gr.pubsub import pubsub
+import converters
+
+EVT_DATA = wx.PyEventBinder(wx.NewEventType())
+class DataEvent(wx.PyEvent):
+ def __init__(self, data):
+ wx.PyEvent.__init__(self, wx.NewId(), EVT_DATA.typeId)
+ self.data = data
+
+def make_bold(widget):
+ font = widget.GetFont()
+ font.SetWeight(wx.FONTWEIGHT_BOLD)
+ widget.SetFont(font)
+
+########################################################################
+# Base Class Form
+########################################################################
+class _form_base(pubsub, wx.BoxSizer):
+ def __init__(self, parent=None, sizer=None, proportion=0, flag=wx.EXPAND, ps=None, key='', value=None, callback=None, converter=converters.identity_converter()):
+ pubsub.__init__(self)
+ wx.BoxSizer.__init__(self, wx.HORIZONTAL)
+ self._parent = parent
+ self._key = key
+ self._converter = converter
+ self._callback = callback
+ self._widgets = list()
+ #add to the sizer if provided
+ if sizer: sizer.Add(self, proportion, flag)
+ #proxy the pubsub and key into this form
+ if ps is not None:
+ assert key
+ self.proxy(EXT_KEY, ps, key)
+ #no pubsub passed, must set initial value
+ else: self.set_value(value)
+
+ def __str__(self):
+ return "Form: %s -> %s"%(self.__class__, self._key)
+
+ def _add_widget(self, widget, label='', flag=0, label_prop=0, widget_prop=1):
+ """
+ Add the main widget to this object sizer.
+ If label is passed, add a label as well.
+ Register the widget and the label in the widgets list (for enable/disable).
+ Bind the update handler to the widget for data events.
+ This ensures that the gui thread handles updating widgets.
+ Setup the pusub triggers for external and internal.
+ @param widget the main widget
+ @param label the optional label
+ @param flag additional flags for widget
+ @param label_prop the proportion for the label
+ @param widget_prop the proportion for the widget
+ """
+ #setup data event
+ widget.Bind(EVT_DATA, lambda x: self._update(x.data))
+ update = lambda x: wx.PostEvent(widget, DataEvent(x))
+ #register widget
+ self._widgets.append(widget)
+ #create optional label
+ if not label: self.Add(widget, widget_prop, wx.ALIGN_CENTER_VERTICAL | flag)
+ else:
+ label_text = wx.StaticText(self._parent, label='%s: '%label)
+ self._widgets.append(label_text)
+ self.Add(label_text, label_prop, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ self.Add(widget, widget_prop, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | flag)
+ #initialize without triggering pubsubs
+ self._translate_external_to_internal(self[EXT_KEY])
+ update(self[INT_KEY])
+ #subscribe all the functions
+ self.subscribe(INT_KEY, update)
+ self.subscribe(INT_KEY, self._translate_internal_to_external)
+ self.subscribe(EXT_KEY, self._translate_external_to_internal)
+ if self._callback: self.subscribe(EXT_KEY, self._callback)
+
+ def _translate_external_to_internal(self, external):
+ try:
+ internal = self._converter.external_to_internal(external)
+ #prevent infinite loop between internal and external pubsub keys by only setting if changed
+ if self[INT_KEY] != internal: self[INT_KEY] = internal
+ except Exception, e:
+ self._err_msg(external, e)
+ self[INT_KEY] = self[INT_KEY] #reset to last good setting
+
+ def _translate_internal_to_external(self, internal):
+ try:
+ external = self._converter.internal_to_external(internal)
+ #prevent infinite loop between internal and external pubsub keys by only setting if changed
+ if self[EXT_KEY] != external: self[EXT_KEY] = external
+ except Exception, e:
+ self._err_msg(internal, e)
+ self[EXT_KEY] = self[EXT_KEY] #reset to last good setting
+
+ def _err_msg(self, value, e):
+ print >> sys.stderr, self, 'Error translating value: "%s"\n\t%s\n\t%s'%(value, e, self._converter.help())
+
+ #override in subclasses to handle the wxgui object
+ def _update(self, value): raise NotImplementedError
+ def _handle(self, event): raise NotImplementedError
+
+ #provide a set/get interface for this form
+ def get_value(self): return self[EXT_KEY]
+ def set_value(self, value): self[EXT_KEY] = value
+
+ def Disable(self, disable=True): self.Enable(not disable)
+ def Enable(self, enable=True):
+ if enable:
+ for widget in self._widgets: widget.Enable()
+ else:
+ for widget in self._widgets: widget.Disable()
+
+########################################################################
+# Base Class Chooser Form
+########################################################################
+class _chooser_base(_form_base):
+ def __init__(self, choices=[], labels=None, **kwargs):
+ _form_base.__init__(self, converter=converters.chooser_converter(choices), **kwargs)
+ self._choices = choices
+ self._labels = map(str, labels or choices)
+
+########################################################################
+# Base Class Slider Form
+########################################################################
+class _slider_base(_form_base):
+ def __init__(self, label='', length=-1, converter=None, num_steps=100, style=wx.SL_HORIZONTAL, **kwargs):
+ _form_base.__init__(self, converter=converter, **kwargs)
+ if style & wx.SL_HORIZONTAL: slider_size = wx.Size(length, -1)
+ elif style & wx.SL_VERTICAL: slider_size = wx.Size(-1, length)
+ else: raise NotImplementedError
+ self._slider = wx.Slider(self._parent, minValue=0, maxValue=num_steps, size=slider_size, style=style)
+ self._slider.Bind(wx.EVT_SCROLL, self._handle)
+ self._add_widget(self._slider, label, flag=wx.EXPAND)
+
+ def _handle(self, event): self[INT_KEY] = self._slider.GetValue()
+ def _update(self, value): self._slider.SetValue(value)
+
+########################################################################
+# Static Text Form
+########################################################################
+class static_text(_form_base):
+ """
+ A text box form.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param label title label for this widget (optional)
+ @param width the width of the form in px
+ @param bold true to bold-ify the text (default=False)
+ @param converter forms.str_converter(), int_converter(), float_converter()...
+ """
+ def __init__(self, label='', width=-1, bold=False, converter=converters.str_converter(), **kwargs):
+ _form_base.__init__(self, converter=converter, **kwargs)
+ self._static_text = wx.StaticText(self._parent, size=wx.Size(width, -1))
+ if bold: make_bold(self._static_text)
+ self._add_widget(self._static_text, label)
+
+ def _update(self, label): self._static_text.SetLabel(label); self._parent.Layout()
+
+########################################################################
+# Text Box Form
+########################################################################
+class text_box(_form_base):
+ """
+ A text box form.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param label title label for this widget (optional)
+ @param width the width of the form in px
+ @param converter forms.str_converter(), int_converter(), float_converter()...
+ """
+ def __init__(self, label='', width=-1, converter=converters.eval_converter(), **kwargs):
+ _form_base.__init__(self, converter=converter, **kwargs)
+ self._text_box = wx.TextCtrl(self._parent, size=wx.Size(width, -1), style=wx.TE_PROCESS_ENTER)
+ self._text_box.Bind(wx.EVT_TEXT_ENTER, self._handle)
+ self._add_widget(self._text_box, label)
+
+ def _handle(self, event): self[INT_KEY] = self._text_box.GetValue()
+ def _update(self, value): self._text_box.SetValue(value)
+
+########################################################################
+# Slider Form
+# Linear Slider
+# Logarithmic Slider
+########################################################################
+class slider(_slider_base):
+ """
+ A generic linear slider.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param label title label for this widget (optional)
+ @param length the length of the slider in px (optional)
+ @param style wx.SL_HORIZONTAL or wx.SL_VERTICAL (default=horizontal)
+ @param minimum the minimum value
+ @param maximum the maximum value
+ @param num_steps the number of slider steps (or specify step_size)
+ @param step_size the step between slider jumps (or specify num_steps)
+ @param cast a cast function, int, or float (default=float)
+ """
+ def __init__(self, minimum=-100, maximum=100, num_steps=100, step_size=None, cast=float, **kwargs):
+ assert step_size or num_steps
+ if step_size is not None: num_steps = (maximum - minimum)/step_size
+ converter = converters.slider_converter(minimum=minimum, maximum=maximum, num_steps=num_steps, cast=cast)
+ _slider_base.__init__(self, converter=converter, num_steps=num_steps, **kwargs)
+
+class log_slider(_slider_base):
+ """
+ A generic logarithmic slider.
+ The sliders min and max values are base**min_exp and base**max_exp.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param label title label for this widget (optional)
+ @param length the length of the slider in px (optional)
+ @param style wx.SL_HORIZONTAL or wx.SL_VERTICAL (default=horizontal)
+ @param min_exp the minimum exponent
+ @param max_exp the maximum exponent
+ @param base the exponent base in base**exp
+ @param num_steps the number of slider steps (or specify step_size)
+ @param step_size the exponent step size (or specify num_steps)
+ """
+ def __init__(self, min_exp=0, max_exp=1, base=10, num_steps=100, step_size=None, **kwargs):
+ assert step_size or num_steps
+ if step_size is not None: num_steps = (max_exp - min_exp)/step_size
+ converter = converters.log_slider_converter(min_exp=min_exp, max_exp=max_exp, num_steps=num_steps, base=base)
+ _slider_base.__init__(self, converter=converter, num_steps=num_steps, **kwargs)
+
+########################################################################
+# Gauge Form
+########################################################################
+class gauge(_form_base):
+ """
+ A gauge bar.
+ The gauge displays floating point values between the minimum and maximum.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param label title label for this widget (optional)
+ @param length the length of the slider in px (optional)
+ @param style wx.GA_HORIZONTAL or wx.GA_VERTICAL (default=horizontal)
+ @param minimum the minimum value
+ @param maximum the maximum value
+ @param num_steps the number of slider steps (or specify step_size)
+ @param step_size the step between slider jumps (or specify num_steps)
+ """
+ def __init__(self, label='', length=-1, minimum=-100, maximum=100, num_steps=100, step_size=None, style=wx.GA_HORIZONTAL, **kwargs):
+ assert step_size or num_steps
+ if step_size is not None: num_steps = (maximum - minimum)/step_size
+ converter = converters.slider_converter(minimum=minimum, maximum=maximum, num_steps=num_steps, cast=float)
+ _form_base.__init__(self, converter=converter, **kwargs)
+ if style & wx.SL_HORIZONTAL: gauge_size = wx.Size(length, -1)
+ elif style & wx.SL_VERTICAL: gauge_size = wx.Size(-1, length)
+ else: raise NotImplementedError
+ self._gauge = wx.Gauge(self._parent, range=num_steps, size=gauge_size, style=style)
+ self._add_widget(self._gauge, label, flag=wx.EXPAND)
+
+ def _update(self, value): self._gauge.SetValue(value)
+
+########################################################################
+# Check Box Form
+########################################################################
+class check_box(_form_base):
+ """
+ Create a check box form.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param true the value for form when checked (default=True)
+ @param false the value for form when unchecked (default=False)
+ @param label title label for this widget (optional)
+ """
+ def __init__(self, label='', true=True, false=False, **kwargs):
+ _form_base.__init__(self, converter=converters.bool_converter(true=true, false=false), **kwargs)
+ self._check_box = wx.CheckBox(self._parent, style=wx.CHK_2STATE, label=label)
+ self._check_box.Bind(wx.EVT_CHECKBOX, self._handle)
+ self._add_widget(self._check_box)
+
+ def _handle(self, event): self[INT_KEY] = self._check_box.IsChecked()
+ def _update(self, checked): self._check_box.SetValue(checked)
+
+########################################################################
+# Drop Down Chooser Form
+########################################################################
+class drop_down(_chooser_base):
+ """
+ Create a drop down menu form.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param choices list of possible values
+ @param labels list of labels for each choice (default=choices)
+ @param label title label for this widget (optional)
+ @param width the form width in px (optional)
+ """
+ def __init__(self, label='', width=-1, **kwargs):
+ _chooser_base.__init__(self, **kwargs)
+ self._drop_down = wx.Choice(self._parent, choices=self._labels, size=wx.Size(width, -1))
+ self._drop_down.Bind(wx.EVT_CHOICE, self._handle)
+ self._add_widget(self._drop_down, label, widget_prop=0, label_prop=1)
+
+ def _handle(self, event): self[INT_KEY] = self._drop_down.GetSelection()
+ def _update(self, i): self._drop_down.SetSelection(i)
+
+########################################################################
+# Button Chooser Form
+# Circularly move through the choices with each click.
+# Can be a single-click button with one choice.
+# Can be a 2-state button with two choices.
+########################################################################
+class button(_chooser_base):
+ """
+ Create a multi-state button.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param choices list of possible values
+ @param labels list of labels for each choice (default=choices)
+ @param width the width of the button in pixels (optional)
+ @param style style arguments (optional)
+ @param label title label for this widget (optional)
+ """
+ def __init__(self, label='', style=0, width=-1, **kwargs):
+ _chooser_base.__init__(self, **kwargs)
+ self._button = wx.Button(self._parent, size=wx.Size(width, -1), style=style)
+ self._button.Bind(wx.EVT_BUTTON, self._handle)
+ self._add_widget(self._button, label, widget_prop=((not style&wx.BU_EXACTFIT) and 1 or 0))
+
+ def _handle(self, event): self[INT_KEY] = (self[INT_KEY] + 1)%len(self._choices) #circularly increment index
+ def _update(self, i): self._button.SetLabel(self._labels[i]); self.Layout()
+
+class toggle_button(button):
+ """
+ Create a dual-state button.
+ This button will alternate between True and False when clicked.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param width the width of the button in pixels (optional)
+ @param style style arguments (optional)
+ @param true_label the button's label in the true state
+ @param false_label the button's label in the false state
+ """
+ def __init__(self, true_label='On (click to stop)', false_label='Off (click to start)', **kwargs):
+ button.__init__(self, choices=[True, False], labels=[true_label, false_label], **kwargs)
+
+class single_button(toggle_button):
+ """
+ Create a single state button.
+ This button will callback() when clicked.
+ For use when state holding is not important.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param width the width of the button in pixels (optional)
+ @param style style arguments (optional)
+ @param label the button's label
+ """
+ def __init__(self, label='click for callback', **kwargs):
+ toggle_button.__init__(self, true_label=label, false_label=label, value=True, **kwargs)
+
+########################################################################
+# Radio Buttons Chooser Form
+########################################################################
+class radio_buttons(_chooser_base):
+ """
+ Create a radio button form.
+ @param parent the parent widget
+ @param sizer add this widget to sizer if provided (optional)
+ @param proportion the proportion when added to the sizer (default=0)
+ @param flag the flag argument when added to the sizer (default=wx.EXPAND)
+ @param ps the pubsub object (optional)
+ @param key the pubsub key (optional)
+ @param value the default value (optional)
+ @param choices list of possible values
+ @param labels list of labels for each choice (default=choices)
+ @param major_dimension the number of rows/cols (default=auto)
+ @param label title label for this widget (optional)
+ @param style useful style args: wx.RA_HORIZONTAL, wx.RA_VERTICAL, wx.NO_BORDER (default=wx.RA_HORIZONTAL)
+ """
+ def __init__(self, style=wx.RA_HORIZONTAL, label='', major_dimension=0, **kwargs):
+ _chooser_base.__init__(self, **kwargs)
+ #create radio buttons
+ self._radio_buttons = wx.RadioBox(self._parent, choices=self._labels, style=style, label=label, majorDimension=major_dimension)
+ self._radio_buttons.Bind(wx.EVT_RADIOBOX, self._handle)
+ self._add_widget(self._radio_buttons)
+
+ def _handle(self, event): self[INT_KEY] = self._radio_buttons.GetSelection()
+ def _update(self, i): self._radio_buttons.SetSelection(i)
+
+########################################################################
+# Notebook Chooser Form
+# The notebook pages/tabs are for selecting between choices.
+# A page must be added to the notebook for each choice.
+########################################################################
+class notebook(_chooser_base):
+ def __init__(self, pages, notebook, **kwargs):
+ _chooser_base.__init__(self, **kwargs)
+ assert len(pages) == len(self._choices)
+ self._notebook = notebook
+ self._notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._handle)
+ #add pages, setting the label on each tab
+ for i, page in enumerate(pages):
+ self._notebook.AddPage(page, self._labels[i])
+ self._add_widget(self._notebook)
+
+ def _handle(self, event): self[INT_KEY] = self._notebook.GetSelection()
+ def _update(self, i): self._notebook.SetSelection(i)
+
+# ----------------------------------------------------------------
+# Stand-alone test application
+# ----------------------------------------------------------------
+
+import wx
+from gnuradio.wxgui import gui
+
+class app_gui (object):
+ def __init__(self, frame, panel, vbox, top_block, options, args):
+
+ def callback(v): print v
+
+ radio_buttons(
+ sizer=vbox,
+ parent=panel,
+ choices=[2, 4, 8, 16],
+ labels=['two', 'four', 'eight', 'sixteen'],
+ value=4,
+ style=wx.RA_HORIZONTAL,
+ label='test radio long string',
+ callback=callback,
+ #major_dimension = 2,
+ )
+
+ radio_buttons(
+ sizer=vbox,
+ parent=panel,
+ choices=[2, 4, 8, 16],
+ labels=['two', 'four', 'eight', 'sixteen'],
+ value=4,
+ style=wx.RA_VERTICAL,
+ label='test radio long string',
+ callback=callback,
+ #major_dimension = 2,
+ )
+
+ radio_buttons(
+ sizer=vbox,
+ parent=panel,
+ choices=[2, 4, 8, 16],
+ labels=['two', 'four', 'eight', 'sixteen'],
+ value=4,
+ style=wx.RA_VERTICAL | wx.NO_BORDER,
+ callback=callback,
+ #major_dimension = 2,
+ )
+
+ button(
+ sizer=vbox,
+ parent=panel,
+ choices=[2, 4, 8, 16],
+ labels=['two', 'four', 'eight', 'sixteen'],
+ value=2,
+ label='button value',
+ callback=callback,
+ #width=100,
+ )
+
+
+ drop_down(
+ sizer=vbox,
+ parent=panel,
+ choices=[2, 4, 8, 16],
+ value=2,
+ label='Choose One',
+ callback=callback,
+ )
+ check_box(
+ sizer=vbox,
+ parent=panel,
+ value=False,
+ label='check me',
+ callback=callback,
+ )
+ text_box(
+ sizer=vbox,
+ parent=panel,
+ value=3,
+ label='text box',
+ callback=callback,
+ width=200,
+ )
+
+ static_text(
+ sizer=vbox,
+ parent=panel,
+ value='bob',
+ label='static text',
+ width=-1,
+ bold=True,
+ )
+
+ slider(
+ sizer=vbox,
+ parent=panel,
+ value=12,
+ label='slider',
+ callback=callback,
+ )
+
+ log_slider(
+ sizer=vbox,
+ parent=panel,
+ value=12,
+ label='slider',
+ callback=callback,
+ )
+
+ slider(
+ sizer=vbox,
+ parent=panel,
+ value=12,
+ label='slider',
+ callback=callback,
+ style=wx.SL_VERTICAL,
+ length=30,
+ )
+
+ toggle_button(
+ sizer=vbox,
+ parent=panel,
+ value=True,
+ label='toggle it',
+ callback=callback,
+ )
+
+ single_button(
+ sizer=vbox,
+ parent=panel,
+ label='sig test',
+ callback=callback,
+ )
+
+if __name__ == "__main__":
+ try:
+
+ # Create the GUI application
+ app = gui.app(
+ gui=app_gui, # User interface class
+ title="Test Forms", # Top window title
+ )
+
+ # And run it
+ app.MainLoop()
+
+ except RuntimeError, e:
+ print e
+ sys.exit(1)