summaryrefslogtreecommitdiff
path: root/gr-wxgui
diff options
context:
space:
mode:
Diffstat (limited to 'gr-wxgui')
-rw-r--r--gr-wxgui/src/python/Makefile.am7
-rw-r--r--gr-wxgui/src/python/common.py162
-rw-r--r--gr-wxgui/src/python/const_window.py68
-rw-r--r--gr-wxgui/src/python/constants.py1
-rw-r--r--gr-wxgui/src/python/fft_window.py116
-rw-r--r--gr-wxgui/src/python/forms/__init__.py103
-rw-r--r--gr-wxgui/src/python/forms/converters.py145
-rw-r--r--gr-wxgui/src/python/forms/forms.py641
-rw-r--r--gr-wxgui/src/python/histo_window.py43
-rw-r--r--gr-wxgui/src/python/number_window.py128
-rw-r--r--gr-wxgui/src/python/scope_window.py298
-rw-r--r--gr-wxgui/src/python/waterfall_window.py116
12 files changed, 1353 insertions, 475 deletions
diff --git a/gr-wxgui/src/python/Makefile.am b/gr-wxgui/src/python/Makefile.am
index 45d75b605..e06298a2d 100644
--- a/gr-wxgui/src/python/Makefile.am
+++ b/gr-wxgui/src/python/Makefile.am
@@ -59,3 +59,10 @@ ourpython_PYTHON = \
waterfall_window.py \
slider.py \
stdgui2.py
+
+formspythondir = $(grpythondir)/wxgui/forms
+
+formspython_PYTHON = \
+ forms/__init__.py \
+ forms/forms.py \
+ forms/converters.py
diff --git a/gr-wxgui/src/python/common.py b/gr-wxgui/src/python/common.py
index c84827eb8..c6b9509b2 100644
--- a/gr-wxgui/src/python/common.py
+++ b/gr-wxgui/src/python/common.py
@@ -71,168 +71,6 @@ class input_watcher(threading.Thread):
self._controller[self._msg_key] = msg.to_string()
##################################################
-# WX Shared Classes
-##################################################
-import math
-import wx
-
-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
-
-class LabelText(wx.StaticText):
- """
- Label text to give the wx plots a uniform look.
- Get the default label text and set the font bold.
- """
- def __init__(self, parent, label):
- wx.StaticText.__init__(self, parent, label=label)
- font = self.GetFont()
- font.SetWeight(wx.FONTWEIGHT_BOLD)
- self.SetFont(font)
-
-class LabelBox(wx.BoxSizer):
- def __init__(self, parent, label, widget):
- wx.BoxSizer.__init__(self, 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)
-
-class IncrDecrButtons(wx.BoxSizer):
- """
- A horizontal box sizer with a increment and a decrement button.
- """
- def __init__(self, parent, on_incr, on_decr):
- """
- @param parent the parent window
- @param on_incr the event handler for increment
- @param on_decr the event handler for decrement
- """
- wx.BoxSizer.__init__(self, wx.HORIZONTAL)
- self._incr_button = wx.Button(parent, label='+', style=wx.BU_EXACTFIT)
- self._incr_button.Bind(wx.EVT_BUTTON, on_incr)
- self.Add(self._incr_button, 0, wx.ALIGN_CENTER_VERTICAL)
- self._decr_button = wx.Button(parent, label=' - ', style=wx.BU_EXACTFIT)
- self._decr_button.Bind(wx.EVT_BUTTON, on_decr)
- self.Add(self._decr_button, 0, wx.ALIGN_CENTER_VERTICAL)
-
- def Disable(self): self.Enable(False)
- def Enable(self, enable=True):
- if enable:
- self._incr_button.Enable()
- self._decr_button.Enable()
- else:
- self._incr_button.Disable()
- self._decr_button.Disable()
-
-class ToggleButtonController(wx.Button):
- def __init__(self, parent, controller, control_key, true_label, false_label):
- self._controller = controller
- self._control_key = control_key
- wx.Button.__init__(self, parent, style=wx.BU_EXACTFIT)
- self.Bind(wx.EVT_BUTTON, self._evt_button)
- controller.subscribe(control_key, lambda x: self.SetLabel(x and true_label or false_label))
-
- def _evt_button(self, e):
- self._controller[self._control_key] = not self._controller[self._control_key]
-
-class CheckBoxController(wx.CheckBox):
- def __init__(self, parent, label, controller, control_key):
- self._controller = controller
- self._control_key = control_key
- wx.CheckBox.__init__(self, parent, style=wx.CHK_2STATE, label=label)
- self.Bind(wx.EVT_CHECKBOX, self._evt_checkbox)
- controller.subscribe(control_key, lambda x: self.SetValue(bool(x)))
-
- def _evt_checkbox(self, e):
- self._controller[self._control_key] = bool(e.IsChecked())
-
-from gnuradio import eng_notation
-
-class TextBoxController(wx.TextCtrl):
- def __init__(self, parent, controller, control_key, cast=float):
- self._controller = controller
- self._control_key = control_key
- self._cast = cast
- wx.TextCtrl.__init__(self, parent, style=wx.TE_PROCESS_ENTER)
- self.Bind(wx.EVT_TEXT_ENTER, self._evt_enter)
- controller.subscribe(control_key, lambda x: self.SetValue(eng_notation.num_to_str(x)))
-
- def _evt_enter(self, e):
- try: self._controller[self._control_key] = self._cast(eng_notation.str_to_num(self.GetValue()))
- except: self._controller[self._control_key] = self._controller[self._control_key]
-
-class LogSliderController(wx.BoxSizer):
- """
- Log slider controller with display label and slider.
- Gives logarithmic scaling to slider operation.
- """
- def __init__(self, parent, prefix, min_exp, max_exp, slider_steps, controller, control_key, formatter=lambda x: ': %.6f'%x):
- self._prefix = prefix
- self._min_exp = min_exp
- self._max_exp = max_exp
- self._controller = controller
- self._control_key = control_key
- self._formatter = formatter
- wx.BoxSizer.__init__(self, wx.VERTICAL)
- self._label = wx.StaticText(parent, label=prefix + formatter(1/3.0))
- self.Add(self._label, 0, wx.EXPAND)
- self._slider = wx.Slider(parent, minValue=0, maxValue=slider_steps, style=wx.SL_HORIZONTAL)
- self.Add(self._slider, 0, wx.EXPAND)
- self._slider.Bind(wx.EVT_SLIDER, self._on_slider_event)
- controller.subscribe(control_key, self._on_controller_set)
-
- def _get_slope(self):
- return float(self._max_exp-self._min_exp)/self._slider.GetMax()
-
- def _on_slider_event(self, e):
- self._controller[self._control_key] = 10**(self._get_slope()*self._slider.GetValue() + self._min_exp)
-
- def _on_controller_set(self, value):
- self._label.SetLabel(self._prefix + self._formatter(value))
- slider_value = (math.log10(value)-self._min_exp)/self._get_slope()
- slider_value = min(max(self._slider.GetMin(), slider_value), self._slider.GetMax())
- if abs(slider_value - self._slider.GetValue()) > 1:
- self._slider.SetValue(slider_value)
-
- def Disable(self): self.Enable(False)
- def Enable(self, enable=True):
- if enable:
- self._slider.Enable()
- self._label.Enable()
- else:
- self._slider.Disable()
- self._label.Disable()
-
-class DropDownController(wx.Choice):
- """
- Drop down controller with label and chooser.
- Srop down selection from a set of choices.
- """
- def __init__(self, parent, choices, controller, control_key, size=(-1, -1)):
- """
- @param parent the parent window
- @param choices a list of tuples -> (label, value)
- @param controller the prop val controller
- @param control_key the prop key for this control
- """
- self._controller = controller
- self._control_key = control_key
- self._choices = choices
- wx.Choice.__init__(self, parent, choices=[c[0] for c in choices], size=size)
- self.Bind(wx.EVT_CHOICE, self._on_chooser_event)
- controller.subscribe(control_key, self._on_controller_set)
-
- def _on_chooser_event(self, e):
- self._controller[self._control_key] = self._choices[self.GetSelection()][1]
-
- def _on_controller_set(self, value):
- #only set the chooser if the value is a possible choice
- for i, choice in enumerate(self._choices):
- if value == choice[1]: self.SetSelection(i)
-
-##################################################
# Shared Functions
##################################################
import numpy
diff --git a/gr-wxgui/src/python/const_window.py b/gr-wxgui/src/python/const_window.py
index 8e0e61ac3..b128a4a98 100644
--- a/gr-wxgui/src/python/const_window.py
+++ b/gr-wxgui/src/python/const_window.py
@@ -30,6 +30,7 @@ import math
import pubsub
from constants import *
from gnuradio import gr #for gr.prefs
+import forms
##################################################
# Constants
@@ -63,34 +64,53 @@ class control_panel(wx.Panel):
"""
self.parent = parent
wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
- control_box = wx.BoxSizer(wx.VERTICAL)
- self.marker_index = 2
- #begin control box
- control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER)
+ control_box = forms.static_box_sizer(
+ parent=self, label='Options',
+ bold=True, orient=wx.VERTICAL,
+ )
#alpha
control_box.AddStretchSpacer()
- alpha_slider = common.LogSliderController(
- self, 'Alpha',
- ALPHA_MIN_EXP, ALPHA_MAX_EXP, SLIDER_STEPS,
- parent, ALPHA_KEY,
+ forms.text_box(
+ sizer=control_box, parent=self, label='Alpha',
+ converter=forms.float_converter(),
+ ps=parent, key=ALPHA_KEY,
+ )
+ forms.log_slider(
+ sizer=control_box, parent=self,
+ min_exp=ALPHA_MIN_EXP,
+ max_exp=ALPHA_MAX_EXP,
+ num_steps=SLIDER_STEPS,
+ ps=parent, key=ALPHA_KEY,
)
- control_box.Add(alpha_slider, 0, wx.EXPAND)
#gain_mu
control_box.AddStretchSpacer()
- gain_mu_slider = common.LogSliderController(
- self, 'Gain Mu',
- GAIN_MU_MIN_EXP, GAIN_MU_MAX_EXP, SLIDER_STEPS,
- parent, GAIN_MU_KEY,
+ forms.text_box(
+ sizer=control_box, parent=self, label='Gain Mu',
+ converter=forms.float_converter(),
+ ps=parent, key=GAIN_MU_KEY,
+ )
+ forms.log_slider(
+ sizer=control_box, parent=self,
+ min_exp=GAIN_MU_MIN_EXP,
+ max_exp=GAIN_MU_MAX_EXP,
+ num_steps=SLIDER_STEPS,
+ ps=parent, key=GAIN_MU_KEY,
)
- control_box.Add(gain_mu_slider, 0, wx.EXPAND)
#marker
control_box.AddStretchSpacer()
- marker_chooser = common.DropDownController(self, MARKER_TYPES, parent, MARKER_KEY)
- control_box.Add(common.LabelBox(self, 'Marker', marker_chooser), 0, wx.EXPAND)
+ forms.drop_down(
+ sizer=control_box, parent=self,
+ ps=parent, key=MARKER_KEY, label='Marker',
+ choices=map(lambda x: x[1], MARKER_TYPES),
+ labels=map(lambda x: x[0], MARKER_TYPES),
+ )
#run/stop
control_box.AddStretchSpacer()
- self.run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run')
- control_box.Add(self.run_button, 0, wx.EXPAND)
+ forms.toggle_button(
+ sizer=control_box, parent=self,
+ true_label='Stop', false_label='Run',
+ ps=parent, key=RUNNING_KEY,
+ )
#set sizer
self.SetSizerAndFit(control_box)
@@ -121,6 +141,11 @@ class const_window(wx.Panel, pubsub.pubsub):
self.proxy(GAIN_OMEGA_KEY, controller, gain_omega_key)
self.proxy(OMEGA_KEY, controller, omega_key)
self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
+ #initialize values
+ self[RUNNING_KEY] = True
+ self[X_DIVS_KEY] = 8
+ self[Y_DIVS_KEY] = 8
+ self[MARKER_KEY] = DEFAULT_MARKER_TYPE
#init panel and plot
wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
self.plotter = plotter.channel_plotter(self)
@@ -141,13 +166,6 @@ class const_window(wx.Panel, pubsub.pubsub):
self.subscribe(ALPHA_KEY, set_beta)
def set_gain_omega(gain_mu): self[GAIN_OMEGA_KEY] = .25*gain_mu**2
self.subscribe(GAIN_MU_KEY, set_gain_omega)
- #initialize values
- self[ALPHA_KEY] = self[ALPHA_KEY]
- self[GAIN_MU_KEY] = self[GAIN_MU_KEY]
- self[RUNNING_KEY] = True
- self[X_DIVS_KEY] = 8
- self[Y_DIVS_KEY] = 8
- self[MARKER_KEY] = DEFAULT_MARKER_TYPE
#register events
self.subscribe(MSG_KEY, self.handle_msg)
self.subscribe(X_DIVS_KEY, self.update_grid)
diff --git a/gr-wxgui/src/python/constants.py b/gr-wxgui/src/python/constants.py
index a4ccdca6d..5e1395701 100644
--- a/gr-wxgui/src/python/constants.py
+++ b/gr-wxgui/src/python/constants.py
@@ -66,3 +66,4 @@ MAXIMUM_KEY = 'maximum'
MINIMUM_KEY = 'minimum'
NUM_BINS_KEY = 'num_bins'
FRAME_SIZE_KEY = 'frame_size'
+CHANNEL_OPTIONS_KEY = 'channel_options'
diff --git a/gr-wxgui/src/python/fft_window.py b/gr-wxgui/src/python/fft_window.py
index fdd5562dc..fded1a8fa 100644
--- a/gr-wxgui/src/python/fft_window.py
+++ b/gr-wxgui/src/python/fft_window.py
@@ -30,6 +30,7 @@ import math
import pubsub
from constants import *
from gnuradio import gr #for gr.prefs
+import forms
##################################################
# Constants
@@ -59,48 +60,66 @@ class control_panel(wx.Panel):
self.parent = parent
wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
control_box = wx.BoxSizer(wx.VERTICAL)
- #checkboxes for average and peak hold
control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER)
- peak_hold_check_box = common.CheckBoxController(self, 'Peak Hold', parent, PEAK_HOLD_KEY)
- control_box.Add(peak_hold_check_box, 0, wx.EXPAND)
- average_check_box = common.CheckBoxController(self, 'Average', parent, AVERAGE_KEY)
- control_box.Add(average_check_box, 0, wx.EXPAND)
- control_box.AddSpacer(2)
- avg_alpha_slider = common.LogSliderController(
- self, 'Avg Alpha',
- AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
- parent, AVG_ALPHA_KEY,
- formatter=lambda x: ': %.4f'%x,
+ #checkboxes for average and peak hold
+ options_box = forms.static_box_sizer(
+ parent=self, sizer=control_box, label='Options',
+ bold=True, orient=wx.VERTICAL,
+ )
+ forms.check_box(
+ sizer=options_box, parent=self, label='Peak Hold',
+ ps=parent, key=PEAK_HOLD_KEY,
+ )
+ forms.check_box(
+ sizer=options_box, parent=self, label='Average',
+ ps=parent, key=AVERAGE_KEY,
)
- parent.subscribe(AVERAGE_KEY, avg_alpha_slider.Enable)
- control_box.Add(avg_alpha_slider, 0, wx.EXPAND)
+ #static text and slider for averaging
+ avg_alpha_text = forms.static_text(
+ sizer=options_box, parent=self, label='Avg Alpha',
+ converter=forms.float_converter(lambda x: '%.4f'%x),
+ ps=parent, key=AVG_ALPHA_KEY, width=50,
+ )
+ avg_alpha_slider = forms.log_slider(
+ sizer=options_box, parent=self,
+ min_exp=AVG_ALPHA_MIN_EXP,
+ max_exp=AVG_ALPHA_MAX_EXP,
+ num_steps=SLIDER_STEPS,
+ ps=parent, key=AVG_ALPHA_KEY,
+ )
+ for widget in (avg_alpha_text, avg_alpha_slider):
+ parent.subscribe(AVERAGE_KEY, widget.Enable)
+ widget.Enable(parent[AVERAGE_KEY])
#radio buttons for div size
control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Set dB/div'), 0, wx.ALIGN_CENTER)
- radio_box = wx.BoxSizer(wx.VERTICAL)
- self.radio_buttons = list()
- for y_per_div in DIV_LEVELS:
- radio_button = wx.RadioButton(self, label="%d dB/div"%y_per_div)
- radio_button.Bind(wx.EVT_RADIOBUTTON, self._on_y_per_div)
- self.radio_buttons.append(radio_button)
- radio_box.Add(radio_button, 0, wx.ALIGN_LEFT)
- parent.subscribe(Y_PER_DIV_KEY, self._on_set_y_per_div)
- control_box.Add(radio_box, 0, wx.EXPAND)
+ y_ctrl_box = forms.static_box_sizer(
+ parent=self, sizer=control_box, label='Axis Options',
+ bold=True, orient=wx.VERTICAL,
+ )
+ forms.radio_buttons(
+ sizer=y_ctrl_box, parent=self,
+ ps=parent, key=Y_PER_DIV_KEY,
+ style=wx.RA_VERTICAL|wx.NO_BORDER, choices=DIV_LEVELS,
+ labels=map(lambda x: '%s dB/div'%x, DIV_LEVELS),
+ )
#ref lvl buttons
- control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Set Ref Level'), 0, wx.ALIGN_CENTER)
- control_box.AddSpacer(2)
- _ref_lvl_buttons = common.IncrDecrButtons(self, self._on_incr_ref_level, self._on_decr_ref_level)
- control_box.Add(_ref_lvl_buttons, 0, wx.ALIGN_CENTER)
+ forms.incr_decr_buttons(
+ parent=self, sizer=y_ctrl_box, label='Ref Level',
+ on_incr=self._on_incr_ref_level, on_decr=self._on_decr_ref_level,
+ )
+ y_ctrl_box.AddSpacer(2)
#autoscale
- control_box.AddStretchSpacer()
- autoscale_button = wx.Button(self, label='Autoscale', style=wx.BU_EXACTFIT)
- autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale)
- control_box.Add(autoscale_button, 0, wx.EXPAND)
+ forms.single_button(
+ sizer=y_ctrl_box, parent=self, label='Autoscale',
+ callback=self.parent.autoscale,
+ )
#run/stop
- run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run')
- control_box.Add(run_button, 0, wx.EXPAND)
+ control_box.AddStretchSpacer()
+ forms.toggle_button(
+ sizer=control_box, parent=self,
+ true_label='Stop', false_label='Run',
+ ps=parent, key=RUNNING_KEY,
+ )
#set sizer
self.SetSizerAndFit(control_box)
#mouse wheel event
@@ -112,15 +131,6 @@ class control_panel(wx.Panel):
##################################################
# Event handlers
##################################################
- def _on_set_y_per_div(self, y_per_div):
- try:
- index = list(DIV_LEVELS).index(y_per_div)
- self.radio_buttons[index].SetValue(True)
- except: pass
- def _on_y_per_div(self, event):
- selected_radio_button = filter(lambda rb: rb.GetValue(), self.radio_buttons)[0]
- index = self.radio_buttons.index(selected_radio_button)
- self.parent[Y_PER_DIV_KEY] = DIV_LEVELS[index]
def _on_incr_ref_level(self, event):
self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] + self.parent[Y_PER_DIV_KEY]
def _on_decr_ref_level(self, event):
@@ -161,6 +171,14 @@ class fft_window(wx.Panel, pubsub.pubsub):
self.proxy(AVERAGE_KEY, controller, average_key)
self.proxy(AVG_ALPHA_KEY, controller, avg_alpha_key)
self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
+ #initialize values
+ self[PEAK_HOLD_KEY] = peak_hold
+ self[Y_PER_DIV_KEY] = y_per_div
+ self[Y_DIVS_KEY] = y_divs
+ self[X_DIVS_KEY] = 8 #approximate
+ self[REF_LEVEL_KEY] = ref_level
+ self[BASEBAND_FREQ_KEY] = baseband_freq
+ self[RUNNING_KEY] = True
#init panel and plot
wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
self.plotter = plotter.channel_plotter(self)
@@ -175,16 +193,6 @@ class fft_window(wx.Panel, pubsub.pubsub):
main_box.Add(self.plotter, 1, wx.EXPAND)
main_box.Add(self.control_panel, 0, wx.EXPAND)
self.SetSizerAndFit(main_box)
- #initialize values
- self[AVERAGE_KEY] = self[AVERAGE_KEY]
- self[AVG_ALPHA_KEY] = self[AVG_ALPHA_KEY]
- self[PEAK_HOLD_KEY] = peak_hold
- self[Y_PER_DIV_KEY] = y_per_div
- self[Y_DIVS_KEY] = y_divs
- self[X_DIVS_KEY] = 8 #approximate
- self[REF_LEVEL_KEY] = ref_level
- self[BASEBAND_FREQ_KEY] = baseband_freq
- self[RUNNING_KEY] = True
#register events
self.subscribe(AVERAGE_KEY, lambda x: self._reset_peak_vals())
self.subscribe(MSG_KEY, self.handle_msg)
diff --git a/gr-wxgui/src/python/forms/__init__.py b/gr-wxgui/src/python/forms/__init__.py
new file mode 100644
index 000000000..3f9f4c735
--- /dev/null
+++ b/gr-wxgui/src/python/forms/__init__.py
@@ -0,0 +1,103 @@
+#
+# 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 following classes will be available through gnuradio.wxgui.forms:
+"""
+
+########################################################################
+# External Converters
+########################################################################
+from converters import \
+ eval_converter, str_converter, \
+ float_converter, int_converter
+
+########################################################################
+# External Forms
+########################################################################
+from forms import \
+ radio_buttons, drop_down, notebook, \
+ button, toggle_button, single_button, \
+ check_box, text_box, static_text, \
+ slider, log_slider, gauge, \
+ make_bold, DataEvent, EVT_DATA
+
+########################################################################
+# Helpful widgets
+########################################################################
+import wx
+
+class static_box_sizer(wx.StaticBoxSizer):
+ """
+ A box sizer with label and border.
+ @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 label title label for this widget (optional)
+ @param bold true to boldify the label
+ @param orient the sizer orientation wx.VERTICAL or wx.HORIZONTAL (default=wx.VERTICAL)
+ """
+ def __init__(self, parent, label='', bold=False, sizer=None, orient=wx.VERTICAL, proportion=0, flag=wx.EXPAND):
+ box = wx.StaticBox(parent=parent, label=label)
+ if bold: make_bold(box)
+ wx.StaticBoxSizer.__init__(self, box=box, orient=orient)
+ if sizer: sizer.Add(self, proportion, flag)
+
+class incr_decr_buttons(wx.BoxSizer):
+ """
+ A horizontal box sizer with a increment and a decrement 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 label title label for this widget (optional)
+ @param on_incr the callback for pressing the + button
+ @param on_decr the callback for pressing the - button
+ """
+ def __init__(self, parent, on_incr, on_decr, label='', sizer=None, proportion=0, flag=wx.EXPAND):
+ """
+ @param parent the parent window
+ @param on_incr the event handler for increment
+ @param on_decr the event handler for decrement
+ """
+ wx.BoxSizer.__init__(self, wx.HORIZONTAL)
+ buttons_box = wx.BoxSizer(wx.HORIZONTAL)
+ self._incr_button = wx.Button(parent, label='+', style=wx.BU_EXACTFIT)
+ self._incr_button.Bind(wx.EVT_BUTTON, on_incr)
+ buttons_box.Add(self._incr_button, 0, wx.ALIGN_CENTER_VERTICAL)
+ self._decr_button = wx.Button(parent, label=' - ', style=wx.BU_EXACTFIT)
+ self._decr_button.Bind(wx.EVT_BUTTON, on_decr)
+ buttons_box.Add(self._decr_button, 0, wx.ALIGN_CENTER_VERTICAL)
+ if label: #add label
+ self.Add(wx.StaticText(parent, label='%s: '%label), 1, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ self.Add(buttons_box, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+ else: self.Add(buttons_box, 0, wx.ALIGN_CENTER_VERTICAL)
+ if sizer: sizer.Add(self, proportion, flag)
+
+ def Disable(self, disable=True): self.Enable(not disable)
+ def Enable(self, enable=True):
+ if enable:
+ self._incr_button.Enable()
+ self._decr_button.Enable()
+ else:
+ self._incr_button.Disable()
+ self._decr_button.Disable()
diff --git a/gr-wxgui/src/python/forms/converters.py b/gr-wxgui/src/python/forms/converters.py
new file mode 100644
index 000000000..123aefeb0
--- /dev/null
+++ b/gr-wxgui/src/python/forms/converters.py
@@ -0,0 +1,145 @@
+#
+# 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.
+#
+
+from gnuradio import eng_notation
+import math
+
+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
+
+########################################################################
+# Commonly used converters
+########################################################################
+class chooser_converter(abstract_converter):
+ """
+ Convert between a set of possible choices and an index.
+ Used in the chooser base and all sub-classes.
+ """
+ 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)
+
+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(int(round(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 __init__(self, formatter=eng_notation.num_to_str):
+ self._formatter = formatter
+ def external_to_internal(self, v):
+ return self._formatter(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, cast):
+ assert minimum < maximum
+ assert num_steps > 0
+ self._offset = minimum
+ self._scaler = float(maximum - minimum)/num_steps
+ self._cast = cast
+ def external_to_internal(self, v):
+ return (v - self._offset)/self._scaler
+ def internal_to_external(self, v):
+ return self._cast(v*self._scaler + self._offset)
+ def help(self):
+ return "Value should be within slider range"
+
+class log_slider_converter(slider_converter):
+ def __init__(self, min_exp, max_exp, num_steps, base):
+ assert min_exp < max_exp
+ assert num_steps > 0
+ self._base = base
+ slider_converter.__init__(self, minimum=min_exp, maximum=max_exp, num_steps=num_steps, cast=float)
+ def external_to_internal(self, v):
+ return slider_converter.external_to_internal(self, math.log(v, self._base))
+ def internal_to_external(self, v):
+ return self._base**slider_converter.internal_to_external(self, v)
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)
diff --git a/gr-wxgui/src/python/histo_window.py b/gr-wxgui/src/python/histo_window.py
index dce52ff9b..5f434d70e 100644
--- a/gr-wxgui/src/python/histo_window.py
+++ b/gr-wxgui/src/python/histo_window.py
@@ -30,6 +30,7 @@ import math
import pubsub
from constants import *
from gnuradio import gr #for gr.prefs
+import forms
##################################################
# Constants
@@ -53,23 +54,31 @@ class control_panel(wx.Panel):
wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
control_box = wx.BoxSizer(wx.VERTICAL)
SIZE = (100, -1)
- control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER)
- control_box.AddStretchSpacer()
+ control_box = forms.static_box_sizer(
+ parent=self, label='Options',
+ bold=True, orient=wx.VERTICAL,
+ )
#num bins
- def num_bins_cast(num):
- num = int(num)
- assert num > 1
- return num
- num_bins_ctrl = common.TextBoxController(self, parent, NUM_BINS_KEY, cast=num_bins_cast)
- control_box.Add(common.LabelBox(self, ' Num Bins ', num_bins_ctrl), 0, wx.EXPAND)
control_box.AddStretchSpacer()
+ forms.text_box(
+ sizer=control_box, parent=self, label='Num Bins',
+ converter=forms.int_converter(),
+ ps=parent, key=NUM_BINS_KEY,
+ )
#frame size
- frame_size_ctrl = common.TextBoxController(self, parent, FRAME_SIZE_KEY, cast=num_bins_cast)
- control_box.Add(common.LabelBox(self, ' Frame Size ', frame_size_ctrl), 0, wx.EXPAND)
control_box.AddStretchSpacer()
+ forms.text_box(
+ sizer=control_box, parent=self, label='Frame Size',
+ converter=forms.int_converter(),
+ ps=parent, key=FRAME_SIZE_KEY,
+ )
#run/stop
- self.run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run')
- control_box.Add(self.run_button, 0, wx.EXPAND)
+ control_box.AddStretchSpacer()
+ forms.toggle_button(
+ sizer=control_box, parent=self,
+ true_label='Stop', false_label='Run',
+ ps=parent, key=RUNNING_KEY,
+ )
#set sizer
self.SetSizerAndFit(control_box)
@@ -98,6 +107,10 @@ class histo_window(wx.Panel, pubsub.pubsub):
self.proxy(NUM_BINS_KEY, controller, num_bins_key)
self.proxy(FRAME_SIZE_KEY, controller, frame_size_key)
self.proxy(MSG_KEY, controller, msg_key)
+ #initialize values
+ self[RUNNING_KEY] = True
+ self[X_DIVS_KEY] = 8
+ self[Y_DIVS_KEY] = 4
#init panel and plot
wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
self.plotter = plotter.bar_plotter(self)
@@ -111,12 +124,6 @@ class histo_window(wx.Panel, pubsub.pubsub):
main_box.Add(self.plotter, 1, wx.EXPAND)
main_box.Add(self.control_panel, 0, wx.EXPAND)
self.SetSizerAndFit(main_box)
- #initialize values
- self[NUM_BINS_KEY] = self[NUM_BINS_KEY]
- self[FRAME_SIZE_KEY] = self[FRAME_SIZE_KEY]
- self[RUNNING_KEY] = True
- self[X_DIVS_KEY] = 8
- self[Y_DIVS_KEY] = 4
#register events
self.subscribe(MSG_KEY, self.handle_msg)
self.subscribe(X_DIVS_KEY, self.update_grid)
diff --git a/gr-wxgui/src/python/number_window.py b/gr-wxgui/src/python/number_window.py
index f12a18248..8a8249764 100644
--- a/gr-wxgui/src/python/number_window.py
+++ b/gr-wxgui/src/python/number_window.py
@@ -28,6 +28,7 @@ import wx
import pubsub
from constants import *
from gnuradio import gr #for gr.prefs
+import forms
##################################################
# Constants
@@ -38,6 +39,9 @@ AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
DEFAULT_NUMBER_RATE = gr.prefs().get_long('wxgui', 'number_rate', 5)
DEFAULT_WIN_SIZE = (300, 300)
DEFAULT_GAUGE_RANGE = 1000
+VALUE_REPR_KEY = 'value_repr'
+VALUE_REAL_KEY = 'value_real'
+VALUE_IMAG_KEY = 'value_imag'
##################################################
# Number window control panel
@@ -53,28 +57,45 @@ class control_panel(wx.Panel):
@param parent the wx parent window
"""
self.parent = parent
- wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
+ wx.Panel.__init__(self, parent)
control_box = wx.BoxSizer(wx.VERTICAL)
#checkboxes for average and peak hold
control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER)
- self.peak_hold_check_box = common.CheckBoxController(self, 'Peak Hold', parent, PEAK_HOLD_KEY)
- control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
- self.average_check_box = common.CheckBoxController(self, 'Average', parent, AVERAGE_KEY)
- control_box.Add(self.average_check_box, 0, wx.EXPAND)
- control_box.AddSpacer(2)
- self.avg_alpha_slider = common.LogSliderController(
- self, 'Avg Alpha',
- AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
- parent, AVG_ALPHA_KEY,
- formatter=lambda x: ': %.4f'%x,
+ options_box = forms.static_box_sizer(
+ parent=self, sizer=control_box, label='Options',
+ bold=True, orient=wx.VERTICAL,
+ )
+ forms.check_box(
+ sizer=options_box, parent=self, label='Peak Hold',
+ ps=parent, key=PEAK_HOLD_KEY,
+ )
+ forms.check_box(
+ sizer=options_box, parent=self, label='Average',
+ ps=parent, key=AVERAGE_KEY,
+ )
+ #static text and slider for averaging
+ avg_alpha_text = forms.static_text(
+ sizer=options_box, parent=self, label='Avg Alpha',
+ converter=forms.float_converter(lambda x: '%.4f'%x),
+ ps=parent, key=AVG_ALPHA_KEY, width=50,
+ )
+ avg_alpha_slider = forms.log_slider(
+ sizer=options_box, parent=self,
+ min_exp=AVG_ALPHA_MIN_EXP,
+ max_exp=AVG_ALPHA_MAX_EXP,
+ num_steps=SLIDER_STEPS,
+ ps=parent, key=AVG_ALPHA_KEY,
)
- parent.subscribe(AVERAGE_KEY, self.avg_alpha_slider.Enable)
- control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
+ for widget in (avg_alpha_text, avg_alpha_slider):
+ parent.subscribe(AVERAGE_KEY, widget.Enable)
+ widget.Enable(parent[AVERAGE_KEY])
#run/stop
control_box.AddStretchSpacer()
- self.run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run')
- control_box.Add(self.run_button, 0, wx.EXPAND)
+ forms.toggle_button(
+ sizer=control_box, parent=self,
+ true_label='Stop', false_label='Run',
+ ps=parent, key=RUNNING_KEY,
+ )
#set sizer
self.SetSizerAndFit(control_box)
@@ -107,38 +128,47 @@ class number_window(wx.Panel, pubsub.pubsub):
self.peak_val_imag = NEG_INF
self.real = real
self.units = units
- self.minval = minval
- self.maxval = maxval
self.decimal_places = decimal_places
#proxy the keys
self.proxy(MSG_KEY, controller, msg_key)
self.proxy(AVERAGE_KEY, controller, average_key)
self.proxy(AVG_ALPHA_KEY, controller, avg_alpha_key)
self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
+ #initialize values
+ self[PEAK_HOLD_KEY] = peak_hold
+ self[RUNNING_KEY] = True
+ self[VALUE_REAL_KEY] = minval
+ self[VALUE_IMAG_KEY] = minval
#setup the box with display and controls
self.control_panel = control_panel(self)
main_box = wx.BoxSizer(wx.HORIZONTAL)
- sizer = wx.BoxSizer(wx.VERTICAL)
- main_box.Add(sizer, 1, wx.EXPAND)
+ sizer = forms.static_box_sizer(
+ parent=self, sizer=main_box, label=title,
+ bold=True, orient=wx.VERTICAL, proportion=1,
+ )
main_box.Add(self.control_panel, 0, wx.EXPAND)
- sizer.Add(common.LabelText(self, title), 1, wx.ALIGN_CENTER)
- self.text = wx.StaticText(self, size=(size[0], -1))
- sizer.Add(self.text, 1, wx.EXPAND)
- self.gauge_real = wx.Gauge(self, range=DEFAULT_GAUGE_RANGE, style=wx.GA_HORIZONTAL)
- self.gauge_imag = wx.Gauge(self, range=DEFAULT_GAUGE_RANGE, style=wx.GA_HORIZONTAL)
+ sizer.AddStretchSpacer()
+ forms.static_text(
+ parent=self, sizer=sizer,
+ ps=self, key=VALUE_REPR_KEY, width=size[0],
+ converter=forms.str_converter(),
+ )
+ sizer.AddStretchSpacer()
+ self.gauge_real = forms.gauge(
+ parent=self, sizer=sizer, style=wx.GA_HORIZONTAL,
+ ps=self, key=VALUE_REAL_KEY, length=size[0],
+ minimum=minval, maximum=maxval, num_steps=DEFAULT_GAUGE_RANGE,
+ )
+ self.gauge_imag = forms.gauge(
+ parent=self, sizer=sizer, style=wx.GA_HORIZONTAL,
+ ps=self, key=VALUE_IMAG_KEY, length=size[0],
+ minimum=minval, maximum=maxval, num_steps=DEFAULT_GAUGE_RANGE,
+ )
#hide/show gauges
self.show_gauges(show_gauge)
- sizer.Add(self.gauge_real, 1, wx.EXPAND)
- sizer.Add(self.gauge_imag, 1, wx.EXPAND)
self.SetSizerAndFit(main_box)
- #initialize values
- self[PEAK_HOLD_KEY] = peak_hold
- self[RUNNING_KEY] = True
- self[AVERAGE_KEY] = self[AVERAGE_KEY]
- self[AVG_ALPHA_KEY] = self[AVG_ALPHA_KEY]
#register events
self.subscribe(MSG_KEY, self.handle_msg)
- self.Bind(common.EVT_DATA, self.update)
def show_gauges(self, show_gauge):
"""
@@ -146,21 +176,11 @@ class number_window(wx.Panel, pubsub.pubsub):
If this is real, never show the imaginary gauge.
@param show_gauge true to show
"""
- if show_gauge: self.gauge_real.Show()
- else: self.gauge_real.Hide()
- if show_gauge and not self.real: self.gauge_imag.Show()
- else: self.gauge_imag.Hide()
+ self.gauge_real.ShowItems(show_gauge)
+ self.gauge_imag.ShowItems(show_gauge and not self.real)
def handle_msg(self, msg):
"""
- Post this message into a data event.
- Allow wx to handle the event to avoid threading issues.
- @param msg the incoming numbersink data
- """
- wx.PostEvent(self, common.DataEvent(msg))
-
- def update(self, event):
- """
Handle a message from the message queue.
Convert the string based message into a float or complex.
If more than one number was read, only take the last number.
@@ -168,29 +188,23 @@ class number_window(wx.Panel, pubsub.pubsub):
@param event event.data is the number sample as a character array
"""
if not self[RUNNING_KEY]: return
- #set gauge
- def set_gauge_value(gauge, value):
- gauge_val = DEFAULT_GAUGE_RANGE*(value-self.minval)/(self.maxval-self.minval)
- gauge_val = max(0, gauge_val) #clip
- gauge_val = min(DEFAULT_GAUGE_RANGE, gauge_val) #clip
- gauge.SetValue(gauge_val)
format_string = "%%.%df"%self.decimal_places
if self.real:
- sample = numpy.fromstring(event.data, numpy.float32)[-1]
+ sample = numpy.fromstring(msg, numpy.float32)[-1]
if self[PEAK_HOLD_KEY]: sample = self.peak_val_real = max(self.peak_val_real, sample)
label_text = "%s %s"%(format_string%sample, self.units)
- set_gauge_value(self.gauge_real, sample)
+ self[VALUE_REAL_KEY] = sample
else:
- sample = numpy.fromstring(event.data, numpy.complex64)[-1]
+ sample = numpy.fromstring(msg, numpy.complex64)[-1]
if self[PEAK_HOLD_KEY]:
self.peak_val_real = max(self.peak_val_real, sample.real)
self.peak_val_imag = max(self.peak_val_imag, sample.imag)
sample = self.peak_val_real + self.peak_val_imag*1j
label_text = "%s + %sj %s"%(format_string%sample.real, format_string%sample.imag, self.units)
- set_gauge_value(self.gauge_real, sample.real)
- set_gauge_value(self.gauge_imag, sample.imag)
+ self[VALUE_REAL_KEY] = sample.real
+ self[VALUE_IMAG_KEY] = sample.imag
#set label text
- self.text.SetLabel(label_text)
+ self[VALUE_REPR_KEY] = label_text
#clear peak hold
if not self[PEAK_HOLD_KEY]:
self.peak_val_real = NEG_INF
diff --git a/gr-wxgui/src/python/scope_window.py b/gr-wxgui/src/python/scope_window.py
index bbc66426a..449046402 100644
--- a/gr-wxgui/src/python/scope_window.py
+++ b/gr-wxgui/src/python/scope_window.py
@@ -30,6 +30,7 @@ import time
import pubsub
from constants import *
from gnuradio import gr #for gr.prefs, trigger modes
+import forms
##################################################
# Constants
@@ -42,12 +43,12 @@ COUPLING_MODES = (
)
TRIGGER_MODES = (
('Freerun', gr.gr_TRIG_MODE_FREE),
- ('Automatic', gr.gr_TRIG_MODE_AUTO),
+ ('Auto', gr.gr_TRIG_MODE_AUTO),
('Normal', gr.gr_TRIG_MODE_NORM),
)
TRIGGER_SLOPES = (
- ('Positive +', gr.gr_TRIG_SLOPE_POS),
- ('Negative -', gr.gr_TRIG_SLOPE_NEG),
+ ('Pos +', gr.gr_TRIG_SLOPE_POS),
+ ('Neg -', gr.gr_TRIG_SLOPE_NEG),
)
CHANNEL_COLOR_SPECS = (
(0.3, 0.3, 1.0),
@@ -78,7 +79,7 @@ class control_panel(wx.Panel):
Create a new control panel.
@param parent the wx parent window
"""
- SIZE = (100, -1)
+ WIDTH = 90
self.parent = parent
wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
control_box = wx.BoxSizer(wx.VERTICAL)
@@ -86,161 +87,238 @@ class control_panel(wx.Panel):
# Axes Options
##################################################
control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Axes Options'), 0, wx.ALIGN_CENTER)
- control_box.AddSpacer(2)
+ axes_options_box = forms.static_box_sizer(
+ parent=self, sizer=control_box, label='Axes Options',
+ bold=True, orient=wx.VERTICAL,
+ )
##################################################
# Scope Mode Box
##################################################
scope_mode_box = wx.BoxSizer(wx.VERTICAL)
- control_box.Add(scope_mode_box, 0, wx.EXPAND)
+ axes_options_box.Add(scope_mode_box, 0, wx.EXPAND)
#x axis divs
- x_buttons_scope = common.IncrDecrButtons(self, self._on_incr_t_divs, self._on_decr_t_divs)
- scope_mode_box.Add(common.LabelBox(self, 'Secs/Div', x_buttons_scope), 0, wx.EXPAND)
+ forms.incr_decr_buttons(
+ parent=self, sizer=scope_mode_box, label='Secs/Div',
+ on_incr=self._on_incr_t_divs, on_decr=self._on_decr_t_divs,
+ )
#y axis divs
- y_buttons_scope = common.IncrDecrButtons(self, self._on_incr_y_divs, self._on_decr_y_divs)
- parent.subscribe(AUTORANGE_KEY, lambda x: y_buttons_scope.Enable(not x))
- scope_mode_box.Add(common.LabelBox(self, 'Counts/Div', y_buttons_scope), 0, wx.EXPAND)
+ y_buttons_scope = forms.incr_decr_buttons(
+ parent=self, sizer=scope_mode_box, label='Counts/Div',
+ on_incr=self._on_incr_y_divs, on_decr=self._on_decr_y_divs,
+ )
#y axis ref lvl
- y_off_buttons_scope = common.IncrDecrButtons(self, self._on_incr_y_off, self._on_decr_y_off)
- parent.subscribe(AUTORANGE_KEY, lambda x: y_off_buttons_scope.Enable(not x))
- scope_mode_box.Add(common.LabelBox(self, 'Y Offset', y_off_buttons_scope), 0, wx.EXPAND)
+ y_off_buttons_scope = forms.incr_decr_buttons(
+ parent=self, sizer=scope_mode_box, label='Y Offset',
+ on_incr=self._on_incr_y_off, on_decr=self._on_decr_y_off,
+ )
#t axis ref lvl
scope_mode_box.AddSpacer(5)
- t_off_slider = wx.Slider(self, size=SIZE, style=wx.SL_HORIZONTAL)
- t_off_slider.SetRange(0, 1000)
- def t_off_slider_changed(evt): parent[T_FRAC_OFF_KEY] = float(t_off_slider.GetValue())/t_off_slider.GetMax()
- t_off_slider.Bind(wx.EVT_SLIDER, t_off_slider_changed)
- parent.subscribe(T_FRAC_OFF_KEY, lambda x: t_off_slider.SetValue(int(round(x*t_off_slider.GetMax()))))
- scope_mode_box.Add(common.LabelBox(self, 'T Offset', t_off_slider), 0, wx.EXPAND)
+ forms.slider(
+ parent=self, sizer=scope_mode_box,
+ ps=parent, key=T_FRAC_OFF_KEY, label='T Offset',
+ minimum=0, maximum=1, num_steps=1000,
+ )
scope_mode_box.AddSpacer(5)
##################################################
# XY Mode Box
##################################################
xy_mode_box = wx.BoxSizer(wx.VERTICAL)
- control_box.Add(xy_mode_box, 0, wx.EXPAND)
+ axes_options_box.Add(xy_mode_box, 0, wx.EXPAND)
#x div controls
- x_buttons = common.IncrDecrButtons(self, self._on_incr_x_divs, self._on_decr_x_divs)
- parent.subscribe(AUTORANGE_KEY, lambda x: x_buttons.Enable(not x))
- xy_mode_box.Add(common.LabelBox(self, 'X/Div', x_buttons), 0, wx.EXPAND)
+ x_buttons = forms.incr_decr_buttons(
+ parent=self, sizer=xy_mode_box, label='X/Div',
+ on_incr=self._on_incr_x_divs, on_decr=self._on_decr_x_divs,
+ )
#y div controls
- y_buttons = common.IncrDecrButtons(self, self._on_incr_y_divs, self._on_decr_y_divs)
- parent.subscribe(AUTORANGE_KEY, lambda x: y_buttons.Enable(not x))
- xy_mode_box.Add(common.LabelBox(self, 'Y/Div', y_buttons), 0, wx.EXPAND)
+ y_buttons = forms.incr_decr_buttons(
+ parent=self, sizer=xy_mode_box, label='Y/Div',
+ on_incr=self._on_incr_y_divs, on_decr=self._on_decr_y_divs,
+ )
#x offset controls
- x_off_buttons = common.IncrDecrButtons(self, self._on_incr_x_off, self._on_decr_x_off)
- parent.subscribe(AUTORANGE_KEY, lambda x: x_off_buttons.Enable(not x))
- xy_mode_box.Add(common.LabelBox(self, 'X Off', x_off_buttons), 0, wx.EXPAND)
+ x_off_buttons = forms.incr_decr_buttons(
+ parent=self, sizer=xy_mode_box, label='X Off',
+ on_incr=self._on_incr_x_off, on_decr=self._on_decr_x_off,
+ )
#y offset controls
- y_off_buttons = common.IncrDecrButtons(self, self._on_incr_y_off, self._on_decr_y_off)
- parent.subscribe(AUTORANGE_KEY, lambda x: y_off_buttons.Enable(not x))
- xy_mode_box.Add(common.LabelBox(self, 'Y Off', y_off_buttons), 0, wx.EXPAND)
+ y_off_buttons = forms.incr_decr_buttons(
+ parent=self, sizer=xy_mode_box, label='Y Off',
+ on_incr=self._on_incr_y_off, on_decr=self._on_decr_y_off,
+ )
+ for widget in (y_buttons_scope, y_off_buttons_scope, x_buttons, y_buttons, x_off_buttons, y_off_buttons):
+ parent.subscribe(AUTORANGE_KEY, widget.Disable)
+ widget.Disable(parent[AUTORANGE_KEY])
xy_mode_box.ShowItems(False)
#autorange check box
- self.autorange_check_box = common.CheckBoxController(self, 'Autorange', parent, AUTORANGE_KEY)
- control_box.Add(self.autorange_check_box, 0, wx.ALIGN_LEFT)
- control_box.AddStretchSpacer()
+ forms.check_box(
+ parent=self, sizer=axes_options_box, label='Autorange',
+ ps=parent, key=AUTORANGE_KEY,
+ )
##################################################
# Channel Options
##################################################
TRIGGER_PAGE_INDEX = parent.num_inputs
XY_PAGE_INDEX = parent.num_inputs+1
- control_box.Add(common.LabelText(self, 'Channel Options'), 0, wx.ALIGN_CENTER)
- control_box.AddSpacer(2)
+ control_box.AddStretchSpacer()
+ chan_options_box = forms.static_box_sizer(
+ parent=self, sizer=control_box, label='Channel Options',
+ bold=True, orient=wx.VERTICAL,
+ )
options_notebook = wx.Notebook(self)
- control_box.Add(options_notebook, 0, wx.EXPAND)
- def options_notebook_changed(evt):
- try:
- parent[TRIGGER_SHOW_KEY] = options_notebook.GetSelection() == TRIGGER_PAGE_INDEX
- parent[XY_MODE_KEY] = options_notebook.GetSelection() == XY_PAGE_INDEX
- except wx.PyDeadObjectError: pass
- options_notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, options_notebook_changed)
- def xy_mode_changed(mode):
- #ensure xy tab is selected
- if mode and options_notebook.GetSelection() != XY_PAGE_INDEX:
- options_notebook.SetSelection(XY_PAGE_INDEX)
- #ensure xy tab is not selected
- elif not mode and options_notebook.GetSelection() == XY_PAGE_INDEX:
- options_notebook.SetSelection(0)
- #show/hide control buttons
- scope_mode_box.ShowItems(not mode)
- xy_mode_box.ShowItems(mode)
- control_box.Layout()
- parent.subscribe(XY_MODE_KEY, xy_mode_changed)
+ options_notebook_args = list()
+ CHANNELS = [('Ch %d'%(i+1), i) for i in range(parent.num_inputs)]
##################################################
# Channel Menu Boxes
##################################################
for i in range(parent.num_inputs):
channel_menu_panel = wx.Panel(options_notebook)
- options_notebook.AddPage(channel_menu_panel, 'Ch%d'%(i+1))
+ options_notebook_args.append((channel_menu_panel, i, 'Ch%d'%(i+1)))
channel_menu_box = wx.BoxSizer(wx.VERTICAL)
channel_menu_panel.SetSizer(channel_menu_box)
#ac couple check box
channel_menu_box.AddStretchSpacer()
- coupling_chooser = common.DropDownController(channel_menu_panel, COUPLING_MODES, parent, common.index_key(AC_COUPLE_KEY, i), SIZE)
- channel_menu_box.Add(common.LabelBox(channel_menu_panel, 'Coupling', coupling_chooser), 0, wx.EXPAND)
+ forms.drop_down(
+ parent=channel_menu_panel, sizer=channel_menu_box,
+ ps=parent, key=common.index_key(AC_COUPLE_KEY, i),
+ choices=map(lambda x: x[1], COUPLING_MODES),
+ labels=map(lambda x: x[0], COUPLING_MODES),
+ label='Coupling', width=WIDTH,
+ )
#marker
channel_menu_box.AddStretchSpacer()
- marker_chooser = common.DropDownController(channel_menu_panel, MARKER_TYPES, parent, common.index_key(MARKER_KEY, i), SIZE)
- channel_menu_box.Add(common.LabelBox(channel_menu_panel, 'Marker', marker_chooser), 0, wx.EXPAND)
+ forms.drop_down(
+ parent=channel_menu_panel, sizer=channel_menu_box,
+ ps=parent, key=common.index_key(MARKER_KEY, i),
+ choices=map(lambda x: x[1], MARKER_TYPES),
+ labels=map(lambda x: x[0], MARKER_TYPES),
+ label='Marker', width=WIDTH,
+ )
channel_menu_box.AddStretchSpacer()
##################################################
# Trigger Menu Box
##################################################
trigger_menu_panel = wx.Panel(options_notebook)
- options_notebook.AddPage(trigger_menu_panel, 'Trig')
+ options_notebook_args.append((trigger_menu_panel, TRIGGER_PAGE_INDEX, 'Trig'))
trigger_menu_box = wx.BoxSizer(wx.VERTICAL)
trigger_menu_panel.SetSizer(trigger_menu_box)
#trigger mode
- trigger_mode_chooser = common.DropDownController(trigger_menu_panel, TRIGGER_MODES, parent, TRIGGER_MODE_KEY, SIZE)
- trigger_menu_box.Add(common.LabelBox(trigger_menu_panel, 'Mode', trigger_mode_chooser), 0, wx.EXPAND)
+ forms.drop_down(
+ parent=trigger_menu_panel, sizer=trigger_menu_box,
+ ps=parent, key=TRIGGER_MODE_KEY,
+ choices=map(lambda x: x[1], TRIGGER_MODES),
+ labels=map(lambda x: x[0], TRIGGER_MODES),
+ label='Mode', width=WIDTH,
+ )
#trigger slope
- trigger_slope_chooser = common.DropDownController(trigger_menu_panel, TRIGGER_SLOPES, parent, TRIGGER_SLOPE_KEY, SIZE)
- parent.subscribe(TRIGGER_MODE_KEY, lambda x: trigger_slope_chooser.Enable(x!=gr.gr_TRIG_MODE_FREE))
- trigger_menu_box.Add(common.LabelBox(trigger_menu_panel, 'Slope', trigger_slope_chooser), 0, wx.EXPAND)
+ trigger_slope_chooser = forms.drop_down(
+ parent=trigger_menu_panel, sizer=trigger_menu_box,
+ ps=parent, key=TRIGGER_SLOPE_KEY,
+ choices=map(lambda x: x[1], TRIGGER_SLOPES),
+ labels=map(lambda x: x[0], TRIGGER_SLOPES),
+ label='Slope', width=WIDTH,
+ )
#trigger channel
- choices = [('Channel %d'%(i+1), i) for i in range(parent.num_inputs)]
- trigger_channel_chooser = common.DropDownController(trigger_menu_panel, choices, parent, TRIGGER_CHANNEL_KEY, SIZE)
- parent.subscribe(TRIGGER_MODE_KEY, lambda x: trigger_channel_chooser.Enable(x!=gr.gr_TRIG_MODE_FREE))
- trigger_menu_box.Add(common.LabelBox(trigger_menu_panel, 'Channel', trigger_channel_chooser), 0, wx.EXPAND)
+ trigger_channel_chooser = forms.drop_down(
+ parent=trigger_menu_panel, sizer=trigger_menu_box,
+ ps=parent, key=TRIGGER_CHANNEL_KEY,
+ choices=map(lambda x: x[1], CHANNELS),
+ labels=map(lambda x: x[0], CHANNELS),
+ label='Channel', width=WIDTH,
+ )
#trigger level
hbox = wx.BoxSizer(wx.HORIZONTAL)
trigger_menu_box.Add(hbox, 0, wx.EXPAND)
- hbox.Add(wx.StaticText(trigger_menu_panel, label=' Level '), 1, wx.ALIGN_CENTER_VERTICAL)
- trigger_level_button = wx.Button(trigger_menu_panel, label='50%', style=wx.BU_EXACTFIT)
- parent.subscribe(TRIGGER_MODE_KEY, lambda x: trigger_level_button.Enable(x!=gr.gr_TRIG_MODE_FREE))
- trigger_level_button.Bind(wx.EVT_BUTTON, self.parent.set_auto_trigger_level)
- hbox.Add(trigger_level_button, 0, wx.ALIGN_CENTER_VERTICAL)
- hbox.AddSpacer(10)
- trigger_level_buttons = common.IncrDecrButtons(trigger_menu_panel, self._on_incr_trigger_level, self._on_decr_trigger_level)
- parent.subscribe(TRIGGER_MODE_KEY, lambda x: trigger_level_buttons.Enable(x!=gr.gr_TRIG_MODE_FREE))
- hbox.Add(trigger_level_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
+ hbox.Add(wx.StaticText(trigger_menu_panel, label='Level:'), 1, wx.ALIGN_CENTER_VERTICAL)
+ trigger_level_button = forms.single_button(
+ parent=trigger_menu_panel, sizer=hbox, label='50%',
+ callback=parent.set_auto_trigger_level, style=wx.BU_EXACTFIT,
+ )
+ hbox.AddSpacer(WIDTH-60)
+ trigger_level_buttons = forms.incr_decr_buttons(
+ parent=trigger_menu_panel, sizer=hbox,
+ on_incr=self._on_incr_trigger_level, on_decr=self._on_decr_trigger_level,
+ )
+ def disable_all(trigger_mode):
+ for widget in (trigger_slope_chooser, trigger_channel_chooser, trigger_level_buttons, trigger_level_button):
+ widget.Disable(trigger_mode == gr.gr_TRIG_MODE_FREE)
+ parent.subscribe(TRIGGER_MODE_KEY, disable_all)
+ disable_all(parent[TRIGGER_MODE_KEY])
##################################################
# XY Menu Box
##################################################
if parent.num_inputs > 1:
xy_menu_panel = wx.Panel(options_notebook)
- options_notebook.AddPage(xy_menu_panel, 'XY')
+ options_notebook_args.append((xy_menu_panel, XY_PAGE_INDEX, 'XY'))
xy_menu_box = wx.BoxSizer(wx.VERTICAL)
xy_menu_panel.SetSizer(xy_menu_box)
#x and y channel choosers
xy_menu_box.AddStretchSpacer()
- choices = [('Ch%d'%(i+1), i) for i in range(parent.num_inputs)]
- x_channel_chooser = common.DropDownController(xy_menu_panel, choices, parent, X_CHANNEL_KEY, SIZE)
- xy_menu_box.Add(common.LabelBox(xy_menu_panel, 'Ch X', x_channel_chooser), 0, wx.EXPAND)
+ forms.drop_down(
+ parent=xy_menu_panel, sizer=xy_menu_box,
+ ps=parent, key=X_CHANNEL_KEY,
+ choices=map(lambda x: x[1], CHANNELS),
+ labels=map(lambda x: x[0], CHANNELS),
+ label='Channel X', width=WIDTH,
+ )
xy_menu_box.AddStretchSpacer()
- y_channel_chooser = common.DropDownController(xy_menu_panel, choices, parent, Y_CHANNEL_KEY, SIZE)
- xy_menu_box.Add(common.LabelBox(xy_menu_panel, 'Ch Y', y_channel_chooser), 0, wx.EXPAND)
+ forms.drop_down(
+ parent=xy_menu_panel, sizer=xy_menu_box,
+ ps=parent, key=Y_CHANNEL_KEY,
+ choices=map(lambda x: x[1], CHANNELS),
+ labels=map(lambda x: x[0], CHANNELS),
+ label='Channel Y', width=WIDTH,
+ )
#marker
xy_menu_box.AddStretchSpacer()
- marker_chooser = common.DropDownController(xy_menu_panel, MARKER_TYPES, parent, XY_MARKER_KEY, SIZE)
- xy_menu_box.Add(common.LabelBox(xy_menu_panel, 'Marker', marker_chooser), 0, wx.EXPAND)
+ forms.drop_down(
+ parent=xy_menu_panel, sizer=xy_menu_box,
+ ps=parent, key=XY_MARKER_KEY,
+ choices=map(lambda x: x[1], MARKER_TYPES),
+ labels=map(lambda x: x[0], MARKER_TYPES),
+ label='Marker', width=WIDTH,
+ )
xy_menu_box.AddStretchSpacer()
##################################################
+ # Setup Options Notebook
+ ##################################################
+ forms.notebook(
+ parent=self, sizer=chan_options_box,
+ notebook=options_notebook,
+ ps=parent, key=CHANNEL_OPTIONS_KEY,
+ pages=map(lambda x: x[0], options_notebook_args),
+ choices=map(lambda x: x[1], options_notebook_args),
+ labels=map(lambda x: x[2], options_notebook_args),
+ )
+ #gui handling for channel options changing
+ def options_notebook_changed(chan_opt):
+ try:
+ parent[TRIGGER_SHOW_KEY] = chan_opt == TRIGGER_PAGE_INDEX
+ parent[XY_MODE_KEY] = chan_opt == XY_PAGE_INDEX
+ except wx.PyDeadObjectError: pass
+ parent.subscribe(CHANNEL_OPTIONS_KEY, options_notebook_changed)
+ #gui handling for xy mode changing
+ def xy_mode_changed(mode):
+ #ensure xy tab is selected
+ if mode and parent[CHANNEL_OPTIONS_KEY] != XY_PAGE_INDEX:
+ parent[CHANNEL_OPTIONS_KEY] = XY_PAGE_INDEX
+ #ensure xy tab is not selected
+ elif not mode and parent[CHANNEL_OPTIONS_KEY] == XY_PAGE_INDEX:
+ parent[CHANNEL_OPTIONS_KEY] = 0
+ #show/hide control buttons
+ scope_mode_box.ShowItems(not mode)
+ xy_mode_box.ShowItems(mode)
+ control_box.Layout()
+ parent.subscribe(XY_MODE_KEY, xy_mode_changed)
+ xy_mode_changed(parent[XY_MODE_KEY])
+ ##################################################
# Run/Stop Button
##################################################
#run/stop
- self.run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run')
- control_box.Add(self.run_button, 0, wx.EXPAND)
+ control_box.AddStretchSpacer()
+ forms.toggle_button(
+ sizer=control_box, parent=self,
+ true_label='Stop', false_label='Run',
+ ps=parent, key=RUNNING_KEY,
+ )
#set sizer
self.SetSizerAndFit(control_box)
#mouse wheel event
@@ -323,28 +401,10 @@ class scope_window(wx.Panel, pubsub.pubsub):
self.proxy(TRIGGER_SLOPE_KEY, controller, trigger_slope_key)
self.proxy(TRIGGER_CHANNEL_KEY, controller, trigger_channel_key)
self.proxy(DECIMATION_KEY, controller, decimation_key)
- for i in range(num_inputs):
- self.proxy(common.index_key(AC_COUPLE_KEY, i), controller, common.index_key(ac_couple_key, i))
- #init panel and plot
- wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
- self.plotter = plotter.channel_plotter(self)
- self.plotter.SetSize(wx.Size(*size))
- self.plotter.set_title(title)
- self.plotter.enable_legend(True)
- self.plotter.enable_point_label(True)
- self.plotter.enable_grid_lines(True)
- #setup the box with plot and controls
- self.control_panel = control_panel(self)
- main_box = wx.BoxSizer(wx.HORIZONTAL)
- main_box.Add(self.plotter, 1, wx.EXPAND)
- main_box.Add(self.control_panel, 0, wx.EXPAND)
- self.SetSizerAndFit(main_box)
#initialize values
self[RUNNING_KEY] = True
- for i in range(self.num_inputs):
- self[common.index_key(AC_COUPLE_KEY, i)] = self[common.index_key(AC_COUPLE_KEY, i)]
- self[common.index_key(MARKER_KEY, i)] = DEFAULT_MARKER_TYPE
self[XY_MARKER_KEY] = 2.0
+ self[CHANNEL_OPTIONS_KEY] = 0
self[XY_MODE_KEY] = xy_mode
self[X_CHANNEL_KEY] = 0
self[Y_CHANNEL_KEY] = self.num_inputs-1
@@ -364,6 +424,22 @@ class scope_window(wx.Panel, pubsub.pubsub):
self[TRIGGER_MODE_KEY] = gr.gr_TRIG_MODE_AUTO
self[TRIGGER_SLOPE_KEY] = gr.gr_TRIG_SLOPE_POS
self[T_FRAC_OFF_KEY] = 0.5
+ for i in range(num_inputs):
+ self.proxy(common.index_key(AC_COUPLE_KEY, i), controller, common.index_key(ac_couple_key, i))
+ #init panel and plot
+ wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER)
+ self.plotter = plotter.channel_plotter(self)
+ self.plotter.SetSize(wx.Size(*size))
+ self.plotter.set_title(title)
+ self.plotter.enable_legend(True)
+ self.plotter.enable_point_label(True)
+ self.plotter.enable_grid_lines(True)
+ #setup the box with plot and controls
+ self.control_panel = control_panel(self)
+ main_box = wx.BoxSizer(wx.HORIZONTAL)
+ main_box.Add(self.plotter, 1, wx.EXPAND)
+ main_box.Add(self.control_panel, 0, wx.EXPAND)
+ self.SetSizerAndFit(main_box)
#register events for message
self.subscribe(MSG_KEY, self.handle_msg)
#register events for grid
diff --git a/gr-wxgui/src/python/waterfall_window.py b/gr-wxgui/src/python/waterfall_window.py
index 8dcb4b619..77819b733 100644
--- a/gr-wxgui/src/python/waterfall_window.py
+++ b/gr-wxgui/src/python/waterfall_window.py
@@ -30,6 +30,7 @@ import math
import pubsub
from constants import *
from gnuradio import gr #for gr.prefs
+import forms
##################################################
# Constants
@@ -64,54 +65,75 @@ class control_panel(wx.Panel):
wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
control_box = wx.BoxSizer(wx.VERTICAL)
control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER)
- #color mode
- control_box.AddStretchSpacer()
- color_mode_chooser = common.DropDownController(self, COLOR_MODES, parent, COLOR_MODE_KEY)
- control_box.Add(common.LabelBox(self, 'Color', color_mode_chooser), 0, wx.EXPAND)
+ options_box = forms.static_box_sizer(
+ parent=self, sizer=control_box, label='Options',
+ bold=True, orient=wx.VERTICAL,
+ )
#average
+ forms.check_box(
+ sizer=options_box, parent=self, label='Average',
+ ps=parent, key=AVERAGE_KEY,
+ )
+ avg_alpha_text = forms.static_text(
+ sizer=options_box, parent=self, label='Avg Alpha',
+ converter=forms.float_converter(lambda x: '%.4f'%x),
+ ps=parent, key=AVG_ALPHA_KEY, width=50,
+ )
+ avg_alpha_slider = forms.log_slider(
+ sizer=options_box, parent=self,
+ min_exp=AVG_ALPHA_MIN_EXP,
+ max_exp=AVG_ALPHA_MAX_EXP,
+ num_steps=SLIDER_STEPS,
+ ps=parent, key=AVG_ALPHA_KEY,
+ )
+ for widget in (avg_alpha_text, avg_alpha_slider):
+ parent.subscribe(AVERAGE_KEY, widget.Enable)
+ widget.Enable(parent[AVERAGE_KEY])
+ #begin axes box
control_box.AddStretchSpacer()
- average_check_box = common.CheckBoxController(self, 'Average', parent, AVERAGE_KEY)
- control_box.Add(average_check_box, 0, wx.EXPAND)
- control_box.AddSpacer(2)
- avg_alpha_slider = common.LogSliderController(
- self, 'Avg Alpha',
- AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
- parent, AVG_ALPHA_KEY,
- formatter=lambda x: ': %.4f'%x,
+ axes_box = forms.static_box_sizer(
+ parent=self, sizer=control_box, label='Axes Options',
+ bold=True, orient=wx.VERTICAL,
+ )
+ #num lines buttons
+ forms.incr_decr_buttons(
+ parent=self, sizer=axes_box, label='Time Scale',
+ on_incr=self._on_incr_time_scale, on_decr=self._on_decr_time_scale,
)
- parent.subscribe(AVERAGE_KEY, avg_alpha_slider.Enable)
- control_box.Add(avg_alpha_slider, 0, wx.EXPAND)
#dyanmic range buttons
- control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Dynamic Range'), 0, wx.ALIGN_CENTER)
- control_box.AddSpacer(2)
- dynamic_range_buttons = common.IncrDecrButtons(self, self._on_incr_dynamic_range, self._on_decr_dynamic_range)
- control_box.Add(dynamic_range_buttons, 0, wx.ALIGN_CENTER)
+ forms.incr_decr_buttons(
+ parent=self, sizer=axes_box, label='Dyn Range',
+ on_incr=self._on_incr_dynamic_range, on_decr=self._on_decr_dynamic_range,
+ )
#ref lvl buttons
- control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Set Ref Level'), 0, wx.ALIGN_CENTER)
- control_box.AddSpacer(2)
- ref_lvl_buttons = common.IncrDecrButtons(self, self._on_incr_ref_level, self._on_decr_ref_level)
- control_box.Add(ref_lvl_buttons, 0, wx.ALIGN_CENTER)
- #num lines buttons
- control_box.AddStretchSpacer()
- control_box.Add(common.LabelText(self, 'Set Time Scale'), 0, wx.ALIGN_CENTER)
- control_box.AddSpacer(2)
- time_scale_buttons = common.IncrDecrButtons(self, self._on_incr_time_scale, self._on_decr_time_scale)
- control_box.Add(time_scale_buttons, 0, wx.ALIGN_CENTER)
+ forms.incr_decr_buttons(
+ parent=self, sizer=axes_box, label='Ref Level',
+ on_incr=self._on_incr_ref_level, on_decr=self._on_decr_ref_level,
+ )
+ #color mode
+ forms.drop_down(
+ parent=self, sizer=axes_box, width=100,
+ ps=parent, key=COLOR_MODE_KEY, label='Color',
+ choices=map(lambda x: x[1], COLOR_MODES),
+ labels=map(lambda x: x[0], COLOR_MODES),
+ )
#autoscale
- control_box.AddStretchSpacer()
- autoscale_button = wx.Button(self, label='Autoscale', style=wx.BU_EXACTFIT)
- autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale)
- control_box.Add(autoscale_button, 0, wx.EXPAND)
+ forms.single_button(
+ parent=self, sizer=axes_box, label='Autoscale',
+ callback=self.parent.autoscale,
+ )
#clear
- clear_button = wx.Button(self, label='Clear', style=wx.BU_EXACTFIT)
- clear_button.Bind(wx.EVT_BUTTON, self._on_clear_button)
- control_box.Add(clear_button, 0, wx.EXPAND)
+ control_box.AddStretchSpacer()
+ forms.single_button(
+ parent=self, sizer=control_box, label='Clear',
+ callback=self._on_clear_button,
+ )
#run/stop
- run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run')
- control_box.Add(run_button, 0, wx.EXPAND)
+ forms.toggle_button(
+ sizer=control_box, parent=self,
+ true_label='Stop', false_label='Run',
+ ps=parent, key=RUNNING_KEY,
+ )
#set sizer
self.SetSizerAndFit(control_box)
@@ -181,18 +203,10 @@ class waterfall_window(wx.Panel, pubsub.pubsub):
self.plotter.set_title(title)
self.plotter.enable_point_label(True)
self.plotter.enable_grid_lines(False)
- #setup the box with plot and controls
- self.control_panel = control_panel(self)
- main_box = wx.BoxSizer(wx.HORIZONTAL)
- main_box.Add(self.plotter, 1, wx.EXPAND)
- main_box.Add(self.control_panel, 0, wx.EXPAND)
- self.SetSizerAndFit(main_box)
#plotter listeners
self.subscribe(COLOR_MODE_KEY, self.plotter.set_color_mode)
self.subscribe(NUM_LINES_KEY, self.plotter.set_num_lines)
#initialize values
- self[AVERAGE_KEY] = self[AVERAGE_KEY]
- self[AVG_ALPHA_KEY] = self[AVG_ALPHA_KEY]
self[DYNAMIC_RANGE_KEY] = dynamic_range
self[NUM_LINES_KEY] = num_lines
self[Y_DIVS_KEY] = 8
@@ -201,6 +215,12 @@ class waterfall_window(wx.Panel, pubsub.pubsub):
self[BASEBAND_FREQ_KEY] = baseband_freq
self[COLOR_MODE_KEY] = COLOR_MODES[0][1]
self[RUNNING_KEY] = True
+ #setup the box with plot and controls
+ self.control_panel = control_panel(self)
+ main_box = wx.BoxSizer(wx.HORIZONTAL)
+ main_box.Add(self.plotter, 1, wx.EXPAND)
+ main_box.Add(self.control_panel, 0, wx.EXPAND)
+ self.SetSizerAndFit(main_box)
#register events
self.subscribe(MSG_KEY, self.handle_msg)
for key in (