#!/usr/bin/env python # # Copyright 2005 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. # import wx from gnuradio import eng_notation # ---------------------------------------------------------------- # Wrappers for certain widgets # ---------------------------------------------------------------- def button_with_callback(parent, label, callback): new_id = wx.NewId() btn = wx.Button(parent, new_id, label) wx.EVT_BUTTON(parent, new_id, lambda evt: callback()) return btn # ---------------------------------------------------------------- # Format converters # ---------------------------------------------------------------- class abstract_converter(object): def value_to_prim(self, v): """ Convert from user specified value to value acceptable to underlying primitive. The underlying primitive usually expects strings. """ raise NotImplementedError def prim_to_value(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 value_to_prim(self,v): return v def prim_to_value(self, s): return s class int_converter(abstract_converter): def value_to_prim(self, v): return str(v) def prim_to_value(self, s): return int(s, 0) def help(self): return "Enter an integer. Leading 0x indicates hex" class float_converter(abstract_converter): def value_to_prim(self, v): return eng_notation.num_to_str(v) def prim_to_value(self, s): return eng_notation.str_to_num(s) def help(self): return "Enter a float with optional scale suffix. E.g., 100.1M" # ---------------------------------------------------------------- # Various types of data entry fields # ---------------------------------------------------------------- class field(object): """ A field in a form. """ def __init__(self, converter, value): self.converter = converter if value is not None: self.set_value(value) def set_value(self, v): self._set_prim_value(self.converter.value_to_prim(v)) def get_value(self): return self.converter.prim_to_value(self._get_prim_value()) def get_value_with_check(self): """ Returns (value, error_msg), where error_msg is not None if there was problem """ try: return (self.get_value(), None) except: return (None, self._error_msg()) def _set_prim_value(self, v): raise NotImplementedError def _get_prim_value(self): raise NotImplementedError def _pair_with_label(self, widget, parent=None, sizer=None, label=None, weight=1): self.label = label if label is None: sizer.Add (widget, weight, wx.EXPAND) return widget elif 0: hbox = wx.BoxSizer(wx.HORIZONTAL) label_widget = wx.StaticText(parent, -1, label + ': ') hbox.Add(label_widget, 0, wx.EXPAND) hbox.Add(widget, 1, wx.EXPAND) sizer.Add(hbox, weight, wx.EXPAND) return widget else: label_widget = wx.StaticText(parent, -1, label + ': ') sizer.Add(label_widget, 0, wx.EXPAND) sizer.Add(widget, weight, wx.EXPAND) return widget def _error_msg(self): prefix = '' if self.label: prefix = self.label + ': ' return "%s%s is invalid. %s" % (prefix, self._get_prim_value(), self.converter.help()) # static (display-only) text fields class static_text_field(field): def __init__(self, parent=None, sizer=None, label=None, value=None, converter=identity_converter(), weight=0): self.f = self._pair_with_label(wx.StaticText(parent, -1, ""), parent=parent, sizer=sizer, label=label, weight=weight) field.__init__(self, converter, value) def _get_prim_value(self): return self.f.GetLabel() def _set_prim_value(self, v): self.f.SetLabel(v) class static_int_field(static_text_field): def __init__(self, parent=None, sizer=None, label=None, value=None, weight=0): static_text_field.__init__(self, parent, sizer, label, value, int_converter(), weight) class static_float_field(static_text_field): def __init__(self, parent=None, sizer=None, label=None, value=None, weight=0): static_text_field.__init__(self, parent, sizer, label, value, float_converter(), weight) # editable text fields class text_field(field): def __init__(self, parent=None, sizer=None, label=None, value=None, converter=identity_converter(), callback=None, weight=1): style = 0 if callback: style = wx.TE_PROCESS_ENTER new_id = wx.NewId() w = wx.TextCtrl(parent, new_id, "", style=style) self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=label, weight=weight) if callback: wx.EVT_TEXT_ENTER(w, new_id, lambda evt: callback()) field.__init__(self, converter, value) def _get_prim_value(self): return self.f.GetValue() def _set_prim_value(self, v): self.f.SetValue(v) class int_field(text_field): def __init__(self, parent=None, sizer=None, label=None, value=None, callback=None, weight=1): text_field.__init__(self, parent, sizer, label, value, int_converter(), callback, weight) class float_field(text_field): def __init__(self, parent=None, sizer=None, label=None, value=None, callback=None, weight=1): text_field.__init__(self, parent, sizer, label, value, float_converter(), callback, weight) # other fields class slider_field(field): def __init__(self, parent=None, sizer=None, label=None, value=None, converter=identity_converter(), callback=None, min=0, max=100, weight=1): new_id = wx.NewId() w = wx.Slider(parent, new_id, (max+min)/2, min, max, size=wx.Size(250, -1), style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=label, weight=weight) if callback: wx.EVT_COMMAND_SCROLL(w, new_id, lambda evt: callback(evt.GetInt())) field.__init__(self, converter, value) def _get_prim_value(self): return self.f.GetValue() def _set_prim_value(self, v): self.f.SetValue(int(v)) class quantized_slider_field(field): def __init__(self, parent=None, sizer=None, label=None, value=None, converter=identity_converter(), callback=None, range=None, weight=1): if not isinstance(range, (tuple, list)) or len(range) != 3: raise ValueError, range self.min = range[0] self.max = range[1] self.step_size = float(range[2]) nsteps = int((self.max-self.min)/self.step_size) new_id = wx.NewId() w = wx.Slider(parent, new_id, 0, 0, nsteps, size=wx.Size(250, -1), style=wx.SL_HORIZONTAL) self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=label, weight=weight) if callback: wx.EVT_COMMAND_SCROLL(w, new_id, lambda evt: callback(self._map_out(evt.GetInt()))) field.__init__(self, converter, value) def _get_prim_value(self): return self._map_out(self.f.GetValue()) def _set_prim_value(self, v): self.f.SetValue(self._map_in(v)) def _map_in(self, x): return int((x-self.min) / self.step_size) def _map_out(self, x): return x * self.step_size + self.min class checkbox_field(field): def __init__(self, parent=None, sizer=None, label=None, value=None, converter=identity_converter(), callback=None, weight=1): new_id = wx.NewId() w = wx.CheckBox(parent, new_id, label, style=wx.CHK_2STATE) self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=None, weight=weight) if callback: wx.EVT_CHECKBOX(w, new_id, lambda evt: callback(evt.GetInt())) field.__init__(self, converter, value) def _get_prim_value(self): return self.f.GetValue() def _set_prim_value(self, v): self.f.SetValue(int(v)) class radiobox_field(field): def __init__(self, parent=None, sizer=None, label=None, value=None, converter=identity_converter(), callback=None, weight=1, choices=None, major_dimension=1, specify_rows=False): new_id = wx.NewId() if specify_rows: style=wx.RA_SPECIFY_ROWS | wx.RA_HORIZONTAL else: style=wx.RA_SPECIFY_COLS | wx.RA_HORIZONTAL w = wx.RadioBox(parent, new_id, label=label, style=style, majorDimension=major_dimension, choices=choices) self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=None, weight=weight) if callback: wx.EVT_RADIOBOX(w, new_id, lambda evt: callback(evt.GetString())) field.__init__(self, converter, value) def _get_prim_value(self): return self.f.GetStringSelection() def _set_prim_value(self, v): self.f.SetStringSelection(str(v)) # ---------------------------------------------------------------- # the form class # ---------------------------------------------------------------- class form(dict): def __init__(self): dict.__init__(self) def check_input_for_errors(self): """ Returns list of error messages if there's trouble, else empty list. """ vals = [f.get_value_with_check() for f in self.values()] return [t[1] for t in vals if t[1] is not None] def get_key_vals(self): d = {} for (key, f) in self.items(): d[key] = f.get_value() return d def _nop(*args): pass def check_input_and_call(self, callback, status_handler=_nop): """ Return a function that checks the form for errors, and then if it's OK, invokes the user specified callback, passing it the form key/value dictionary. status_handler is called with a string indicating results. """ def doit_callback(*ignore): errors = self.check_input_for_errors() if errors: status_handler(errors[0]) #print '\n'.join(tuple(errors)) else: kv = self.get_key_vals() if callback(kv): status_handler("OK") else: status_handler("Failed") return doit_callback # ---------------------------------------------------------------- # Stand-alone example code # ---------------------------------------------------------------- import sys from gnuradio.wxgui import stdgui2 class demo_app_flow_graph (stdgui2.std_top_block): def __init__(self, frame, panel, vbox, argv): stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) self.frame = frame self.panel = panel def _print_kv(kv): print "kv =", kv return True self.form = form() self.form['static1'] = \ static_text_field(parent=panel, sizer=vbox, label="Static Text", value="The Static Value") self.form['text1'] = \ text_field(parent=panel, sizer=vbox, label="TextCtrl", value="The Editable Value") self.form['int1'] = \ int_field(parent=panel, sizer=vbox, label="Int Field", value=1234) self.form['float1'] = \ float_field(parent=panel, sizer=vbox, label="Float Field", value=3.14159) self.doit = button_with_callback( panel, "Do It!", self.form.check_input_and_call(_print_kv, self._set_status_msg)) vbox.Add(self.doit, 0, wx.CENTER) def _set_status_msg(self, msg): self.frame.GetStatusBar().SetStatusText(msg, 0) def main (): app = stdgui2.stdapp(demo_app_flow_graph, "wxgui form demo", nstatus=1) app.MainLoop () if __name__ == '__main__': main ()