commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] r10738 - gnuradio/branches/features/experimental-gui


From: jblum
Subject: [Commit-gnuradio] r10738 - gnuradio/branches/features/experimental-gui
Date: Wed, 1 Apr 2009 19:32:03 -0600 (MDT)

Author: jblum
Date: 2009-04-01 19:32:02 -0600 (Wed, 01 Apr 2009)
New Revision: 10738

Modified:
   gnuradio/branches/features/experimental-gui/forms.py
Log:
Added converter classes and layered model for forms.
Added text box, static text, slider.
Slider needs float, int, log options.



Modified: gnuradio/branches/features/experimental-gui/forms.py
===================================================================
--- gnuradio/branches/features/experimental-gui/forms.py        2009-04-02 
00:26:27 UTC (rev 10737)
+++ gnuradio/branches/features/experimental-gui/forms.py        2009-04-02 
01:32:02 UTC (rev 10738)
@@ -19,88 +19,273 @@
 # Boston, MA 02110-1301, USA.
 #
 
-PRI_FORM_KEY = 'pri_form'
+"""
+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
+"""
+
+EXT_KEY = 'external'
+INT_KEY = 'internal'
+
 import wx
+import sys
 from gnuradio.gr.pubsub import pubsub
+from gnuradio import eng_notation
 
-#NOTE not used yet in forms
-class LabelText(wx.StaticText):
+########################################################################
+# Commonly used converters
+########################################################################
+class abstract_converter(object):
+       def external_to_internal(self, v):
+               """
+               Convert from user specified value to value acceptable to 
underlying primitive.
+               The underlying primitive usually expects strings.
+               """
+               raise NotImplementedError
+       def internal_to_external(self, s):
+               """
+               Convert from underlying primitive value to user specified value.
+               The underlying primitive usually expects strings.
+               """
+               raise NotImplementedError
+       def help(self):
+               return "Any string is acceptable"
+
+class identity_converter(abstract_converter):
+       def external_to_internal(self,v):
+               return v
+       def internal_to_external(self, s):
+               return s
+
+class chooser_converter(abstract_converter):
        """
-       Label text to give the wx plots a uniform look.
-       Get the default label text and set the font bold.
+       Convert between a set of possible choices and an index.
+       Used in the chooser base and all sub-classes.
        """
-       def __init__(self, parent, label):
-               wx.StaticText.__init__(self, parent, label=label)
-               font = self.GetFont()
-               font.SetWeight(wx.FONTWEIGHT_BOLD)
-               self.SetFont(font)
+       def __init__(self, choices):
+               self._choices = choices
+       def external_to_internal(self, choice):
+               return self._choices.index(choice)
+       def internal_to_external(self, index):
+               return self._choices[index]
+       def help(self):
+               return 'Enter a possible value in choices: 
"%s"'%str(self._choices)
 
-#NOTE used in button and drop_down
-class LabelBox(wx.BoxSizer):
-       def __init__(self, parent, label, widget, style=wx.HORIZONTAL):
-               wx.BoxSizer.__init__(self, style)
-               if style == wx.HORIZONTAL:
-                       self.Add(wx.StaticText(parent, label=' %s '%label), 1, 
wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
-                       self.Add(widget, 0, wx.ALIGN_CENTER_VERTICAL | 
wx.ALIGN_RIGHT)
-               if style == wx.VERTICAL:
-                       self.Add(wx.StaticText(parent, label=' %s '%label), 1, 
wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_TOP)
-                       self.Add(widget, 0, wx.ALIGN_CENTER_HORIZONTAL | 
wx.ALIGN_BOTTOM)
+class bool_converter(abstract_converter):
+       """
+       The internal representation is boolean.
+       The external representation is specified.
+       Used in the check box form.
+       """
+       def __init__(self, true, false):
+               self._true = true
+               self._false = false
+       def external_to_internal(self, v):
+               return bool(v)
+       def internal_to_external(self, v):
+               if v: return self._true
+               else: return self._false
+       def help(self):
+               return "Value must be cast-able to type bool."
 
+class eval_converter(abstract_converter):
+       """
+       A catchall converter when int and float are not enough.
+       Evaluate the internal representation with python's eval().
+       Possible uses, set a complex number, constellation points.
+       Used in text box.
+       """
+       def external_to_internal(self, s):
+               return str(s)
+       def internal_to_external(self, s):
+               return eval(s)
+       def help(self):
+               return "Value must be evaluatable by python's eval."
+
+class str_converter(abstract_converter):
+       def external_to_internal(self, v):
+               return str(v)
+       def internal_to_external(self, s):
+               return str(s)
+
+class int_converter(abstract_converter):
+       def external_to_internal(self, v):
+               return str(v)
+       def internal_to_external(self, s):
+               return int(s, 0)
+       def help(self):
+               return "Enter an integer.  Leading 0x indicates hex"
+
+class float_converter(abstract_converter):
+       def external_to_internal(self, v):
+               return eng_notation.num_to_str(v)
+       def internal_to_external(self, s):
+               return eng_notation.str_to_num(s)
+       def help(self):
+               return "Enter a float with optional scale suffix.  E.g., 100.1M"
+
+class slider_converter(abstract_converter):
+       """
+       Scale values to and from the slider.
+       """
+       def __init__(self, minimum, maximum, num_steps):
+               assert minimum < maximum
+               assert num_steps > 0
+               self._offset = minimum
+               self._scaler = float(maximum - minimum)/num_steps
+
+       def external_to_internal(self, v):
+               return int(round((v - self._offset)/self._scaler))
+
+       def internal_to_external(self, v):
+               return v*self._scaler + self._offset
+
 ########################################################################
 # Base Class Form
 ########################################################################
 class _form_base(pubsub, wx.BoxSizer):
-       def __init__(self, parent=None, sizer=None, weight=0, ps=None, key='', 
value=None, callback=None):
+       def __init__(self, parent=None, sizer=None, proportion=0, 
style=wx.HORIZONTAL, ps=None, key='', value=None, callback=None, 
converter=identity_converter()):
                pubsub.__init__(self)
-               wx.BoxSizer.__init__(self, wx.VERTICAL)
+               wx.BoxSizer.__init__(self, style)
                self._parent = parent
+               self._converter = converter
                #add to the sizer if provided
-               if sizer: sizer.Add(self, weight, wx.EXPAND)
+               if sizer: sizer.Add(self, proportion, wx.EXPAND)
                #proxy the pubsub and key into this form
                if ps is not None:
                        assert key
-                       self.proxy(PRI_FORM_KEY, ps, key)
+                       self.proxy(EXT_KEY, ps, key)
                #no pubsub passed, must set initial value
                else: self.set_value(value)
                #subscribe callbacks for changes
-               self.subscribe(PRI_FORM_KEY, self._update)
-               if callback: self.subscribe(PRI_FORM_KEY, callback)
+               self.subscribe(INT_KEY, self._update)
+               self.subscribe(INT_KEY, self._translate_internal_to_external)
+               self.subscribe(EXT_KEY, self._translate_external_to_internal)
+               if callback: self.subscribe(EXT_KEY, callback)
 
-       #override in subclasses to update the gui form
-       def _update(self, value): pass
+       def _add_widget(self, widget, label=None, proportion=0, 
label_proportion=1, style=wx.HORIZONTAL):
+               if label is None: wx.BoxSizer.Add(self, widget, proportion, 
wx.EXPAND)
+               elif style == wx.HORIZONTAL:
+                       self.Add(wx.StaticText(self._parent, label=' %s: 
'%label), label_proportion, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+                       self.Add(widget, proportion, wx.ALIGN_CENTER_VERTICAL | 
wx.ALIGN_RIGHT)
+               elif style == wx.VERTICAL:
+                       self.Add(wx.StaticText(self._parent, label=' %s: 
'%label), label_proportion, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_TOP)
+                       self.Add(widget, proportion, wx.ALIGN_CENTER_HORIZONTAL 
| wx.ALIGN_BOTTOM)
+               else: raise NotImplementedError
 
+       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, 'Error translating value: 
"%s"\n\t%s\n\t%s'%(value, e, self._converter.help())
+
+       #call after gui is setup to initialize
+       def _init(self): self[EXT_KEY] = self[EXT_KEY]
+
+       #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[PRI_FORM_KEY]
-       def set_value(self, value): self[PRI_FORM_KEY] = value
+       def get_value(self): return self[EXT_KEY]
+       def set_value(self, value): self[EXT_KEY] = value
 
 #TODO slider base class, log slider, linear slider, float & int types
-#TODO text box base class, eng notation, float, int, other
-#TODO static text display, float, int, other
 
 ########################################################################
+# Static Text Form
+########################################################################
+class static_text(_form_base):
+       def __init__(self, label='', width=-1, **kwargs):
+               _form_base.__init__(self, converter=str_converter(), **kwargs)
+               self._static_text = wx.StaticText(self._parent, 
size=wx.Size(width, -1))
+               self._add_widget(self._static_text, label)
+               self._init()
+
+       def _update(self, label): self._static_text.SetLabel(label)
+
+########################################################################
+# Text Box Form
+########################################################################
+class text_box(_form_base):
+       def __init__(self, label='', width=-1, **kwargs):
+               _form_base.__init__(self, converter=eval_converter(), **kwargs)
+               self._text_box = wx.TextCtrl(self._parent, size=wx.Size(width, 
-1), style=wx.TE_PROCESS_ENTER)
+               self._add_widget(self._text_box, label)
+               self._text_box.Bind(wx.EVT_TEXT_ENTER, self._handle)
+               self._init()
+
+       def _handle(self, event): self[INT_KEY] = self._text_box.GetValue()
+       def _update(self, value): self._text_box.SetValue(value)
+
+########################################################################
+# Slider Form
+########################################################################
+class slider(_form_base):
+       def __init__(self, label='', length=-1, minimum=-100, maximum=100, 
num_steps=100, style=wx.HORIZONTAL, **kwargs):
+               converter = slider_converter(minimum=minimum, maximum=maximum, 
num_steps=num_steps)
+               _form_base.__init__(self, converter=converter, style=style, 
**kwargs)
+               if style == wx.HORIZONTAL:
+                       slider_size = wx.Size(length, -1)
+                       slider_style = wx.SL_HORIZONTAL
+               elif style == wx.VERTICAL:
+                       slider_size = wx.Size(-1, length)
+                       slider_style = wx.SL_VERTICAL
+               else: raise NotImplementedError
+               self._slider = wx.Slider(self._parent, minValue=0, 
maxValue=num_steps, size=slider_size, style=slider_style)
+               self._add_widget(self._slider, label, proportion=1, style=style)
+               self._slider.Bind(wx.EVT_SCROLL, self._handle)
+               self._init()
+
+       def _handle(self, event): self[INT_KEY] = self._slider.GetValue()
+       def _update(self, value): self._slider.SetValue(value)
+
+########################################################################
 # Check Box Form
 ########################################################################
 class check_box(_form_base):
-       def __init__(self, label='', **kwargs):
-               _form_base.__init__(self, **kwargs)
+       def __init__(self, label='', true=True, false=False, **kwargs):
+               _form_base.__init__(self, converter=bool_converter(true=true, 
false=false), **kwargs)
                self._check_box = wx.CheckBox(self._parent, 
style=wx.CHK_2STATE, label=label)
-               self.Add(self._check_box, 0, wx.ALIGN_CENTER)
+               self._add_widget(self._check_box)
                self._check_box.Bind(wx.EVT_CHECKBOX, self._handle)
-               self.set_value(self.get_value())
+               self._init()
 
-       def _handle(self, event):
-               self[PRI_FORM_KEY] = bool(self._check_box.IsChecked())
+       def _handle(self, event): self[INT_KEY] = self._check_box.IsChecked()
+       def _update(self, checked): self._check_box.SetValue(checked)
 
-       def _update(self, value):
-               self._check_box.SetValue(bool(value))
-
 ########################################################################
 # Base Class Chooser Form
 ########################################################################
 class _chooser_base(_form_base):
        def __init__(self, choices=[], labels=None, **kwargs):
-               _form_base.__init__(self, **kwargs)
+               _form_base.__init__(self, converter=chooser_converter(choices), 
**kwargs)
                self._choices = choices
                self._labels = map(str, labels or choices)
 
@@ -111,17 +296,13 @@
        def __init__(self, label='', **kwargs):
                _chooser_base.__init__(self, **kwargs)
                self._drop_down = wx.Choice(self._parent, choices=self._labels)
-               self.Add(LabelBox(self._parent, label, self._drop_down), 0, 
wx.ALIGN_CENTER)
+               self._add_widget(self._drop_down, label)
                self._drop_down.Bind(wx.EVT_CHOICE, self._handle)
-               self.set_value(self.get_value())
+               self._init()
 
-       def _handle(self, event):
-               self[PRI_FORM_KEY] = 
self._choices[self._drop_down.GetSelection()]
+       def _handle(self, event): self[INT_KEY] = self._drop_down.GetSelection()
+       def _update(self, i): self._drop_down.SetSelection(i)
 
-       def _update(self, value):
-               try: self._drop_down.SetSelection(self._choices.index(value))
-               except ValueError: pass
-
 ########################################################################
 # Button Chooser Form
 #  Circularly move through the choices with each click.
@@ -132,20 +313,13 @@
        def __init__(self, label='', **kwargs):
                _chooser_base.__init__(self, **kwargs)
                self._button = wx.Button(self._parent)
-               self.Add(LabelBox(self._parent, label, self._button), 0, 
wx.ALIGN_CENTER)
+               self._add_widget(self._button, label)
                self._button.Bind(wx.EVT_BUTTON, self._handle)
-               self.set_value(self.get_value())
+               self._init()
 
-       def _handle(self, event):
-               self._index = (self._index + 1)%len(self._choices) #circularly 
increment index
-               self[PRI_FORM_KEY] = self._choices[self._index]
+       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])
 
-       def _update(self, value):
-               try:
-                       self._index = self._choices.index(value)
-                       self._button.SetLabel(self._labels[self._index])
-               except ValueError: pass
-
 ########################################################################
 # Radio Buttons Chooser Form
 ########################################################################
@@ -157,17 +331,13 @@
                if style == wx.VERTICAL: style = wx.RA_VERTICAL
                #create radio buttons
                self._radio_buttons = wx.RadioBox(self._parent, 
choices=self._labels, style=style, label=label)
-               self.Add(self._radio_buttons, 0, wx.ALIGN_CENTER)
+               self._add_widget(self._radio_buttons)
                self._radio_buttons.Bind(wx.EVT_RADIOBOX, self._handle)
-               self.set_value(self.get_value())
+               self._init()
 
-       def _handle(self, event):
-               self[PRI_FORM_KEY] = 
self._choices[self._radio_buttons.GetSelection()]
+       def _handle(self, event): self[INT_KEY] = 
self._radio_buttons.GetSelection()
+       def _update(self, i): self._radio_buttons.SetSelection(i)
 
-       def _update(self, value):
-               try: 
self._radio_buttons.SetSelection(self._choices.index(value))
-               except ValueError: pass
-
 ########################################################################
 # Notebook Chooser Form
 #  The notebook pages/tabs are for selecting between choices.
@@ -177,19 +347,15 @@
        def __init__(self, **kwargs):
                _chooser_base.__init__(self, **kwargs)
                self._notebook = wx.Notebook(self._parent)
-               self.Add(self._notebook, 0, wx.ALIGN_CENTER)
+               self._add_widget(self._notebook)
                self._notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self._handle)
 
        #get at the real notebook object for adding pages and such
        def get_notebook(self): return self._notebook
 
-       def _handle(self, event):
-               self[PRI_FORM_KEY] = 
self._choices[self._notebook.GetSelection()]
+       def _handle(self, event): self[INT_KEY] = self._notebook.GetSelection()
+       def _update(self, i): self._notebook.SetSelection(i)
 
-       def _update(self, value):
-               try: self._notebook.SetSelection(self._choices.index(value))
-               except ValueError: pass
-
 # ----------------------------------------------------------------
 # Stand-alone test application
 # ----------------------------------------------------------------
@@ -210,6 +376,7 @@
             value=4,
             style=wx.VERTICAL,
             label='test radio long string',
+            callback=callback,
         )
         
         
@@ -220,7 +387,8 @@
             choices=[2, 4, 8, 16],
             labels=['two', 'four', 'eight', 'sixteen'],
             value=16,
-            label='button value'
+            label='button value',
+            callback=callback,
         )
         
         
@@ -229,7 +397,8 @@
             parent=panel,
             choices=[2, 4, 8, 16],
             value=2,
-            label='Choose One'
+            label='Choose One',
+            callback=callback,
         )
         check_box(
             sizer=vbox,
@@ -238,6 +407,40 @@
             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,
+        )
+        
+        slider(
+            sizer=vbox,
+            parent=panel,
+            value=12,
+            label='slider',
+            callback=callback,
+        )
+        
+        slider(
+            sizer=vbox,
+            parent=panel,
+            value=12,
+            label='slider2',
+            callback=callback,
+            style=wx.VERTICAL,
+            length=100,
+        )
 
 if __name__ == "__main__":
     try:





reply via email to

[Prev in Thread] Current Thread [Next in Thread]