switch source package format to 3.0 quilt
[debian/gnuradio] / gr-wxgui / src / python / forms / forms.py
1 #
2 # Copyright 2009 Free Software Foundation, Inc.
3 #
4 # This file is part of GNU Radio
5 #
6 # GNU Radio is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3, or (at your option)
9 # any later version.
10 #
11 # GNU Radio is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with GNU Radio; see the file COPYING.  If not, write to
18 # the Free Software Foundation, Inc., 51 Franklin Street,
19 # Boston, MA 02110-1301, USA.
20 #
21
22 """
23 The forms module contains general purpose wx-gui forms for gnuradio apps.
24
25 The forms follow a layered model:
26   * internal layer
27     * deals with the wxgui objects directly
28     * implemented in event handler and update methods
29   * translation layer
30     * translates the between the external and internal layers
31     * handles parsing errors between layers
32   * external layer 
33     * provided external access to the user
34     * set_value, get_value, and optional callback
35     * set and get through optional pubsub and key
36
37 Known problems:
38   * An empty label in the radio box still consumes space.
39   * The static text cannot resize the parent at runtime.
40 """
41
42 EXT_KEY = 'external'
43 INT_KEY = 'internal'
44
45 import wx
46 import sys
47 from gnuradio.gr.pubsub import pubsub
48 import converters
49
50 EVT_DATA = wx.PyEventBinder(wx.NewEventType())
51 class DataEvent(wx.PyEvent):
52         def __init__(self, data):
53                 wx.PyEvent.__init__(self, wx.NewId(), EVT_DATA.typeId)
54                 self.data = data
55
56 def make_bold(widget):
57         font = widget.GetFont()
58         font.SetWeight(wx.FONTWEIGHT_BOLD)
59         widget.SetFont(font)
60
61 ########################################################################
62 # Base Class Form
63 ########################################################################
64 class _form_base(pubsub, wx.BoxSizer):
65         def __init__(self, parent=None, sizer=None, proportion=0, flag=wx.EXPAND, ps=None, key='', value=None, callback=None, converter=converters.identity_converter()):
66                 pubsub.__init__(self)
67                 wx.BoxSizer.__init__(self, wx.HORIZONTAL)
68                 self._parent = parent
69                 self._key = key
70                 self._converter = converter
71                 self._callback = callback
72                 self._widgets = list()
73                 #add to the sizer if provided
74                 if sizer: sizer.Add(self, proportion, flag)
75                 #proxy the pubsub and key into this form
76                 if ps is not None:
77                         assert key
78                         self.proxy(EXT_KEY, ps, key)
79                 #no pubsub passed, must set initial value
80                 else: self.set_value(value)
81
82         def __str__(self):
83                 return "Form: %s -> %s"%(self.__class__, self._key)
84
85         def _add_widget(self, widget, label='', flag=0, label_prop=0, widget_prop=1):
86                 """
87                 Add the main widget to this object sizer.
88                 If label is passed, add a label as well.
89                 Register the widget and the label in the widgets list (for enable/disable).
90                 Bind the update handler to the widget for data events.
91                 This ensures that the gui thread handles updating widgets.
92                 Setup the pusub triggers for external and internal.
93                 @param widget the main widget
94                 @param label the optional label
95                 @param flag additional flags for widget
96                 @param label_prop the proportion for the label
97                 @param widget_prop the proportion for the widget
98                 """
99                 #setup data event
100                 widget.Bind(EVT_DATA, lambda x: self._update(x.data))
101                 update = lambda x: wx.PostEvent(widget, DataEvent(x))
102                 #register widget
103                 self._widgets.append(widget)
104                 #create optional label
105                 if not label: self.Add(widget, widget_prop, wx.ALIGN_CENTER_VERTICAL | flag)
106                 else:
107                         label_text = wx.StaticText(self._parent, label='%s: '%label)
108                         self._widgets.append(label_text)
109                         self.Add(label_text, label_prop, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
110                         self.Add(widget, widget_prop, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | flag)
111                 #initialize without triggering pubsubs
112                 self._translate_external_to_internal(self[EXT_KEY])
113                 update(self[INT_KEY])
114                 #subscribe all the functions
115                 self.subscribe(INT_KEY, update)
116                 self.subscribe(INT_KEY, self._translate_internal_to_external)
117                 self.subscribe(EXT_KEY, self._translate_external_to_internal)
118
119         def _translate_external_to_internal(self, external):
120                 try:
121                         internal = self._converter.external_to_internal(external)
122                         #prevent infinite loop between internal and external pubsub keys by only setting if changed
123                         if self[INT_KEY] != internal: self[INT_KEY] = internal
124                 except Exception, e:
125                         self._err_msg(external, e)
126                         self[INT_KEY] = self[INT_KEY] #reset to last good setting
127
128         def _translate_internal_to_external(self, internal):
129                 try:
130                         external = self._converter.internal_to_external(internal)
131                         #prevent infinite loop between internal and external pubsub keys by only setting if changed
132                         if self[EXT_KEY] != external: self[EXT_KEY] = external
133                 except Exception, e:
134                         self._err_msg(internal, e)
135                         self[EXT_KEY] = self[EXT_KEY] #reset to last good setting
136                 if self._callback: self._callback(self[EXT_KEY])
137
138         def _err_msg(self, value, e):
139                 print >> sys.stderr, self, 'Error translating value: "%s"\n\t%s\n\t%s'%(value, e, self._converter.help())
140
141         #override in subclasses to handle the wxgui object
142         def _update(self, value): raise NotImplementedError
143         def _handle(self, event): raise NotImplementedError
144
145         #provide a set/get interface for this form
146         def get_value(self): return self[EXT_KEY]
147         def set_value(self, value): self[EXT_KEY] = value
148
149         def Disable(self, disable=True): self.Enable(not disable)
150         def Enable(self, enable=True):
151                 if enable:
152                         for widget in self._widgets: widget.Enable()
153                 else:
154                         for widget in self._widgets: widget.Disable()
155
156 ########################################################################
157 # Base Class Chooser Form
158 ########################################################################
159 class _chooser_base(_form_base):
160         def __init__(self, choices=[], labels=None, **kwargs):
161                 _form_base.__init__(self, converter=converters.chooser_converter(choices), **kwargs)
162                 self._choices = choices
163                 self._labels = map(str, labels or choices)
164
165 ########################################################################
166 # Base Class Slider Form
167 ########################################################################
168 class _slider_base(_form_base):
169         def __init__(self, label='', length=-1, converter=None, num_steps=100, style=wx.SL_HORIZONTAL, **kwargs):
170                 _form_base.__init__(self, converter=converter, **kwargs)
171                 if style & wx.SL_HORIZONTAL: slider_size = wx.Size(length, -1)
172                 elif style & wx.SL_VERTICAL: slider_size = wx.Size(-1, length)
173                 else: raise NotImplementedError
174                 self._slider = wx.Slider(self._parent, minValue=0, maxValue=num_steps, size=slider_size, style=style)
175                 self._slider.Bind(wx.EVT_SCROLL, self._handle)
176                 self._add_widget(self._slider, label, flag=wx.EXPAND)
177
178         def _handle(self, event): self[INT_KEY] = self._slider.GetValue()
179         def _update(self, value): self._slider.SetValue(int(round(value)))
180
181 ########################################################################
182 # Static Text Form
183 ########################################################################
184 class static_text(_form_base):
185         """
186         A text box form.
187         @param parent the parent widget
188         @param sizer add this widget to sizer if provided (optional)
189         @param proportion the proportion when added to the sizer (default=0)
190         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
191         @param ps the pubsub object (optional)
192         @param key the pubsub key (optional)
193         @param value the default value (optional)
194         @param label title label for this widget (optional)
195         @param width the width of the form in px
196         @param bold true to bold-ify the text (default=False)
197         @param units a suffix to add after the text
198         @param converter forms.str_converter(), int_converter(), float_converter()...
199         """
200         def __init__(self, label='', width=-1, bold=False, units='', converter=converters.str_converter(), **kwargs):
201                 self._units = units
202                 _form_base.__init__(self, converter=converter, **kwargs)
203                 self._static_text = wx.StaticText(self._parent, size=wx.Size(width, -1))
204                 if bold: make_bold(self._static_text)
205                 self._add_widget(self._static_text, label)
206
207         def _update(self, label):
208                         if self._units: label += ' ' + self._units
209                         self._static_text.SetLabel(label); self._parent.Layout()
210
211 ########################################################################
212 # Text Box Form
213 ########################################################################
214 class text_box(_form_base):
215         """
216         A text box form.
217         @param parent the parent widget
218         @param sizer add this widget to sizer if provided (optional)
219         @param proportion the proportion when added to the sizer (default=0)
220         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
221         @param ps the pubsub object (optional)
222         @param key the pubsub key (optional)
223         @param value the default value (optional)
224         @param label title label for this widget (optional)
225         @param width the width of the form in px
226         @param converter forms.str_converter(), int_converter(), float_converter()...
227         """
228         def __init__(self, label='', width=-1, converter=converters.eval_converter(), **kwargs):
229                 _form_base.__init__(self, converter=converter, **kwargs)
230                 self._text_box = wx.TextCtrl(self._parent, size=wx.Size(width, -1), style=wx.TE_PROCESS_ENTER)
231                 self._default_bg_colour = self._text_box.GetBackgroundColour()
232                 self._text_box.Bind(wx.EVT_TEXT_ENTER, self._handle)
233                 self._text_box.Bind(wx.EVT_TEXT, self._update_color)
234                 self._add_widget(self._text_box, label)
235
236         def _update_color(self, *args):
237                 if self._text_box.GetValue() == self[INT_KEY]:
238                         self._text_box.SetBackgroundColour(self._default_bg_colour)
239                 else: self._text_box.SetBackgroundColour('#EEDDDD')
240
241         def _handle(self, event): self[INT_KEY] = self._text_box.GetValue()
242         def _update(self, value): self._text_box.SetValue(value); self._update_color()
243
244 ########################################################################
245 # Slider Form
246 #  Linear Slider
247 #  Logarithmic Slider
248 ########################################################################
249 class slider(_slider_base):
250         """
251         A generic linear slider.
252         @param parent the parent widget
253         @param sizer add this widget to sizer if provided (optional)
254         @param proportion the proportion when added to the sizer (default=0)
255         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
256         @param ps the pubsub object (optional)
257         @param key the pubsub key (optional)
258         @param value the default value (optional)
259         @param label title label for this widget (optional)
260         @param length the length of the slider in px (optional)
261         @param style wx.SL_HORIZONTAL or wx.SL_VERTICAL (default=horizontal)
262         @param minimum the minimum value
263         @param maximum the maximum value
264         @param num_steps the number of slider steps (or specify step_size)
265         @param step_size the step between slider jumps (or specify num_steps)
266         @param cast a cast function, int, or float (default=float)
267         """
268         def __init__(self, minimum=-100, maximum=100, num_steps=100, step_size=None, cast=float, **kwargs):
269                 assert step_size or num_steps
270                 if step_size is not None: num_steps = (maximum - minimum)/step_size
271                 converter = converters.slider_converter(minimum=minimum, maximum=maximum, num_steps=num_steps, cast=cast)
272                 _slider_base.__init__(self, converter=converter, num_steps=num_steps, **kwargs)
273
274 class log_slider(_slider_base):
275         """
276         A generic logarithmic slider.
277         The sliders min and max values are base**min_exp and base**max_exp.
278         @param parent the parent widget
279         @param sizer add this widget to sizer if provided (optional)
280         @param proportion the proportion when added to the sizer (default=0)
281         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
282         @param ps the pubsub object (optional)
283         @param key the pubsub key (optional)
284         @param value the default value (optional)
285         @param label title label for this widget (optional)
286         @param length the length of the slider in px (optional)
287         @param style wx.SL_HORIZONTAL or wx.SL_VERTICAL (default=horizontal)
288         @param min_exp the minimum exponent
289         @param max_exp the maximum exponent
290         @param base the exponent base in base**exp
291         @param num_steps the number of slider steps (or specify step_size)
292         @param step_size the exponent step size (or specify num_steps)
293         """
294         def __init__(self, min_exp=0, max_exp=1, base=10, num_steps=100, step_size=None, **kwargs):
295                 assert step_size or num_steps
296                 if step_size is not None: num_steps = (max_exp - min_exp)/step_size
297                 converter = converters.log_slider_converter(min_exp=min_exp, max_exp=max_exp, num_steps=num_steps, base=base)
298                 _slider_base.__init__(self, converter=converter, num_steps=num_steps, **kwargs)
299
300 ########################################################################
301 # Gauge Form
302 ########################################################################
303 class gauge(_form_base):
304         """
305         A gauge bar.
306         The gauge displays floating point values between the minimum and maximum.
307         @param parent the parent widget
308         @param sizer add this widget to sizer if provided (optional)
309         @param proportion the proportion when added to the sizer (default=0)
310         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
311         @param ps the pubsub object (optional)
312         @param key the pubsub key (optional)
313         @param value the default value (optional)
314         @param label title label for this widget (optional)
315         @param length the length of the slider in px (optional)
316         @param style wx.GA_HORIZONTAL or wx.GA_VERTICAL (default=horizontal)
317         @param minimum the minimum value
318         @param maximum the maximum value
319         @param num_steps the number of slider steps (or specify step_size)
320         @param step_size the step between slider jumps (or specify num_steps)
321         """
322         def __init__(self, label='', length=-1, minimum=-100, maximum=100, num_steps=100, step_size=None, style=wx.GA_HORIZONTAL, **kwargs):
323                 assert step_size or num_steps
324                 if step_size is not None: num_steps = (maximum - minimum)/step_size
325                 converter = converters.slider_converter(minimum=minimum, maximum=maximum, num_steps=num_steps, cast=float)
326                 _form_base.__init__(self, converter=converter, **kwargs)
327                 if style & wx.SL_HORIZONTAL: gauge_size = wx.Size(length, -1)
328                 elif style & wx.SL_VERTICAL: gauge_size = wx.Size(-1, length)
329                 else: raise NotImplementedError
330                 self._gauge = wx.Gauge(self._parent, range=num_steps, size=gauge_size, style=style)
331                 self._add_widget(self._gauge, label, flag=wx.EXPAND)
332
333         def _update(self, value): self._gauge.SetValue(value)
334
335 ########################################################################
336 # Check Box Form
337 ########################################################################
338 class check_box(_form_base):
339         """
340         Create a check box form.
341         @param parent the parent widget
342         @param sizer add this widget to sizer if provided (optional)
343         @param proportion the proportion when added to the sizer (default=0)
344         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
345         @param ps the pubsub object (optional)
346         @param key the pubsub key (optional)
347         @param value the default value (optional)
348         @param true the value for form when checked (default=True)
349         @param false the value for form when unchecked (default=False)
350         @param label title label for this widget (optional)
351         """
352         def __init__(self, label='', true=True, false=False, **kwargs):
353                 _form_base.__init__(self, converter=converters.bool_converter(true=true, false=false), **kwargs)
354                 self._check_box = wx.CheckBox(self._parent, style=wx.CHK_2STATE, label=label)
355                 self._check_box.Bind(wx.EVT_CHECKBOX, self._handle)
356                 self._add_widget(self._check_box)
357
358         def _handle(self, event): self[INT_KEY] = self._check_box.IsChecked()
359         def _update(self, checked): self._check_box.SetValue(checked)
360
361 ########################################################################
362 # Drop Down Chooser Form
363 ########################################################################
364 class drop_down(_chooser_base):
365         """
366         Create a drop down menu form.
367         @param parent the parent widget
368         @param sizer add this widget to sizer if provided (optional)
369         @param proportion the proportion when added to the sizer (default=0)
370         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
371         @param ps the pubsub object (optional)
372         @param key the pubsub key (optional)
373         @param value the default value (optional)
374         @param choices list of possible values
375         @param labels list of labels for each choice (default=choices)
376         @param label title label for this widget (optional)
377         @param width the form width in px (optional)
378         """
379         def __init__(self, label='', width=-1, **kwargs):
380                 _chooser_base.__init__(self, **kwargs)
381                 self._drop_down = wx.Choice(self._parent, choices=self._labels, size=wx.Size(width, -1))
382                 self._drop_down.Bind(wx.EVT_CHOICE, self._handle)
383                 self._add_widget(self._drop_down, label, widget_prop=0, label_prop=1)
384
385         def _handle(self, event): self[INT_KEY] = self._drop_down.GetSelection()
386         def _update(self, i): self._drop_down.SetSelection(i)
387
388 ########################################################################
389 # Button Chooser Form
390 #  Circularly move through the choices with each click.
391 #  Can be a single-click button with one choice.
392 #  Can be a 2-state button with two choices.
393 ########################################################################
394 class button(_chooser_base):
395         """
396         Create a multi-state button.
397         @param parent the parent widget
398         @param sizer add this widget to sizer if provided (optional)
399         @param proportion the proportion when added to the sizer (default=0)
400         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
401         @param ps the pubsub object (optional)
402         @param key the pubsub key (optional)
403         @param value the default value (optional)
404         @param choices list of possible values
405         @param labels list of labels for each choice (default=choices)
406         @param width the width of the button in pixels (optional)
407         @param style style arguments (optional)
408         @param label title label for this widget (optional)
409         """
410         def __init__(self, label='', style=0, width=-1, **kwargs):
411                 _chooser_base.__init__(self, **kwargs)
412                 self._button = wx.Button(self._parent, size=wx.Size(width, -1), style=style)
413                 self._button.Bind(wx.EVT_BUTTON, self._handle)
414                 self._add_widget(self._button, label, widget_prop=((not style&wx.BU_EXACTFIT) and 1 or 0))
415
416         def _handle(self, event): self[INT_KEY] = (self[INT_KEY] + 1)%len(self._choices) #circularly increment index
417         def _update(self, i): self._button.SetLabel(self._labels[i]); self.Layout()
418
419 class toggle_button(button):
420         """
421         Create a dual-state button.
422         This button will alternate between True and False when clicked.
423         @param parent the parent widget
424         @param sizer add this widget to sizer if provided (optional)
425         @param proportion the proportion when added to the sizer (default=0)
426         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
427         @param ps the pubsub object (optional)
428         @param key the pubsub key (optional)
429         @param value the default value (optional)
430         @param width the width of the button in pixels (optional)
431         @param style style arguments (optional)
432         @param true_label the button's label in the true state
433         @param false_label the button's label in the false state
434         """
435         def __init__(self, true_label='On (click to stop)', false_label='Off (click to start)', **kwargs):
436                 button.__init__(self, choices=[True, False], labels=[true_label, false_label], **kwargs)
437
438 class single_button(toggle_button):
439         """
440         Create a single state button.
441         This button will callback() when clicked.
442         For use when state holding is not important.
443         @param parent the parent widget
444         @param sizer add this widget to sizer if provided (optional)
445         @param proportion the proportion when added to the sizer (default=0)
446         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
447         @param ps the pubsub object (optional)
448         @param key the pubsub key (optional)
449         @param value the default value (optional)
450         @param width the width of the button in pixels (optional)
451         @param style style arguments (optional)
452         @param label the button's label
453         """
454         def __init__(self, label='click for callback', **kwargs):
455                 toggle_button.__init__(self, true_label=label, false_label=label, value=True, **kwargs)
456
457 ########################################################################
458 # Radio Buttons Chooser Form
459 ########################################################################
460 class radio_buttons(_chooser_base):
461         """
462         Create a radio button form.
463         @param parent the parent widget
464         @param sizer add this widget to sizer if provided (optional)
465         @param proportion the proportion when added to the sizer (default=0)
466         @param flag the flag argument when added to the sizer (default=wx.EXPAND)
467         @param ps the pubsub object (optional)
468         @param key the pubsub key (optional)
469         @param value the default value (optional)
470         @param choices list of possible values
471         @param labels list of labels for each choice (default=choices)
472         @param major_dimension the number of rows/cols (default=auto)
473         @param label title label for this widget (optional)
474         @param style useful style args: wx.RA_HORIZONTAL, wx.RA_VERTICAL, wx.NO_BORDER (default=wx.RA_HORIZONTAL)
475         """
476         def __init__(self, style=wx.RA_HORIZONTAL, label='', major_dimension=0, **kwargs):
477                 _chooser_base.__init__(self, **kwargs)
478                 #create radio buttons
479                 self._radio_buttons = wx.RadioBox(self._parent, choices=self._labels, style=style, label=label, majorDimension=major_dimension)
480                 self._radio_buttons.Bind(wx.EVT_RADIOBOX, self._handle)
481                 self._add_widget(self._radio_buttons)
482
483         def _handle(self, event): self[INT_KEY] = self._radio_buttons.GetSelection()
484         def _update(self, i): self._radio_buttons.SetSelection(i)
485
486 ########################################################################
487 # Notebook Chooser Form
488 #  The notebook pages/tabs are for selecting between choices.
489 #  A page must be added to the notebook for each choice.
490 ########################################################################
491 class notebook(_chooser_base):
492         def __init__(self, pages, notebook, **kwargs):
493                 _chooser_base.__init__(self, **kwargs)
494                 assert len(pages) == len(self._choices)
495                 self._notebook = notebook
496                 self._notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._handle)
497                 #add pages, setting the label on each tab
498                 for i, page in enumerate(pages):
499                         self._notebook.AddPage(page, self._labels[i])
500                 self._add_widget(self._notebook)
501
502         def _handle(self, event): self[INT_KEY] = self._notebook.GetSelection()
503         def _update(self, i): self._notebook.SetSelection(i)
504
505 # ----------------------------------------------------------------
506 # Stand-alone test application
507 # ----------------------------------------------------------------
508
509 import wx
510 from gnuradio.wxgui import gui
511
512 class app_gui (object):
513     def __init__(self, frame, panel, vbox, top_block, options, args):
514         
515         def callback(v): print v
516        
517         radio_buttons(
518             sizer=vbox,
519             parent=panel,
520             choices=[2, 4, 8, 16],
521             labels=['two', 'four', 'eight', 'sixteen'],
522             value=4,
523             style=wx.RA_HORIZONTAL,
524             label='test radio long string',
525             callback=callback,
526             #major_dimension = 2,
527         )
528         
529         radio_buttons(
530             sizer=vbox,
531             parent=panel,
532             choices=[2, 4, 8, 16],
533             labels=['two', 'four', 'eight', 'sixteen'],
534             value=4,
535             style=wx.RA_VERTICAL,
536             label='test radio long string',
537             callback=callback,
538             #major_dimension = 2,
539         )
540         
541         radio_buttons(
542             sizer=vbox,
543             parent=panel,
544             choices=[2, 4, 8, 16],
545             labels=['two', 'four', 'eight', 'sixteen'],
546             value=4,
547             style=wx.RA_VERTICAL | wx.NO_BORDER,
548             callback=callback,
549             #major_dimension = 2,
550         )
551         
552         button(
553             sizer=vbox,
554             parent=panel,
555             choices=[2, 4, 8, 16],
556             labels=['two', 'four', 'eight', 'sixteen'],
557             value=2,
558             label='button value',
559             callback=callback,
560             #width=100,
561         )
562         
563         
564         drop_down(
565             sizer=vbox,
566             parent=panel,
567             choices=[2, 4, 8, 16],
568             value=2,
569             label='Choose One',
570             callback=callback,
571         )
572         check_box(
573             sizer=vbox,
574             parent=panel,
575             value=False,
576             label='check me',
577             callback=callback,
578         )
579         text_box(
580             sizer=vbox,
581             parent=panel,
582             value=3,
583             label='text box',
584             callback=callback,
585             width=200,
586         )
587         
588         static_text(
589             sizer=vbox,
590             parent=panel,
591             value='bob',
592             label='static text',
593             width=-1,
594             bold=True,
595         )
596         
597         slider(
598             sizer=vbox,
599             parent=panel,
600             value=12,
601             label='slider',
602             callback=callback,
603         )
604         
605         log_slider(
606             sizer=vbox,
607             parent=panel,
608             value=12,
609             label='slider',
610             callback=callback,
611         )
612         
613         slider(
614             sizer=vbox,
615             parent=panel,
616             value=12,
617             label='slider',
618             callback=callback,
619             style=wx.SL_VERTICAL,
620             length=30,
621         )
622                
623         toggle_button(
624             sizer=vbox,
625             parent=panel,
626             value=True,
627             label='toggle it',
628             callback=callback,
629         )
630         
631         single_button(
632             sizer=vbox,
633             parent=panel,
634             label='sig test',
635             callback=callback,
636         )
637
638 if __name__ == "__main__":
639     try:
640
641         # Create the GUI application
642         app = gui.app(
643                       gui=app_gui,                     # User interface class
644                       title="Test Forms",  # Top window title
645                       )
646
647         # And run it
648         app.MainLoop()
649
650     except RuntimeError, e:
651         print e
652         sys.exit(1)