commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] [gnuradio] 08/50: controlport: adding abstraction laye


From: git
Subject: [Commit-gnuradio] [gnuradio] 08/50: controlport: adding abstraction layer for the controlport backends; support thrift currently.
Date: Wed, 15 Apr 2015 21:07:51 +0000 (UTC)

This is an automated email from the git hooks/post-receive script.

jcorgan pushed a commit to branch master
in repository gnuradio.

commit f2e8dd82b5436a344dadc0faeb1f3aaec18e0e09
Author: Nate Goergen <address@hidden>
Date:   Sun Feb 22 11:56:31 2015 -0500

    controlport: adding abstraction layer for the controlport backends; support 
thrift currently.
---
 .../gnuradio/ctrlport/GNURadioControlPortClient.py | 276 ++++++++
 .../python/gnuradio/ctrlport/gr-ctrlport-monitor   | 768 +++++++++++++++++++++
 2 files changed, 1044 insertions(+)

diff --git 
a/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py 
b/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py
new file mode 100644
index 0000000..3cf5186
--- /dev/null
+++ b/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py
@@ -0,0 +1,276 @@
+#
+# Copyright 2015 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 this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+"""
+Python Client classes for interfacing with the GNU Radio ControlPort interface
+and for accessing Performance Counters.
+
+While ControlPort and these client classes are designed to support multiple
+Remote Procedure Call (RPC) transports, the Apache Thrift middle-ware RPC
+is currently the only supported transport.
+
+"""
+
+"""
+RPCMethods is a dictionary listing RPC transports currently supported
+by this client.
+
+Args:
+    function: the function whose parameter list will be examined
+    excluded_args: function arguments that are NOT to be added to the 
dictionary (sequence of strings)
+    options: result of command argument parsing (optparse.Values)
+"""
+
+RPCMethods = {'thrift': 'Apache Thrift',
+             #'ice': 'Zeroc ICE'
+             }
+
+import exceptions
+
+"""
+Base class for RPC transport clients
+
+Methods that all RPC clients should implement include:
+
+    def newConnection(host,port): Method for re-establishing a new client
+        connection to a different host / port
+
+    def properties([]): Given a list of ControlPort property names,
+        or an empty list to specify all currently registered properties,
+        this method returns a dictionary of metadata describing the
+        the specified properties. The dictionary key contains the name
+        of each returned properties.
+
+    def getKnobs([]): Given a list of ControlPort property names,
+        or an empty list to specify all currently registered properties,
+        this method returns a dictionary of the current value of
+        the specified properties.
+
+    def getRe([]): Given a list of regular expression strings,
+        this method returns a dictionary of the current value of
+        the all properties with names that match the specified
+        expressions.
+
+    def setKnobs({}): Given a dictionary of ControlPort property
+        key / value pairs, this method requests that ControlPort
+        attempt to set the specified named properties to the
+        value given. Success in setting each property to the
+        value specified requires that the property be registered
+        as a 'setable' ControlPort property, that the client have the
+        requisite privilege level to set the property, and
+        the underlying Block's implementation in handling
+        the set request.
+
+Args:
+    method: name of the RPC transport
+    port: port number of the connection
+    host: hostname of the connection
+"""
+
+class RPCConnection(object):
+    def __init__(self, method, port, host=None):
+        (self.method, self.port) = (method, port)
+        if host is None: self.host = '127.0.0.1'
+        else: self.host = host
+
+    def __str__(self):
+        return "%s connection on %s:%s"%(self.getName(), self.getHost(), 
self.getPort())
+
+    def getName(self):
+        return RPCMethods[self.method]
+
+    def getHost(self):
+        return self.host
+
+    def getPort(self):
+        return self.port
+
+    def newConnection(self, host=None, port=None):
+        raise exceptions.NotImplementedError()
+
+    def properties(self, *args):
+        raise exceptions.NotImplementedError()
+
+    def getKnobs(self, *args):
+        raise exceptions.NotImplementedError()
+
+    def getRe(self,*args):
+        raise exceptions.NotImplementedError()
+
+    def setKnobs(self,*args):
+        raise exceptions.NotImplementedError()
+
+    def shutdown(self):
+        raise exceptions.NotImplementedError()
+
+"""
+RPC Client interface for the Apache Thrift middle-ware RPC transport.
+
+Args:
+    port: port number of the connection
+    host: hostname of the connection
+"""
+
+class RPCConnectionThrift(RPCConnection):
+    def __init__(self, host=None, port=None):
+        if port is None: port = 9090
+        super(RPCConnectionThrift, self).__init__(method='thrift', port=port, 
host=host)
+        self.newConnection(host, port)
+
+    def newConnection(self, host=None, port=None):
+        from gnuradio.ctrlport.ThriftRadioClient import ThriftRadioClient
+        self.thriftclient = ThriftRadioClient(self.getHost(), self.getPort())
+
+    def properties(self, *args):
+        return self.thriftclient.radio.properties(*args)
+
+    def getKnobs(self, *args):
+        class Knob():
+            def __init__(self, key, value):
+                (self.key, self.value) = (key, value)
+
+        result = {}
+        for key, knob in self.thriftclient.radio.getKnobs(*args).iteritems():
+            if knob.type ==    0: result[key] = Knob(key, knob.value.a_bool)
+            elif knob.type ==  1: result[key] = Knob(key, knob.value.a_byte)
+            elif knob.type ==  2: result[key] = Knob(key, knob.value.a_short)
+            elif knob.type ==  3: result[key] = Knob(key, knob.value.a_int)
+            elif knob.type ==  4: result[key] = Knob(key, knob.value.a_long)
+            elif knob.type ==  5: result[key] = Knob(key, knob.value.a_double)
+            elif knob.type ==  6: result[key] = Knob(key, knob.value.a_string)
+            elif knob.type ==  7: result[key] = Knob(key, knob.value.a_complex)
+            elif knob.type ==  8: result[key] = Knob(key, 
knob.value.a_f32vector)
+            elif knob.type ==  9: result[key] = Knob(key, 
knob.value.a_f64vector)
+            elif knob.type == 10: result[key] = Knob(key, 
knob.value.a_s64vector)
+            elif knob.type == 11: result[key] = Knob(key, 
knob.value.a_s32vector)
+            elif knob.type == 12: result[key] = Knob(key, 
knob.value.a_s16vector)
+            elif knob.type == 13: result[key] = Knob(key, 
knob.value.a_s8vector)
+            elif knob.type == 14: result[key] = Knob(key, 
knob.value.a_s32vector)
+            elif knob.type == 15: result[key] = Knob(key, knob.value.byte)
+            else:
+                raise exceptions.ValueError
+
+        return result
+
+    def getRe(self,*args):
+        return self.thriftclient.radio.getRe(*args)
+
+    def setKnobs(self,*args):
+        self.thriftclient.radio.setKnobs(*args)
+
+    def shutdown(self):
+        self.thriftclient.radio.shutdown()
+
+"""
+GNURadioControlPortClient is the main class for creating a GNU Radio
+ControlPort client application for all transports.
+
+Two constructors are provided for creating a connection to ControlPort.
+
+"""
+
+class GNURadioControlPortClient():
+    """
+    Constructor for creating a ControlPort connection to a specified host / 
port
+
+    Args:
+        host: hostname of the connection. Specifying None (default) will
+            select the loopback interface.
+
+        port: port number to use for the connection. Specifying None (default)
+            will select the specified RPC transport's default port number, if
+            the transport has a default.
+
+        rpcmethod: This string specifies the RPC transport to use for the
+            client connection. The default implementation currently uses
+            the Apache Thrift RPC transport. The value specified here must
+            be one of the transport keys listed in the RPCMethods dictionary
+            above
+
+        callback: This optional parameter is a callback function that will be 
passed
+            a reference to the Client implementation for the RPC transport 
specified
+            by rpcmethod. The callback will be executed after the client has 
been
+            constructed, but before __init__ returns.
+
+        blockingcallback: This optional parameter is a callback function with
+            no parameters that will be executed after callback() is executed,
+            but before __init__ returns. It is useful if your application
+            requires that a blocking function be called to start the 
application,
+            such as QtGui.QApplication.exec_
+
+    """
+
+    def __init__(self, host = None, port = None, rpcmethod = 'thrift', 
callback = None, blockingcallback = None):
+        __init__([host, port], rpcmethod, callback, blockingcallback)
+
+    """
+    Constructor for creating a ControlPort from a tuple of command line 
arguments (i.e. sys.argv)
+
+    Args:
+        argv: List of command line arguments. Future implementations may parse 
the argument list
+            for OptionParser style key / value pairs, however the current 
implementation
+            simply takes argv[1] and argv[2] as the connection hostname and 
port, respectively.
+
+    Example Usage:
+
+        In the following QT client example, the ControlPort host and port are 
specified to
+        the Client application as the first two command line arguments. The 
MAINWindow class is
+        of the type QtGui.QMainWindow, and is the main window for the QT 
application. MyApp
+        is a simple helper class for starting the application.
+
+        class MAINWindow(QtGui.QMainWindow):
+            ... QT Application implementation ...
+
+        class MyApp(object):
+            def __init__(self, args):
+                from GNURadioControlPortClient import GNURadioControlPortClient
+                GNURadioControlPortClient(args, 'thrift', self.run, 
QtGui.QApplication(sys.argv).exec_)
+
+            def run(self, client):
+                MAINWindow(client).show()
+
+        MyApp(sys.argv)
+
+
+    """
+
+    def __init__(self, argv = [], rpcmethod = 'thrift', callback = None, 
blockingcallback = None):
+        if len(argv) > 1: host = argv[1]
+        else: host = None
+
+        if len(argv) > 2: port = argv[2]
+        else: port = None
+
+        self.client = None
+
+        if RPCMethods.has_key(rpcmethod):
+            if rpcmethod == 'thrift':
+#                 print("making RPCConnectionThrift")
+                self.client = RPCConnectionThrift(host, port)
+#                 print("made %s" % self.client)
+
+#                 print("making callback call")
+                if not callback is None: callback(self.client)
+
+#                 print("making blockingcallback call")
+                if not blockingcallback is None: blockingcallback()
+        else:
+            print("Unsupported RPC method: ", rpcmethod)
+            raise exceptions.ValueError()
diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor 
b/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor
new file mode 100644
index 0000000..5411b24
--- /dev/null
+++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor
@@ -0,0 +1,768 @@
+#!/usr/bin/env python
+#
+# Copyright 2012,2013 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 gr, ctrlport
+
+from PyQt4 import QtCore,Qt
+import PyQt4.QtGui as QtGui
+import os, sys, time, struct
+
+# from gnuradio.ctrlport.IceRadioClient import *
+from gnuradio.ctrlport.GrDataPlotter import *
+# from gnuradio.ctrlport import GNURadio
+
+class RateDialog(QtGui.QDialog):
+    def __init__(self, delay, parent=None):
+        super(RateDialog, self).__init__(parent)
+        self.gridLayout = QtGui.QGridLayout(self)
+        self.setWindowTitle("Update Delay (ms)");
+        self.delay = QtGui.QLineEdit(self);
+        self.delay.setText(str(delay));
+        self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | 
QtGui.QDialogButtonBox.Cancel)
+        self.gridLayout.addWidget(self.delay);
+        self.gridLayout.addWidget(self.buttonBox);
+        self.buttonBox.accepted.connect(self.accept)
+        self.buttonBox.rejected.connect(self.reject)
+    def accept(self):
+        self.done(1);
+    def reject(self):
+        self.done(0);
+
+class MAINWindow(QtGui.QMainWindow):
+    def minimumSizeHint(self):
+        return Qtgui.QSize(800,600)
+
+    def __init__(self, radioclient):
+
+        super(MAINWindow, self).__init__()
+        self.radioclient = radioclient
+        self.updateRate = 1000;
+        self.conns = []
+        self.plots = []
+        self.knobprops = []
+
+        self.mdiArea = QtGui.QMdiArea()
+        self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
+        self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
+        self.setCentralWidget(self.mdiArea)
+
+        self.mdiArea.subWindowActivated.connect(self.updateMenus)
+        self.windowMapper = QtCore.QSignalMapper(self)
+        
self.windowMapper.mapped[QtGui.QWidget].connect(self.setActiveSubWindow)
+
+        self.createActions()
+        self.createMenus()
+        self.createToolBars()
+        self.createStatusBar()
+        self.updateMenus()
+
+        self.setWindowTitle("GNU Radio Control Port Monitor")
+        self.setUnifiedTitleAndToolBarOnMac(True)
+
+        self.newCon(radioclient)
+        icon = QtGui.QIcon(sys.argv[0] + "/icon.png" )
+        self.setWindowIcon(icon)
+
+        # Locally turn off ControlPort export from GR. This prevents
+        # our GR-based plotters from launching their own ControlPort
+        # instance (and possibly causing a port collision if one has
+        # been specified).
+        os.environ['GR_CONF_CONTROLPORT_ON'] = 'False'
+
+    def setUpdateRate(self,nur):
+        self.updateRate = int(nur);
+        for c in self.conns:
+            c.updateRate = self.updateRate;
+            c.timer.setInterval(self.updateRate);
+
+    def newCon(self, csomeBool):
+        child = MForm(self.radioclient, len(self.conns), parent = self, 
dialogprompt = not csomeBool)
+        if(child.radioclient is not None):
+            child.setWindowTitle(str(child.radioclient))
+            self.mdiArea.addSubWindow(child)
+            self.mdiArea.currentSubWindow().showMaximized()
+        self.conns.append(child)
+        self.plots.append([])
+
+    def propertiesMenu(self, key, radio, uid):
+        r = str(radio).split(" ")
+        title = "{0}:{1}".format(r[3], r[5])
+
+        props = radio.properties([key])
+
+        pmin,pmax = get_minmax(props[key])
+
+        # Use display option mask of item to set up available plot
+        # types and default options.
+        disp = self.knobprops[uid][key].display
+        cplx = disp & gr.DISPOPTCPLX | disp & gr.DISPXY
+        strip = disp & gr.DISPOPTSTRIP
+        stem = disp & gr.DISPOPTSTEM
+        log = disp & gr.DISPOPTLOG
+        scatter = disp & gr.DISPOPTSCATTER
+
+        def newUpdaterProxy():
+            self.newUpdater(key, radio)
+
+        def newPlotterFProxy():
+            self.newPlotF(key, uid, title, pmin, pmax,
+                          log, strip, stem)
+
+        def newPlotterCProxy():
+            self.newPlotC(key, uid, title, pmin, pmax,
+                          log, strip, stem)
+
+        def newPlotterConstProxy():
+            self.newPlotConst(key, uid, title, pmin, pmax,
+                              scatter, strip)
+
+        def newPlotterPsdFProxy():
+            self.newPlotPsdF(key, uid, title)
+
+        def newPlotterPsdCProxy():
+            self.newPlotPsdC(key, uid, title)
+
+        def newPlotterRasterFProxy():
+            self.newPlotRasterF(key, uid, title, pmin, pmax)
+
+        def newPlotterRasterBProxy():
+            self.newPlotRasterB(key, uid, title, pmin, pmax)
+
+        menu = QtGui.QMenu(self)
+        menu.setTitle("Item Actions")
+        menu.setTearOffEnabled(False)
+
+        # object properties
+        menu.addAction("Properties", newUpdaterProxy)
+
+        # displays available
+        if(cplx == 0):
+            menu.addAction("Plot Time", newPlotterFProxy)
+            menu.addAction("Plot PSD", newPlotterPsdFProxy)
+            menu.addAction("Plot Raster (real)", newPlotterRasterFProxy)
+            #menu.addAction("Plot Raster (bits)", newPlotterRasterBProxy)
+        else:
+            menu.addAction("Plot Time", newPlotterCProxy)
+            menu.addAction("Plot PSD", newPlotterPsdCProxy)
+            menu.addAction("Plot Constellation", newPlotterConstProxy)
+
+        menu.popup(QtGui.QCursor.pos())
+
+    def newUpdater(self, key, radio):
+        updater = UpdaterWindow(key, radio, None)
+        updater.setWindowTitle("Updater: " + key)
+        updater.setModal(False)
+        updater.exec_()
+
+    def newSub(self, e):
+        tag = str(e.text(0))
+        tree = e.treeWidget().parent()
+        uid = tree.uid
+        knobprop = self.knobprops[uid][tag]
+
+        r = str(tree.radio).split(" ")
+        title = "{0}:{1}".format(r[3], r[5])
+        pmin,pmax = get_minmax(knobprop)
+
+        disp = knobprop.display
+        if(disp & gr.DISPTIME):
+            strip = disp & gr.DISPOPTSTRIP
+            stem = disp & gr.DISPOPTSTEM
+            log = disp & gr.DISPOPTLOG
+            if(disp & gr.DISPOPTCPLX == 0):
+                self.newPlotF(tag, uid, title, pmin, pmax,
+                              log, strip, stem)
+            else:
+                self.newPlotC(tag, uid, title, pmin, pmax,
+                              log, strip, stem)
+
+        elif(disp & gr.DISPXY):
+            scatter = disp & gr.DISPOPTSCATTER
+            self.newPlotConst(tag, uid, title, pmin, pmax, scatter)
+
+        elif(disp & gr.DISPPSD):
+            if(disp & gr.DISPOPTCPLX == 0):
+                self.newPlotPsdF(tag, uid, title)
+            else:
+                self.newPlotPsdC(tag, uid, title)
+
+    def startDrag(self, e):
+        drag = QtGui.QDrag(self)
+        mime_data = QtCore.QMimeData()
+
+        tag = str(e.text(0))
+        tree = e.treeWidget().parent()
+        knobprop = self.knobprops[tree.uid][tag]
+        disp = knobprop.display
+        iscomplex = (disp & gr.DISPOPTCPLX) or (disp & gr.DISPXY)
+
+        if(disp != gr.DISPNULL):
+            data = "PlotData:::{0}:::{1}".format(tag, iscomplex)
+        else:
+            data = "OtherData:::{0}:::{1}".format(tag, iscomplex)
+
+        mime_data.setText(data)
+        drag.setMimeData(mime_data)
+
+        drop = drag.start()
+
+    def createPlot(self, plot, uid, title):
+        plot.start()
+        self.plots[uid].append(plot)
+
+        self.mdiArea.addSubWindow(plot)
+        plot.setWindowTitle("{0}: {1}".format(title, plot.name()))
+        self.connect(plot.qwidget(),
+                     QtCore.SIGNAL('destroyed(QObject*)'),
+                     self.destroyPlot)
+
+        # when the plot is updated via drag-and-drop, we need to be
+        # notified of the new qwidget that's created so we can
+        # properly destroy it.
+        plot.plotupdated.connect(self.plotUpdated)
+
+        plot.show()
+
+    def plotUpdated(self, q):
+        # the plot has been updated with a new qwidget; make sure this
+        # gets dies to the destroyPlot function.
+        for i, plots in enumerate(self.plots):
+            for p in plots:
+                if(p == q):
+                    #plots.remove(p)
+                    #plots.append(q)
+                    self.connect(q.qwidget(),
+                                 QtCore.SIGNAL('destroyed(QObject*)'),
+                                 self.destroyPlot)
+                    break
+
+    def destroyPlot(self, obj):
+        for plots in self.plots:
+            for p in plots:
+                if p.qwidget() == obj:
+                    plots.remove(p)
+                    break
+
+    def newPlotConst(self, tag, uid, title="", pmin=None, pmax=None,
+                     scatter=False, stripchart=False):
+        plot = GrDataPlotterConst(tag, 32e6, pmin, pmax, stripchart)
+        plot.scatter(scatter)
+        self.createPlot(plot, uid, title)
+
+    def newPlotF(self, tag, uid, title="", pmin=None, pmax=None,
+                 logy=False, stripchart=False, stem=False):
+        plot = GrDataPlotterF(tag, 32e6, pmin, pmax, stripchart)
+        plot.semilogy(logy)
+        plot.stem(stem)
+        self.createPlot(plot, uid, title)
+
+    def newPlotC(self, tag, uid, title="", pmin=None, pmax=None,
+                 logy=False, stripchart=False, stem=False):
+        plot = GrDataPlotterC(tag, 32e6, pmin, pmax, stripchart)
+        plot.semilogy(logy)
+        plot.stem(stem)
+        self.createPlot(plot, uid, title)
+
+    def newPlotPsdF(self, tag, uid, title="", pmin=None, pmax=None):
+        plot = GrDataPlotterPsdF(tag, 32e6, pmin, pmax)
+        self.createPlot(plot, uid, title)
+
+    def newPlotPsdC(self, tag, uid, title="", pmin=None, pmax=None):
+        plot = GrDataPlotterPsdC(tag, 32e6, pmin, pmax)
+        self.createPlot(plot, uid, title)
+
+    def newPlotRasterF(self, tag, uid, title="", pmin=None, pmax=None):
+        plot = GrTimeRasterF(tag, 32e6, pmin, pmax)
+        self.createPlot(plot, uid, title)
+
+    def newPlotRasterB(self, tag, uid, title="", pmin=None, pmax=None):
+        plot = GrTimeRasterB(tag, 32e6, pmin, pmax)
+        self.createPlot(plot, uid, title)
+
+    def update(self, knobs, uid):
+        #sys.stderr.write("KNOB KEYS: {0}\n".format(knobs.keys()))
+        for plot in self.plots[uid]:
+            print("update plotuid:", uid)
+            data = []
+            for n in plot.knobnames:
+               print("update plotuid:", uid, "name:", n)
+                d = knobs[n].value
+                if(type(d) == GNURadio.complex):
+                    d = [d.re, d.im]
+
+                # If it's a byte stream, Python thinks it's a string.
+                # Unpack and convert to floats for plotting.
+                if(type(d) == str and n.find('probe2_b') == 0):
+                    d = struct.unpack(len(d)*'b', d)
+                    d = [float(di) for di in d]
+
+                data.append(d)
+            plot.update(data)
+            plot.stop()
+            plot.wait()
+            plot.start()
+
+    def setActiveSubWindow(self, window):
+        if window:
+            self.mdiArea.setActiveSubWindow(window)
+
+
+    def createActions(self):
+        self.newConAct = QtGui.QAction("&New Connection",
+                self, shortcut=QtGui.QKeySequence.New,
+                statusTip="Create a new file", triggered=self.newCon)
+
+        self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q",
+                statusTip="Exit the application",
+                triggered=QtGui.qApp.closeAllWindows)
+
+        self.closeAct = QtGui.QAction("Cl&ose", self, shortcut="Ctrl+F4",
+                statusTip="Close the active window",
+                triggered=self.mdiArea.closeActiveSubWindow)
+
+        self.closeAllAct = QtGui.QAction("Close &All", self,
+                statusTip="Close all the windows",
+                triggered=self.mdiArea.closeAllSubWindows)
+
+        self.urAct = QtGui.QAction("Update Rate", self, shortcut="F5",
+                statusTip="Change Update Rate",
+                triggered=self.updateRateShow)
+
+        qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_T);
+        self.tileAct = QtGui.QAction("&Tile", self,
+                statusTip="Tile the windows",
+                triggered=self.mdiArea.tileSubWindows,
+                shortcut=qks)
+
+        qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_C);
+        self.cascadeAct = QtGui.QAction("&Cascade", self,
+                statusTip="Cascade the windows", shortcut=qks,
+                triggered=self.mdiArea.cascadeSubWindows)
+
+        self.nextAct = QtGui.QAction("Ne&xt", self,
+                shortcut=QtGui.QKeySequence.NextChild,
+                statusTip="Move the focus to the next window",
+                triggered=self.mdiArea.activateNextSubWindow)
+
+        self.previousAct = QtGui.QAction("Pre&vious", self,
+                shortcut=QtGui.QKeySequence.PreviousChild,
+                statusTip="Move the focus to the previous window",
+                triggered=self.mdiArea.activatePreviousSubWindow)
+
+        self.separatorAct = QtGui.QAction(self)
+        self.separatorAct.setSeparator(True)
+
+        self.aboutAct = QtGui.QAction("&About", self,
+                statusTip="Show the application's About box",
+                triggered=self.about)
+
+        self.aboutQtAct = QtGui.QAction("About &Qt", self,
+                statusTip="Show the Qt library's About box",
+                triggered=QtGui.qApp.aboutQt)
+
+    def createMenus(self):
+        self.fileMenu = self.menuBar().addMenu("&File")
+        self.fileMenu.addAction(self.newConAct)
+        self.fileMenu.addAction(self.urAct)
+        self.fileMenu.addSeparator()
+        self.fileMenu.addAction(self.exitAct)
+
+        self.windowMenu = self.menuBar().addMenu("&Window")
+        self.updateWindowMenu()
+        self.windowMenu.aboutToShow.connect(self.updateWindowMenu)
+
+        self.menuBar().addSeparator()
+
+        self.helpMenu = self.menuBar().addMenu("&Help")
+        self.helpMenu.addAction(self.aboutAct)
+        self.helpMenu.addAction(self.aboutQtAct)
+
+    def updateRateShow(self):
+        askrate = RateDialog(self.updateRate, self);
+        if askrate.exec_():
+            ur = float(str(askrate.delay.text()));
+            self.setUpdateRate(ur);
+            return;
+        else:
+            return;
+
+    def createToolBars(self):
+        self.fileToolBar = self.addToolBar("File")
+        self.fileToolBar.addAction(self.newConAct)
+        self.fileToolBar.addAction(self.urAct)
+
+        self.fileToolBar = self.addToolBar("Window")
+        self.fileToolBar.addAction(self.tileAct)
+        self.fileToolBar.addAction(self.cascadeAct)
+
+    def createStatusBar(self):
+        self.statusBar().showMessage("Ready")
+
+
+    def activeMdiChild(self):
+        activeSubWindow = self.mdiArea.activeSubWindow()
+        if activeSubWindow:
+            return activeSubWindow.widget()
+        return None
+
+    def updateMenus(self):
+        hasMdiChild = (self.activeMdiChild() is not None)
+        self.closeAct.setEnabled(hasMdiChild)
+        self.closeAllAct.setEnabled(hasMdiChild)
+        self.tileAct.setEnabled(hasMdiChild)
+        self.cascadeAct.setEnabled(hasMdiChild)
+        self.nextAct.setEnabled(hasMdiChild)
+        self.previousAct.setEnabled(hasMdiChild)
+        self.separatorAct.setVisible(hasMdiChild)
+
+    def updateWindowMenu(self):
+        self.windowMenu.clear()
+        self.windowMenu.addAction(self.closeAct)
+        self.windowMenu.addAction(self.closeAllAct)
+        self.windowMenu.addSeparator()
+        self.windowMenu.addAction(self.tileAct)
+        self.windowMenu.addAction(self.cascadeAct)
+        self.windowMenu.addSeparator()
+        self.windowMenu.addAction(self.nextAct)
+        self.windowMenu.addAction(self.previousAct)
+        self.windowMenu.addAction(self.separatorAct)
+
+    def about(self):
+        about_info = \
+'''Copyright 2012 Free Software Foundation, Inc.\n
+This program is part of GNU Radio.\n
+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.\n
+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.\n
+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.'''
+
+        QtGui.QMessageBox.about(None, "gr-ctrlport-monitor", about_info)
+
+
+class ConInfoDialog(QtGui.QDialog):
+    def __init__(self, parent=None):
+        super(ConInfoDialog, self).__init__(parent)
+
+        self.gridLayout = QtGui.QGridLayout(self)
+
+
+        self.host = QtGui.QLineEdit(self);
+        self.port = QtGui.QLineEdit(self);
+        self.host.setText("localhost");
+        self.port.setText("43243");
+
+        self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | 
QtGui.QDialogButtonBox.Cancel)
+
+        self.gridLayout.addWidget(self.host);
+        self.gridLayout.addWidget(self.port);
+        self.gridLayout.addWidget(self.buttonBox);
+
+        self.buttonBox.accepted.connect(self.accept)
+        self.buttonBox.rejected.connect(self.reject)
+
+
+    def accept(self):
+        self.done(1);
+
+    def reject(self):
+        self.done(0);
+
+
+class UpdaterWindow(QtGui.QDialog):
+    def __init__(self, key, radio, parent):
+        QtGui.QDialog.__init__(self, parent)
+
+        self.key = key;
+        self.radio = radio
+
+        self.resize(300,200)
+        self.layout = QtGui.QVBoxLayout()
+
+        self.props = radio.properties([key])[key]
+        info = str(self.props)
+
+        self.infoLabel = QtGui.QLabel(info)
+        self.layout.addWidget(self.infoLabel)
+
+        # Test here to make sure that a 'set' function exists
+        try:
+            a = radio.set(radio.get([key]))
+            has_set = True
+        except Ice.UnknownException:
+            has_set = False
+
+        if(has_set is False):
+            self.cancelButton = QtGui.QPushButton("Ok")
+            self.cancelButton.connect(self.cancelButton, 
QtCore.SIGNAL('clicked()'), self.reject)
+
+            self.buttonlayout = QtGui.QHBoxLayout()
+            self.buttonlayout.addWidget(self.cancelButton)
+            self.layout.addLayout(self.buttonlayout)
+
+        else: # we have a set function
+            self.textInput = QtGui.QLineEdit()
+            self.layout.addWidget(self.textInput)
+
+            self.applyButton = QtGui.QPushButton("Apply")
+            self.setButton = QtGui.QPushButton("OK")
+            self.cancelButton = QtGui.QPushButton("Cancel")
+
+            rv = radio.get([key])
+            val = rv[key].value
+            if(type(val) == GNURadio.complex):
+                val = val.re + val.im*1j
+
+            self.textInput.setText(str(val))
+            self.sv = rv[key]
+
+            self.applyButton.connect(self.applyButton, 
QtCore.SIGNAL('clicked()'), self._apply)
+            self.setButton.connect(self.setButton, QtCore.SIGNAL('clicked()'), 
self._set)
+            self.cancelButton.connect(self.cancelButton, 
QtCore.SIGNAL('clicked()'), self.reject)
+
+            self.is_num = ((type(self.sv.value)==float) or 
(type(self.sv.value)==int))
+            if(self.is_num):
+                self.sliderlayout = QtGui.QHBoxLayout()
+
+                self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
+
+                
self.sliderlayout.addWidget(QtGui.QLabel(str(self.props.min.value)))
+                self.sliderlayout.addWidget(self.slider)
+                
self.sliderlayout.addWidget(QtGui.QLabel(str(self.props.max.value)))
+
+                self.steps = 10000
+                self.valspan = self.props.max.value - self.props.min.value
+
+                self.slider.setRange(0, 10000)
+                self._set_slider_value(self.sv.value)
+
+                self.connect(self.slider, QtCore.SIGNAL("sliderReleased()"), 
self._slide)
+
+                self.layout.addLayout(self.sliderlayout)
+            else:
+                self._set_slider_value = None
+
+            self.buttonlayout = QtGui.QHBoxLayout()
+            self.buttonlayout.addWidget(self.applyButton)
+            self.buttonlayout.addWidget(self.setButton)
+            self.buttonlayout.addWidget(self.cancelButton)
+            self.layout.addLayout(self.buttonlayout)
+
+        # set layout and go...
+        self.setLayout(self.layout)
+
+    def _set_slider_value(self, val):
+        
self.slider.setValue(self.steps*(val-self.props.min.value)/self.valspan)
+
+    def _slide(self):
+        val = (self.slider.value()*self.valspan + 
self.props.min.value)/float(self.steps)
+        self.textInput.setText(str(val))
+
+    def _apply(self):
+        if(type(self.sv.value) == str):
+            val = str(self.textInput.text())
+        elif(type(self.sv.value) == int):
+            val = int(round(float(self.textInput.text())))
+        elif(type(self.sv.value) == float):
+            val = float(self.textInput.text())
+        elif(type(self.sv.value) == GNURadio.complex):
+            t = str(self.textInput.text())
+            t = complex(t.strip("(").strip(")").replace(" ", ""))
+            val = GNURadio.complex()
+            val.re = t.real
+            val.im = t.imag
+        else:
+            sys.stderr.write("set type not supported! 
({0})\n".format(type(self.sv.value)))
+            return
+
+        self.sv.value = val
+        km = {}
+        km[self.key] = self.sv
+        self.radioclient.setKnobs(km)
+        if self._set_slider_value:
+            self._set_slider_value(self.sv.value)
+
+    def _set(self):
+        self._apply()
+        self.done(0)
+
+
+class MForm(QtGui.QWidget):
+    def update(self):
+        # TODO: revisit this try-except block, figure out what it's doing, and 
if we need to keep it. at very lease makes debugging dificult
+        if True: #try:
+            st = time.time();
+            knobs = self.radioclient.getKnobs([])
+            ft = time.time();
+            latency = ft-st;
+            self.parent.statusBar().showMessage("Current GNU Radio Control 
Port Query Latency: %f ms"%(latency*1000))
+
+#         except Exception, e:
+#             sys.stderr.write("ctrlport-monitor: radio.get threw exception 
({0}).\n".format(e))
+#             if(type(self.parent) is MAINWindow):
+#                 # Find window of connection
+#                 remove = []
+#                 for p in self.parent.mdiArea.subWindowList():
+#                     if self.parent.conns[self.uid] == p.widget():
+#                         remove.append(p)
+#
+#                 # Find any subplot windows of connection
+#                 for p in self.parent.mdiArea.subWindowList():
+#                     for plot in self.parent.plots[self.uid]:
+#                         if plot.qwidget() == p.widget():
+#                             remove.append(p)
+#
+#                 # Clean up local references to these
+#                 self.parent.conns.remove(self.parent.conns[self.uid])
+#                 self.parent.plots.remove(self.parent.plots[self.uid])
+#
+#                 # Remove subwindows for connection and plots
+#                 for r in remove:
+#                     self.parent.mdiArea.removeSubWindow(r)
+#
+#                 # Clean up self
+#                 self.close()
+#             else:
+#                 sys.exit(1)
+#             return
+
+        tableitems = knobs.keys()
+
+        #UPDATE TABLE:
+        #try:
+        self.table.updateItems(knobs, self.knobprops)
+        #except:
+        #    self.knobprops = self.radioclient.properties([])
+        #    print("knobsprops1:", len(self.knobprops))
+
+        #UPDATE PLOTS
+        self.parent.update(knobs, self.uid)
+
+
+    def __init__(self, radioclient, uid=0, updateRate=2000, parent=None, 
dialogprompt = False):
+
+        super(MForm, self).__init__()
+        self.radioclient = radioclient
+#         print("before radioclient.getHost()", radioclient.getHost(), 
radioclient.getPort(), "prompt", prompt)
+        if(dialogprompt or radioclient.getHost() is None or 
radioclient.getPort() is None):
+#             print("before ConInfoDialog")
+            askinfo = ConInfoDialog(self);
+            if askinfo.exec_():
+                host = str(askinfo.host.text());
+                port = str(askinfo.port.text());
+#                 print("before radioclient.newConnection host: %s port: 
%s"%(host,port))
+                newradio = self.radioclient.newConnection(host, port)
+                if newradio is None:
+                    print("Error making a %s connection to %s:%s from %s" % 
(radioclient.getName(), host, port, radioclient))
+                else:
+                    self.radioclient = newradio
+
+            else:
+                self.radioclient = Nonclient = None
+                return
+
+
+        self.uid = uid
+        self.parent = parent
+        self.horizontalLayout = QtGui.QVBoxLayout(self)
+        self.gridLayout = QtGui.QGridLayout()
+
+        self.knobprops = self.radioclient.properties([])
+        #print("props5:", self.knobprops)
+        self.parent.knobprops.append(self.knobprops)
+        self.resize(775,500)
+        self.timer = QtCore.QTimer()
+        self.constupdatediv = 0
+        self.tableupdatediv = 0
+        plotsize=250
+
+        # make table
+        self.table = GrDataPlotterValueTable(uid, self, 0, 0, 400, 200)
+        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, 
QtGui.QSizePolicy.Preferred)
+        self.table.treeWidget.setSizePolicy(sizePolicy)
+        
self.table.treeWidget.setEditTriggers(QtGui.QAbstractItemView.EditKeyPressed)
+        self.table.treeWidget.setSortingEnabled(True)
+        self.table.treeWidget.setDragEnabled(True)
+
+        # add things to layouts
+        self.horizontalLayout.addWidget(self.table.treeWidget)
+
+        # set up timer
+        self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update)
+        self.updateRate = updateRate;
+        self.timer.start(self.updateRate)
+
+        # set up context menu ..
+        self.table.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+        self.table.treeWidget.customContextMenuRequested.connect(self.openMenu)
+
+        # Set up double-click to launch default plotter
+        self.connect(self.table.treeWidget,
+                     QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'),
+                     self.parent.newSub);
+
+        # Allow drag/drop event from table item to plotter
+        self.connect(self.table.treeWidget,
+                     QtCore.SIGNAL('itemPressed(QTreeWidgetItem*, int)'),
+                     self.parent.startDrag)
+
+    def openMenu(self, pos):
+        index = self.table.treeWidget.selectedIndexes()
+        item = self.table.treeWidget.itemFromIndex(index[0])
+        itemname = str(item.text(0))
+        self.parent.propertiesMenu(itemname, self.radio, self.uid)
+
+
+def get_minmax(p):
+    pmin = p.min.value
+    pmax = p.max.value
+
+    # Find min/max or real or imag for GNURadio::complex
+    if(type(pmin) == GNURadio.complex):
+        pmin = min(pmin.re, pmin.im)
+    if(type(pmax) == GNURadio.complex):
+        pmax = max(pmax.re, pmax.im)
+
+    # If it's a byte stream, Python thinks it's a string.
+    if(type(pmin) == str):
+        pmin = struct.unpack('b', pmin)[0]
+    if(type(pmax) == str):
+        pmax = struct.unpack('b', pmax)[0]
+
+    if pmin == []:
+        pmin = None
+    else:
+        pmin = 1.1*float(pmin)
+    if pmax == []:
+        pmax = None
+    else:
+        pmax = 1.1*float(pmax)
+
+    return pmin, pmax
+
+class MyApp(object):
+    def __init__(self, args):
+        from gnuradio.ctrlport.GNURadioControlPortClient import 
GNURadioControlPortClient
+        GNURadioControlPortClient(args, 'thrift', self.run, 
QtGui.QApplication(sys.argv).exec_)
+
+    def run(self, client):
+        MAINWindow(client).show()
+
+MyApp(sys.argv)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]