diff options
-rw-r--r-- | gr-wxgui/src/python/Makefile.am | 7 | ||||
-rw-r--r-- | gr-wxgui/src/python/common.py | 162 | ||||
-rw-r--r-- | gr-wxgui/src/python/const_window.py | 68 | ||||
-rw-r--r-- | gr-wxgui/src/python/constants.py | 1 | ||||
-rw-r--r-- | gr-wxgui/src/python/fft_window.py | 116 | ||||
-rw-r--r-- | gr-wxgui/src/python/forms/__init__.py | 103 | ||||
-rw-r--r-- | gr-wxgui/src/python/forms/converters.py (renamed from grc/src/grc_gnuradio/wxgui/forms/converters.py) | 4 | ||||
-rw-r--r-- | gr-wxgui/src/python/forms/forms.py (renamed from grc/src/grc_gnuradio/wxgui/forms/forms.py) | 254 | ||||
-rw-r--r-- | gr-wxgui/src/python/histo_window.py | 43 | ||||
-rw-r--r-- | gr-wxgui/src/python/number_window.py | 128 | ||||
-rw-r--r-- | gr-wxgui/src/python/scope_window.py | 298 | ||||
-rw-r--r-- | gr-wxgui/src/python/waterfall_window.py | 116 | ||||
-rw-r--r-- | grc/data/platforms/python/blocks/variable_chooser.xml | 2 | ||||
-rw-r--r-- | grc/data/platforms/python/blocks/variable_slider.xml | 2 | ||||
-rw-r--r-- | grc/data/platforms/python/blocks/variable_text_box.xml | 2 | ||||
-rw-r--r-- | grc/src/grc_gnuradio/wxgui/Makefile.am | 6 | ||||
-rw-r--r-- | grc/src/grc_gnuradio/wxgui/forms/__init__.py | 54 |
17 files changed, 784 insertions, 582 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/grc/src/grc_gnuradio/wxgui/forms/converters.py b/gr-wxgui/src/python/forms/converters.py index 5971cfc09..123aefeb0 100644 --- a/grc/src/grc_gnuradio/wxgui/forms/converters.py +++ b/gr-wxgui/src/python/forms/converters.py @@ -107,8 +107,10 @@ class int_converter(abstract_converter): 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 eng_notation.num_to_str(v) + return self._formatter(v) def internal_to_external(self, s): return eng_notation.str_to_num(s) def help(self): diff --git a/grc/src/grc_gnuradio/wxgui/forms/forms.py b/gr-wxgui/src/python/forms/forms.py index 5c5b6bad5..10f6a4823 100644 --- a/grc/src/grc_gnuradio/wxgui/forms/forms.py +++ b/gr-wxgui/src/python/forms/forms.py @@ -33,6 +33,10 @@ The forms follow a layered model: * 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' @@ -49,6 +53,11 @@ class DataEvent(wx.PyEvent): 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 ######################################################################## @@ -57,6 +66,7 @@ class _form_base(pubsub, wx.BoxSizer): pubsub.__init__(self) wx.BoxSizer.__init__(self, wx.HORIZONTAL) self._parent = parent + self._key = key self._converter = converter self._callback = callback self._widgets = list() @@ -69,7 +79,10 @@ class _form_base(pubsub, wx.BoxSizer): #no pubsub passed, must set initial value else: self.set_value(value) - def _add_widget(self, widget, label='', flag=0): + 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. @@ -80,6 +93,8 @@ class _form_base(pubsub, wx.BoxSizer): @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)) @@ -87,12 +102,12 @@ class _form_base(pubsub, wx.BoxSizer): #register widget self._widgets.append(widget) #create optional label - if not label: self.Add(widget, 1, wx.ALIGN_CENTER_VERTICAL | flag) + 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, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) - self.Add(widget, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | flag) + 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]) @@ -121,7 +136,7 @@ class _form_base(pubsub, wx.BoxSizer): self[EXT_KEY] = self[EXT_KEY] #reset to last good setting def _err_msg(self, value, e): - print >> sys.stderr, 'Error translating value: "%s"\n\t%s\n\t%s'%(value, e, self._converter.help()) + 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 @@ -139,24 +154,73 @@ class _form_base(pubsub, wx.BoxSizer): 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: - font = self._static_text.GetFont() - font.SetWeight(wx.FONTWEIGHT_BOLD) - self._static_text.SetFont(font) + if bold: make_bold(self._static_text) self._add_widget(self._static_text, label) - def _update(self, label): self._static_text.SetLabel(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) @@ -168,28 +232,26 @@ class text_box(_form_base): ######################################################################## # Slider Form +# Linear Slider +# Logarithmic Slider ######################################################################## -class _slider_base(_form_base): - """ - Base class for linear and log slider. - @param length the length of the slider in px - @param style wx.SL_HORIZONTAL or wx.SL_VERTICAL - """ - 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) - 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): @@ -200,7 +262,23 @@ class slider(_slider_base): class log_slider(_slider_base): """ - A generic log slider. + 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 @@ -209,9 +287,57 @@ class log_slider(_slider_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) @@ -222,23 +348,28 @@ class check_box(_form_base): def _update(self, checked): self._check_box.SetValue(checked) ######################################################################## -# 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) - -######################################################################## # Drop Down Chooser Form ######################################################################## class drop_down(_chooser_base): - def __init__(self, label='', **kwargs): + """ + 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) + 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) + 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) @@ -250,19 +381,45 @@ class drop_down(_chooser_base): # 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) + 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. + 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) @@ -272,6 +429,16 @@ 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) @@ -285,6 +452,7 @@ class radio_buttons(_chooser_base): @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) 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 ( diff --git a/grc/data/platforms/python/blocks/variable_chooser.xml b/grc/data/platforms/python/blocks/variable_chooser.xml index 6827c3675..3ca33045a 100644 --- a/grc/data/platforms/python/blocks/variable_chooser.xml +++ b/grc/data/platforms/python/blocks/variable_chooser.xml @@ -9,7 +9,7 @@ <block> <name>Variable Chooser</name> <key>variable_chooser</key> - <import>from grc_gnuradio.wxgui import forms</import> + <import>from gnuradio.wxgui import forms</import> <make>$value self['$id'] = $id self.subscribe('$id', self.set_$(id)) diff --git a/grc/data/platforms/python/blocks/variable_slider.xml b/grc/data/platforms/python/blocks/variable_slider.xml index 9c8e7ece9..f8a5543f0 100644 --- a/grc/data/platforms/python/blocks/variable_slider.xml +++ b/grc/data/platforms/python/blocks/variable_slider.xml @@ -8,7 +8,7 @@ <block> <name>Variable Slider</name> <key>variable_slider</key> - <import>from grc_gnuradio.wxgui import forms</import> + <import>from gnuradio.wxgui import forms</import> <make>$value self['$id'] = $id self.subscribe('$id', self.set_$(id)) diff --git a/grc/data/platforms/python/blocks/variable_text_box.xml b/grc/data/platforms/python/blocks/variable_text_box.xml index 2857fa366..b5acd9d2e 100644 --- a/grc/data/platforms/python/blocks/variable_text_box.xml +++ b/grc/data/platforms/python/blocks/variable_text_box.xml @@ -8,7 +8,7 @@ <block> <name>Variable Text Box</name> <key>variable_text_box</key> - <import>from grc_gnuradio.wxgui import forms</import> + <import>from gnuradio.wxgui import forms</import> <make>$value self['$id'] = $id self.subscribe('$id', self.set_$(id)) diff --git a/grc/src/grc_gnuradio/wxgui/Makefile.am b/grc/src/grc_gnuradio/wxgui/Makefile.am index b82ca4c90..2e5e7ebd0 100644 --- a/grc/src/grc_gnuradio/wxgui/Makefile.am +++ b/grc/src/grc_gnuradio/wxgui/Makefile.am @@ -25,9 +25,3 @@ ourpythondir = $(grc_gnuradio_prefix)/wxgui ourpython_PYTHON = \ __init__.py \ top_block_gui.py - -oursubpythondir = $(grc_gnuradio_prefix)/wxgui/forms -oursubpython_PYTHON = \ - forms/__init__.py \ - forms/converters.py \ - forms/forms.py diff --git a/grc/src/grc_gnuradio/wxgui/forms/__init__.py b/grc/src/grc_gnuradio/wxgui/forms/__init__.py deleted file mode 100644 index 07226668b..000000000 --- a/grc/src/grc_gnuradio/wxgui/forms/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# 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 - -######################################################################## -# Helpful widgets -######################################################################## -import wx - -class static_box_sizer(wx.StaticBoxSizer): - def __init__(self, parent, label='', bold=False, orient=wx.VERTICAL): - box = wx.StaticBox(parent=parent, label=label) - if bold: - font = box.GetFont() - font.SetWeight(wx.FONTWEIGHT_BOLD) - box.SetFont(font) - wx.StaticBoxSizer.__init__(self, box=box, orient=orient) |