From eeee294b5cab0b5646e29339f019d844f89a8377 Mon Sep 17 00:00:00 2001
From: Tom Rondeau <trondeau@vt.edu>
Date: Fri, 22 Feb 2013 16:12:57 -0500
Subject: ctrlport: wip: performance counter monitor for ControlPort.

---
 .../src/python/gnuradio/ctrlport/gr-perf-monitor   | 673 +++++++++++++++++++++
 1 file changed, 673 insertions(+)
 create mode 100755 gnuradio-core/src/python/gnuradio/ctrlport/gr-perf-monitor

(limited to 'gnuradio-core/src/python')

diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/gr-perf-monitor b/gnuradio-core/src/python/gnuradio/ctrlport/gr-perf-monitor
new file mode 100755
index 0000000000..2cafe92259
--- /dev/null
+++ b/gnuradio-core/src/python/gnuradio/ctrlport/gr-perf-monitor
@@ -0,0 +1,673 @@
+#!/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,Qwt5
+import PyQt4.QtGui as QtGui
+import sys, time, re, pprint
+import scipy
+
+import Ice
+from gnuradio.ctrlport.IceRadioClient import *
+from gnuradio.ctrlport.GrDataPlotter import *
+from gnuradio.ctrlport import GNURadio
+
+class MAINWindow(QtGui.QMainWindow):
+    def minimumSizeHint(self):
+        return QtGui.QSize(800,600)
+
+    def __init__(self, radio, port, interface):
+        
+        super(MAINWindow, self).__init__()
+        self.conns = []
+        self.plots = []
+        self.knobprops = []
+        self.interface = interface
+
+        self.mdiArea = QtGui.QMdiArea()
+        self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
+        #self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
+        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 Performance Monitor")
+        self.setUnifiedTitleAndToolBarOnMac(True)
+
+        self.newCon(radio, port)
+        icon = QtGui.QIcon(ctrlport.__path__[0] + "/icon.png" )
+        self.setWindowIcon(icon)
+
+    def newCon(self, radio=None, port=None):
+        child = MForm(radio, port, len(self.conns), self)
+        if(child.radio is not None):
+            child.setWindowTitle(str(child.radio))
+            self.mdiArea.addSubWindow(child)
+            child.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 = props[key].min.value
+        pmax = props[key].max.value
+        if pmin == []:
+            pmin = None
+        else:
+            pmin = 1.1*pmin
+        if pmax == []:
+            pmax = None
+        else:
+            pmax = 1.1*pmax
+
+        # 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)
+
+        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 = knobprop.min.value
+        pmax = knobprop.max.value
+        if pmin == []:
+            pmin = None
+        else:
+            pmin = 1.1*pmin
+        if pmax == []:
+            pmax = None
+        else:
+            pmax = 1.1*pmax
+
+        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 createPlot(self, plot, uid, title):
+        plot.start()
+        self.plots[uid].append(plot)
+
+        self.mdiArea.addSubWindow(plot.qwidget())
+        plot.qwidget().setWindowTitle("{0}: {1}".format(title, plot.name()))
+        self.connect(plot.qwidget(),
+                     QtCore.SIGNAL('destroyed(QObject*)'),
+                     self.destroyPlot)
+        plot.qwidget().show()
+
+
+    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):
+        plot = GrDataPlotterConst(tag, 32e6, pmin, pmax)
+        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]:
+            data = knobs[plot.name()].value
+            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.newAct = QtGui.QAction(QtGui.QIcon(':/images/new.png'), "&New Plot",
+        self.newPlotAct = QtGui.QAction("&New Plot",
+                self,
+                statusTip="Create a new file", triggered=self.newPlotF)
+
+        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)
+
+
+        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.newPlotAct)
+        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 createToolBars(self):
+        self.fileToolBar = self.addToolBar("File")
+        self.fileToolBar.addAction(self.newConAct)
+        self.fileToolBar.addAction(self.newPlotAct)
+
+        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)
+
+        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)
+ 
+        # 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())
+        else:
+            sys.stderr.write("set type not supported! ({0})\n".format(type(self.sv.value)))
+            sys.exit(-1)
+
+        self.sv.value = val
+        km = {}
+        km[self.key] = self.sv
+        self.radio.set(km)
+        self._set_slider_value(self.sv.value)
+
+    def _set(self):
+        self._apply()
+        self.done(0)
+
+
+class MForm(QtGui.QWidget):
+    def update(self):
+        try:
+            st = time.time()
+            knobs = self.radio.get(self.block_dict.keys())
+
+            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
+            
+        #UPDATE TABLE:
+        self.updateItems(knobs)
+
+        #UPDATE PLOTS
+        self.parent.update(knobs, self.uid)
+    
+    def updateItems(self, knobs):
+        for b in self.block_dict:
+            self.block_dict[b].setText("{0:.4f}".format(knobs[b].value))
+
+
+    def __init__(self, radio=None, port=None, uid=0, parent=None):
+
+        super(MForm, self).__init__()
+
+        if(radio == None or port == None):
+            askinfo = ConInfoDialog(self);
+            if askinfo.exec_():
+                host = str(askinfo.host.text());
+                port = str(askinfo.port.text());
+                radio = parent.interface.getRadio(host, port)
+            else:
+                self.radio = None
+                return
+        
+        self.uid = uid
+        self.parent = parent
+        self.layout = QtGui.QGridLayout(self)
+        self.layout.setSizeConstraint(QtGui.QLayout.SetFixedSize)
+
+        self.radio = radio
+        self.knobprops = self.radio.properties([])
+        self.parent.knobprops.append(self.knobprops)
+        self.resize(775,500)
+        self.timer = QtCore.QTimer()
+        self.constupdatediv = 0
+        self.tableupdatediv = 0
+        plotsize=250
+            
+
+        # Set up the graph of blocks
+        input_name = lambda x: x+"::avg input % full"
+        output_name = lambda x: x+"::avg output % full"
+        wtime_name = lambda x: x+"::avg work time"
+        nout_name = lambda x: x+"::avg noutput_items"
+        nprod_name = lambda x: x+"::avg nproduced"
+        
+        allblocks = []
+        knobs = self.radio.get([])
+        for k in knobs:
+            propname = k.split("::")
+            blockname = propname[0]
+            keyname = propname[1]
+            if(keyname == "edge list"):
+                edgelist = knobs[k].value
+            elif(blockname not in allblocks):
+                # only take gr_blocks (no hier_block2)
+                if(knobs.has_key(input_name(blockname))):
+                    allblocks.append(blockname)
+
+        edges = edgelist.split("\n")[0:-1]
+        producers = []
+        consumers = []
+        for e in edges:
+            _e = e.split("->")
+            producers.append(_e[0])
+            consumers.append(_e[1])
+
+        # Get producers and consumers as sets while ignoring the
+        # ports.
+        prods = set(map(lambda x: x.split(":")[0], producers))
+        cons  = set(map(lambda x: x.split(":")[0], consumers))
+
+        # Split out all blocks, sources, and sinks based on how they
+        # appear as consumers and/or producers.
+        blocks = prods.intersection(cons)
+        sources = prods.difference(blocks)
+        sinks = cons.difference(blocks)
+
+        nblocks = len(prods) + len(cons)
+        nrows = max(len(sources), len(sinks))
+        ncols = int(scipy.ceil(nblocks/float(nrows)))
+        allblocks = []
+        for n in xrange(nrows):
+            allblocks.append(["",])
+
+        def find_edge(src, edges, i):
+            for edge in edges:
+                if(re.match(src, edge)):
+                    s = edge.split("->")[0]
+                    b = edge.split("->")[1]
+                    allblocks[nsrcs][i] = s
+                    allblocks[nsrcs].append(b)
+                    return b
+            return None
+
+        for nsrcs, src in enumerate(sources):
+            b = src
+            i = 0
+            while b:
+                b = find_edge(b, edges, i)
+                i += 1
+
+        printer = pprint.PrettyPrinter()
+        printer.pprint(allblocks)
+
+        col_width = 120
+
+        self.block_dict = {}
+
+        for row, blockrow in enumerate(allblocks):
+            for col, block in enumerate(blockrow):
+                bgroup = QtGui.QGroupBox(block)
+                playout = QtGui.QFormLayout()
+                bgroup.setLayout(playout)
+                self.layout.addWidget(bgroup, row, col)
+
+                name = wtime_name(block.split(":")[0])
+                wtime = knobs[name].value
+                newtime = QtGui.QLineEdit()
+                newtime.setMinimumWidth(col_width)
+                newtime.setText("{0:.4f}".format(wtime))
+                self.block_dict[name] = newtime
+
+                name = nout_name(block.split(":")[0])
+                nout = knobs[name].value
+                newnout = QtGui.QLineEdit()
+                newnout.setText("{0:.4f}".format(nout))
+                newnout.setMinimumWidth(col_width)
+                self.block_dict[name] = newnout
+
+                name = nprod_name(block.split(":")[0])
+                nprod = knobs[name].value
+                newnprod = QtGui.QLineEdit()
+                newnprod.setMinimumWidth(col_width)
+                newnprod.setText("{0:.4f}".format(nprod))
+                self.block_dict[name] = newnprod
+
+                playout.addRow("Work time", newtime)
+                playout.addRow("noutput_items", newnout)
+                playout.addRow("nproduced", newnprod)
+                #print dir(playout)
+                #playout.setMinimumSize(col_width*2)
+
+        # set up timer   
+        self.timer = QtCore.QTimer()
+        self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update)
+        self.timer.start(1000)
+
+    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)
+        
+
+class MyClient(IceRadioClient):
+    def __init__(self):
+        IceRadioClient.__init__(self, MAINWindow)
+
+sys.exit(MyClient().main(sys.argv))
-- 
cgit v1.2.3