3 # Copyright 2005 Free Software Foundation, Inc.
5 # This file is part of GNU Radio
7 # GNU Radio is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3, or (at your option)
12 # GNU Radio is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with GNU Radio; see the file COPYING. If not, write to
19 # the Free Software Foundation, Inc., 51 Franklin Street,
20 # Boston, MA 02110-1301, USA.
24 from gnuradio import eng_notation
26 # ----------------------------------------------------------------
27 # Wrappers for certain widgets
28 # ----------------------------------------------------------------
30 def button_with_callback(parent, label, callback):
32 btn = wx.Button(parent, new_id, label)
33 wx.EVT_BUTTON(parent, new_id, lambda evt: callback())
37 # ----------------------------------------------------------------
39 # ----------------------------------------------------------------
41 class abstract_converter(object):
42 def value_to_prim(self, v):
44 Convert from user specified value to value acceptable to underlying primitive.
45 The underlying primitive usually expects strings.
47 raise NotImplementedError
48 def prim_to_value(self, s):
50 Convert from underlying primitive value to user specified value.
51 The underlying primitive usually expects strings.
53 raise NotImplementedError
55 return "Any string is acceptable"
57 class identity_converter(abstract_converter):
58 def value_to_prim(self,v):
60 def prim_to_value(self, s):
63 class int_converter(abstract_converter):
64 def value_to_prim(self, v):
66 def prim_to_value(self, s):
69 return "Enter an integer. Leading 0x indicates hex"
71 class float_converter(abstract_converter):
72 def value_to_prim(self, v):
73 return eng_notation.num_to_str(v)
74 def prim_to_value(self, s):
75 return eng_notation.str_to_num(s)
77 return "Enter a float with optional scale suffix. E.g., 100.1M"
80 # ----------------------------------------------------------------
81 # Various types of data entry fields
82 # ----------------------------------------------------------------
88 def __init__(self, converter, value):
89 self.converter = converter
93 def set_value(self, v):
94 self._set_prim_value(self.converter.value_to_prim(v))
97 return self.converter.prim_to_value(self._get_prim_value())
99 def get_value_with_check(self):
101 Returns (value, error_msg), where error_msg is not None if there was problem
104 return (self.get_value(), None)
106 return (None, self._error_msg())
108 def _set_prim_value(self, v):
109 raise NotImplementedError
111 def _get_prim_value(self):
112 raise NotImplementedError
114 def _pair_with_label(self, widget, parent=None, sizer=None, label=None, weight=1):
117 sizer.Add (widget, weight, wx.EXPAND)
120 hbox = wx.BoxSizer(wx.HORIZONTAL)
121 label_widget = wx.StaticText(parent, -1, label + ': ')
122 hbox.Add(label_widget, 0, wx.EXPAND)
123 hbox.Add(widget, 1, wx.EXPAND)
124 sizer.Add(hbox, weight, wx.EXPAND)
127 label_widget = wx.StaticText(parent, -1, label + ': ')
128 sizer.Add(label_widget, 0, wx.EXPAND)
129 sizer.Add(widget, weight, wx.EXPAND)
132 def _error_msg(self):
135 prefix = self.label + ': '
136 return "%s%s is invalid. %s" % (prefix, self._get_prim_value(),
137 self.converter.help())
139 # static (display-only) text fields
141 class static_text_field(field):
142 def __init__(self, parent=None, sizer=None, label=None, value=None,
143 converter=identity_converter(), weight=0):
144 self.f = self._pair_with_label(wx.StaticText(parent, -1, ""),
145 parent=parent, sizer=sizer, label=label, weight=weight)
146 field.__init__(self, converter, value)
148 def _get_prim_value(self):
149 return self.f.GetLabel()
151 def _set_prim_value(self, v):
155 class static_int_field(static_text_field):
156 def __init__(self, parent=None, sizer=None, label=None, value=None, weight=0):
157 static_text_field.__init__(self, parent, sizer, label, value, int_converter(), weight)
159 class static_float_field(static_text_field):
160 def __init__(self, parent=None, sizer=None, label=None, value=None, weight=0):
161 static_text_field.__init__(self, parent, sizer, label, value, float_converter(), weight)
164 # editable text fields
166 class text_field(field):
167 def __init__(self, parent=None, sizer=None, label=None, value=None,
168 converter=identity_converter(), callback=None, weight=1):
171 style = wx.TE_PROCESS_ENTER
174 w = wx.TextCtrl(parent, new_id, "", style=style)
175 self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=label, weight=weight)
177 wx.EVT_TEXT_ENTER(w, new_id, lambda evt: callback())
178 field.__init__(self, converter, value)
180 def _get_prim_value(self):
181 return self.f.GetValue()
183 def _set_prim_value(self, v):
187 class int_field(text_field):
188 def __init__(self, parent=None, sizer=None, label=None, value=None,
189 callback=None, weight=1):
190 text_field.__init__(self, parent, sizer, label, value, int_converter(), callback, weight)
192 class float_field(text_field):
193 def __init__(self, parent=None, sizer=None, label=None, value=None,
194 callback=None, weight=1):
195 text_field.__init__(self, parent, sizer, label, value, float_converter(), callback, weight)
199 class slider_field(field):
200 def __init__(self, parent=None, sizer=None, label=None, value=None,
201 converter=identity_converter(), callback=None, min=0, max=100, weight=1):
203 w = wx.Slider(parent, new_id, (max+min)/2, min, max,
204 size=wx.Size(250, -1), style=wx.SL_HORIZONTAL | wx.SL_LABELS)
205 self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=label, weight=weight)
207 wx.EVT_COMMAND_SCROLL(w, new_id, lambda evt: callback(evt.GetInt()))
208 field.__init__(self, converter, value)
210 def _get_prim_value(self):
211 return self.f.GetValue()
213 def _set_prim_value(self, v):
214 self.f.SetValue(int(v))
216 class quantized_slider_field(field):
217 def __init__(self, parent=None, sizer=None, label=None, value=None,
218 converter=identity_converter(), callback=None, range=None, weight=1):
219 if not isinstance(range, (tuple, list)) or len(range) != 3:
220 raise ValueError, range
224 self.step_size = float(range[2])
225 nsteps = int((self.max-self.min)/self.step_size)
228 w = wx.Slider(parent, new_id, 0, 0, nsteps,
229 size=wx.Size(250, -1), style=wx.SL_HORIZONTAL)
230 self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=label, weight=weight)
232 wx.EVT_COMMAND_SCROLL(w, new_id,
233 lambda evt: callback(self._map_out(evt.GetInt())))
234 field.__init__(self, converter, value)
236 def _get_prim_value(self):
237 return self._map_out(self.f.GetValue())
239 def _set_prim_value(self, v):
240 self.f.SetValue(self._map_in(v))
242 def _map_in(self, x):
243 return int((x-self.min) / self.step_size)
245 def _map_out(self, x):
246 return x * self.step_size + self.min
248 class checkbox_field(field):
249 def __init__(self, parent=None, sizer=None, label=None, value=None,
250 converter=identity_converter(), callback=None, weight=1):
252 w = wx.CheckBox(parent, new_id, label, style=wx.CHK_2STATE)
253 self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=None, weight=weight)
255 wx.EVT_CHECKBOX(w, new_id, lambda evt: callback(evt.GetInt()))
256 field.__init__(self, converter, value)
258 def _get_prim_value(self):
259 return self.f.GetValue()
261 def _set_prim_value(self, v):
262 self.f.SetValue(int(v))
265 class radiobox_field(field):
266 def __init__(self, parent=None, sizer=None, label="", value=None,
267 converter=identity_converter(), callback=None, weight=1,
268 choices=None, major_dimension=1, specify_rows=False):
272 style=wx.RA_SPECIFY_ROWS | wx.RA_HORIZONTAL
274 style=wx.RA_SPECIFY_COLS | wx.RA_HORIZONTAL
276 w = wx.RadioBox(parent, new_id, label, style=style, majorDimension=major_dimension,
278 self.f = self._pair_with_label(w, parent=parent, sizer=sizer, label=label, weight=weight)
280 wx.EVT_RADIOBOX(w, new_id, lambda evt: callback(evt.GetString()))
281 field.__init__(self, converter, value)
283 def _get_prim_value(self):
284 return self.f.GetStringSelection()
286 def _set_prim_value(self, v):
287 self.f.SetStringSelection(str(v))
289 # ----------------------------------------------------------------
291 # ----------------------------------------------------------------
297 def check_input_for_errors(self):
299 Returns list of error messages if there's trouble,
302 vals = [f.get_value_with_check() for f in self.values()]
303 return [t[1] for t in vals if t[1] is not None]
305 def get_key_vals(self):
307 for (key, f) in self.items():
308 d[key] = f.get_value()
312 def _nop(*args): pass
314 def check_input_and_call(self, callback, status_handler=_nop):
316 Return a function that checks the form for errors, and then if it's OK,
317 invokes the user specified callback, passing it the form key/value dictionary.
318 status_handler is called with a string indicating results.
320 def doit_callback(*ignore):
321 errors = self.check_input_for_errors()
323 status_handler(errors[0])
324 #print '\n'.join(tuple(errors))
326 kv = self.get_key_vals()
330 status_handler("Failed")
336 # ----------------------------------------------------------------
337 # Stand-alone example code
338 # ----------------------------------------------------------------
341 from gnuradio.wxgui import stdgui
343 class demo_app_flow_graph (stdgui.gui_flow_graph):
344 def __init__(self, frame, panel, vbox, argv):
345 stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox, argv)
356 self.form['static1'] = \
357 static_text_field(parent=panel, sizer=vbox,
359 value="The Static Value")
361 self.form['text1'] = \
362 text_field(parent=panel, sizer=vbox,
364 value="The Editable Value")
366 self.form['int1'] = \
367 int_field(parent=panel, sizer=vbox,
371 self.form['float1'] = \
372 float_field(parent=panel, sizer=vbox,
376 self.doit = button_with_callback(
378 self.form.check_input_and_call(_print_kv, self._set_status_msg))
380 vbox.Add(self.doit, 0, wx.CENTER)
382 def _set_status_msg(self, msg):
383 self.frame.GetStatusBar().SetStatusText(msg, 0)
387 app = stdgui.stdapp (demo_app_flow_graph, "wxgui form demo", nstatus=1)
390 if __name__ == '__main__':