summaryrefslogtreecommitdiff
path: root/gr-qtgui/python
diff options
context:
space:
mode:
authorJosh Blum2010-12-06 22:46:39 -0500
committerJosh Blum2011-03-01 17:02:06 -0800
commit9ae8997e5f682bc16e58c0a0bba21794dbd705c5 (patch)
tree8ffd4f98e92a1e9261f88574d0c2679b11cabaec /gr-qtgui/python
parent0e076c5835ea624c25e7be964e946d9f6425f414 (diff)
downloadgnuradio-9ae8997e5f682bc16e58c0a0bba21794dbd705c5.tar.gz
gnuradio-9ae8997e5f682bc16e58c0a0bba21794dbd705c5.tar.bz2
gnuradio-9ae8997e5f682bc16e58c0a0bba21794dbd705c5.zip
qtgui: created a forms module with a slider and text box, implemented in grc
Diffstat (limited to 'gr-qtgui/python')
-rw-r--r--gr-qtgui/python/.gitignore2
-rw-r--r--gr-qtgui/python/Makefile.am29
-rw-r--r--gr-qtgui/python/forms/__init__.py32
-rw-r--r--gr-qtgui/python/forms/converters.py154
-rw-r--r--gr-qtgui/python/forms/forms.py140
5 files changed, 357 insertions, 0 deletions
diff --git a/gr-qtgui/python/.gitignore b/gr-qtgui/python/.gitignore
new file mode 100644
index 000000000..b336cc7ce
--- /dev/null
+++ b/gr-qtgui/python/.gitignore
@@ -0,0 +1,2 @@
+/Makefile
+/Makefile.in
diff --git a/gr-qtgui/python/Makefile.am b/gr-qtgui/python/Makefile.am
new file mode 100644
index 000000000..5f6d22b17
--- /dev/null
+++ b/gr-qtgui/python/Makefile.am
@@ -0,0 +1,29 @@
+#
+# Copyright 2010 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.
+#
+
+include $(top_srcdir)/Makefile.common
+
+qtguiformspythondir = $(grpythondir)/qtgui/forms
+
+qtguiformspython_PYTHON = \
+ forms/__init__.py \
+ forms/converters.py \
+ forms/forms.py
diff --git a/gr-qtgui/python/forms/__init__.py b/gr-qtgui/python/forms/__init__.py
new file mode 100644
index 000000000..0f4391bb8
--- /dev/null
+++ b/gr-qtgui/python/forms/__init__.py
@@ -0,0 +1,32 @@
+#
+# Copyright 2010 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.
+#
+
+########################################################################
+# External Converters
+########################################################################
+from converters import \
+ eval_converter, str_converter, \
+ float_converter, int_converter
+
+########################################################################
+# External Forms
+########################################################################
+from forms import slider, text_box
diff --git a/gr-qtgui/python/forms/converters.py b/gr-qtgui/python/forms/converters.py
new file mode 100644
index 000000000..53c966d32
--- /dev/null
+++ b/gr-qtgui/python/forms/converters.py
@@ -0,0 +1,154 @@
+#
+# 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):
+ #choices must be a list because tuple does not have .index() in python2.5
+ self._choices = list(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):
+ if v == self._true: return True
+ if v == self._false: return False
+ raise Exception, 'Value "%s" is not a possible option.'%v
+ def internal_to_external(self, v):
+ if v: return self._true
+ else: return self._false
+ def help(self):
+ return "Value must be in (%s, %s)."%(self._true, self._false)
+
+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 __init__(self, formatter=lambda x: '%s'%(x)):
+ self._formatter = formatter
+ def external_to_internal(self, v):
+ return self._formatter(v)
+ 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 __init__(self, formatter=lambda x: '%s'%(x)):
+ self._formatter = formatter
+ def external_to_internal(self, v):
+ return self._formatter(v)
+ def internal_to_external(self, s):
+ return str(s)
+
+class int_converter(abstract_converter):
+ def __init__(self, formatter=lambda x: '%d'%round(x)):
+ self._formatter = formatter
+ def external_to_internal(self, v):
+ return self._formatter(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, start, stop, num_steps, cast):
+ assert start < stop
+ assert num_steps > 0
+ self._offset = start
+ self._scaler = float(stop - start)/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, start=min_exp, stop=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-qtgui/python/forms/forms.py b/gr-qtgui/python/forms/forms.py
new file mode 100644
index 000000000..56ee72499
--- /dev/null
+++ b/gr-qtgui/python/forms/forms.py
@@ -0,0 +1,140 @@
+#
+# Copyright 2010 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 PyQt4 import QtGui
+from PyQt4.QtCore import Qt
+
+import converters
+
+########################################################################
+# Base class for all forms
+########################################################################
+class _form_base(QtGui.QWidget):
+ def __init__(self, parent=None, converter=None, callback=None, value=None):
+ QtGui.QWidget.__init__(self, parent)
+ self._layout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self)
+ self._converter = converter
+ self._callback = callback
+ self._value = value
+
+ def _add_widget(self, widget, label):
+ if label:
+ label_widget = QtGui.QLabel(self)
+ label_widget.setText('%s: '%label)
+ self._layout.addWidget(label_widget, True)
+ self._layout.addWidget(widget, stretch=1)
+ #disable callback, update, re-enable
+ callback = self._callback
+ self._callback = None
+ self.set_value(self.get_value())
+ self._callback = callback
+
+ def get_value(self):
+ return self._value
+
+ def set_value(self, value):
+ self._value = value
+ self._base_update(self._converter.external_to_internal(value))
+
+ def _base_update(self, int_val):
+ self._update(int_val)
+
+ def _base_handle(self, int_val):
+ self._value = self._converter.internal_to_external(int_val)
+ if self._callback: self._callback(self._value)
+
+########################################################################
+# Slider base class, shared by log and linear sliders
+########################################################################
+class _slider_base(_form_base):
+ def __init__(self, label='', length=-1, num_steps=100, orient=Qt.Horizontal, **kwargs):
+ _form_base.__init__(self, **kwargs)
+ self._slider = QtGui.QSlider(parent=self)
+ self._slider.setOrientation(orient)
+ self._slider.setRange(0, num_steps)
+ if length > 0:
+ if orient == Qt.Horizontal:
+ slider_size = self._slider.setWidth(length)
+ if orient == Qt.Vertical:
+ slider_size = self._slider.setHeight(length)
+ self._add_widget(self._slider, label)
+ self._slider.valueChanged.connect(self._handle)
+
+ def _handle(self, event):
+ value = self._slider.value()
+ if self._cache != value: self._base_handle(value)
+ def _update(self, value):
+ self._cache = int(round(value))
+ self._slider.setValue(self._cache)
+
+########################################################################
+# Linear slider form
+########################################################################
+class slider(_slider_base):
+ """
+ A generic linear slider.
+ @param parent the parent widget
+ @param value the default value
+ @param label title label for this widget (optional)
+ @param length the length of the slider in px (optional)
+ @param orient Qt.Horizontal Veritcal (default=horizontal)
+ @param start the start value
+ @param stop the stop 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, start, stop, step=None, num_steps=100, cast=float, **kwargs):
+ assert step or num_steps
+ if step is not None: num_steps = (stop - start)/step
+ converter = converters.slider_converter(start=start, stop=stop, num_steps=num_steps, cast=cast)
+ _slider_base.__init__(self, converter=converter, num_steps=num_steps, **kwargs)
+
+########################################################################
+# 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 value the default value
+ @param label title label for this widget (optional)
+ @param converter forms.str_converter(), int_converter(), float_converter()...
+ """
+ def __init__(self, label='', converter=converters.eval_converter(), **kwargs):
+ _form_base.__init__(self, converter=converter, **kwargs)
+ self._text_box = QtGui.QLineEdit(self)
+ self._default_style_sheet = self._text_box.styleSheet()
+ self._text_box.textChanged.connect(self._set_color_changed)
+ self._text_box.returnPressed.connect(self._handle)
+ self._add_widget(self._text_box, label)
+
+ def _set_color_default(self):
+ self._text_box.setStyleSheet(self._default_style_sheet)
+
+ def _set_color_changed(self, *args):
+ self._text_box.setStyleSheet("QWidget { background-color: #EEDDDD }")
+
+ def _handle(self): self._base_handle(str(self._text_box.text()))
+ def _update(self, value):
+ self._text_box.setText(value)
+ self._set_color_default()