From eaedba3fa9969e6579afe986200b128285b3b9c9 Mon Sep 17 00:00:00 2001
From: Tom Rondeau <trondeau@vt.edu>
Date: Wed, 27 Feb 2013 17:22:17 -0500
Subject: ctrlport: Allows drag-and-drop from table list to a plotter to add a
 new curve to the plot.

---
 .../src/python/gnuradio/ctrlport/GrDataPlotter.py  | 672 ++++++++-------------
 .../python/gnuradio/ctrlport/gr-ctrlport-monitor   |  50 +-
 2 files changed, 298 insertions(+), 424 deletions(-)

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

diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/GrDataPlotter.py b/gnuradio-core/src/python/gnuradio/ctrlport/GrDataPlotter.py
index 091f0642c6..1bcf6bf807 100644
--- a/gnuradio-core/src/python/gnuradio/ctrlport/GrDataPlotter.py
+++ b/gnuradio-core/src/python/gnuradio/ctrlport/GrDataPlotter.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2012 Free Software Foundation, Inc.
+# Copyright 2012,2013 Free Software Foundation, Inc.
 #
 # This file is part of GNU Radio
 #
@@ -32,34 +32,68 @@ except ImportError:
     print "Error: Program requires PyQt4 and gr-qtgui."
     sys.exit(1)
 
-class GrDataPlotterC(gr.top_block):
-    def __init__(self, name, rate, pmin=None, pmax=None, stripchart=False):
+class GrDataPlotParent(gr.top_block, QtGui.QWidget):
+    # Setup signals
+    plotupdated = QtCore.pyqtSignal(QtGui.QWidget)
+
+    def __init__(self, name, rate, pmin=None, pmax=None):
         gr.top_block.__init__(self)
+        QtGui.QWidget.__init__(self, None)
 
-        self._stripchart = stripchart
         self._name = name
         self._npts = 500
-        samp_rate = 1.0
+        self._rate = rate
+        self.knobnames = [name,]
+
+        self.layout = QtGui.QVBoxLayout()
+        self.setLayout(self.layout)
+
+        self.setAcceptDrops(True)
 
-        self._last_data = self._npts*[0,]
-        self._data_len = 0
+    def _setup(self, nconnections):
+        self.stop()
+        self.wait()
 
-        self.src = gr.vector_source_c([])
-        self.thr = blocks.throttle(gr.sizeof_gr_complex, rate)
-        self.snk = qtgui.time_sink_c(self._npts, samp_rate,
-                                     self._name, 1)
-        self.snk.enable_autoscale(True)
+        if(self.layout.count() > 0):
+            # Remove and disconnect. Making sure no references to snk
+            # remain so that the plot gets deleted.
+            self.layout.removeWidget(self.py_window)
+            self.disconnect(self.thr, (self.snk, 0))
+            self.disconnect(self.src[0], self.thr)
+            for n in xrange(1, self._ncons):
+                self.disconnect(self.src[n], (self.snk,n))
 
-        self.connect(self.src, self.thr, (self.snk, 0))
+        self._ncons = nconnections
+        self._data_len = self._ncons*[0,]
 
-        self.snk.set_line_label(0, "Real")
-        self.snk.set_line_label(1, "Imag")
+        self.thr = blocks.throttle(self._datasize, self._rate)
+        self.snk = self.get_qtsink()
+
+        self.connect(self.thr, (self.snk, 0))
+
+        self._last_data = []
+        self.src = []
+        for n in xrange(self._ncons):
+            self.set_line_label(n, self.knobnames[n])
+
+            self._last_data.append(int(self._npts)*[0,])
+            self.src.append(self.get_vecsource())
+
+            if(n == 0):
+                self.connect(self.src[n], self.thr)
+            else:
+                self.connect(self.src[n], (self.snk,n))
 
         self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget)
 
+        self.layout.addWidget(self.py_window)
+
     def __del__(self):
         pass
 
+    def close(self):
+        self.snk.close()
+
     def qwidget(self):
         return self.py_window
 
@@ -69,162 +103,162 @@ class GrDataPlotterC(gr.top_block):
     def semilogy(self, en=True):
         self.snk.enable_semilogy(en)
 
-    def stem(self, en=True):
-        self.snk.enable_stem_plot(en)
+    def dragEnterEvent(self, e):
+        e.acceptProposedAction()
+
+    def dropEvent(self, e):
+        self.knobnames.append(str(e.mimeData().text()))
+
+        # create a new qwidget plot with the new data stream.
+        self._setup(len(self.knobnames))
+
+        # emit that this plot has been updated with a new qwidget.
+        self.plotupdated.emit(self)
+
+        e.acceptProposedAction()
+
+    def data_to_complex(self, data):
+        if(self._iscomplex):
+            data_r = data[0::2]
+            data_i = data[1::2]
+            data = [complex(r,i) for r,i in zip(data_r, data_i)]
+        return data
 
     def update(self, data):
         # Ask GUI if there has been a change in nsamps
-        npts = self.snk.nsamps()
+        npts = self.get_npts()
         if(self._npts != npts):
 
             # Adjust buffers to accomodate new settings
-            if(npts < self._npts):
-                if(self._data_len < npts):
-                    self._last_data = self._last_data[0:npts]
+            for n in xrange(self._ncons):
+                if(npts < self._npts):
+                    if(self._data_len[n] < npts):
+                        self._last_data[n] = self._last_data[n][0:npts]
+                    else:
+                        self._last_data[n] = self._last_data[n][self._data_len[n]-npts:self._data_len[n]]
+                        self._data_len[n] = npts
                 else:
-                    self._last_data = self._last_data[self._data_len-npts:self._data_len]
-                    self._data_len = npts
-            else:
-                self._last_data += (npts - self._npts)*[0,]
+                    self._last_data[n] += (npts - self._npts)*[0,]
             self._npts = npts
             self.snk.reset()
-        
+
         if(self._stripchart):
             # Update the plot data depending on type
-            if(type(data) == list):
-                data_r = data[0::2]
-                data_i = data[1::2]
-                data = [complex(r,i) for r,i in zip(data_r, data_i)]
-                if(len(data) > self._npts):
-                    self.src.set_data(data)
-                    self._last_data = data[-self._npts:]
-                else:
-                    newdata = self._last_data[-(self._npts-len(data)):]
-                    newdata += data
-                    self.src.set_data(newdata)
-                    self._last_data = newdata
-
-            else: # single value update
-                if(self._data_len < self._npts):
-                    self._last_data[self._data_len] = data
-                    self._data_len += 1
-                else:
-                    self._last_data = self._last_data[1:]
-                    self._last_data.append(data)
-                self.src.set_data(self._last_data)
+            for n in xrange(self._ncons):
+                if(type(data[n]) == list):
+                    data[n] = self.data_to_complex(data[n])
+                    if(len(data[n]) > self._npts):
+                        self.src[n].set_data(data[n])
+                        self._last_data[n] = data[n][-self._npts:]
+                    else:
+                        newdata = self._last_data[n][-(self._npts-len(data)):]
+                        newdata += data[n]
+                        self.src[n].set_data(newdata)
+                        self._last_data[n] = newdata
+
+                else: # single value update
+                    if(self._iscomplex):
+                        data[n] = complex(data[n][0], data[n][1])
+                    if(self._data_len[n] < self._npts):
+                        self._last_data[n][self._data_len[n]] = data[n]
+                        self._data_len[n] += 1
+                    else:
+                        self._last_data[n] = self._last_data[n][1:]
+                        self._last_data[n].append(data[n])
+                    self.src[n].set_data(self._last_data[n])
         else:
-            if(type(data) != list):
-                data = [data,]
-            self.src.set_data(data)
+            for n in xrange(self._ncons):
+                if(type(data[n]) != list):
+                    data[n] = [data[n],]
+                data[n] = self.data_to_complex(data[n])
+                self.src[n].set_data(data[n])
+            
+
 
-class GrDataPlotterF(gr.top_block):
+class GrDataPlotterC(GrDataPlotParent):
     def __init__(self, name, rate, pmin=None, pmax=None, stripchart=False):
-        gr.top_block.__init__(self)
+        GrDataPlotParent.__init__(self, name, rate, pmin, pmax)
 
         self._stripchart = stripchart
-        self._name = name
-        self._npts = 500
-        samp_rate = 1.0
+        self._datasize = gr.sizeof_gr_complex
+        self._iscomplex = True
 
-        self._last_data = self._npts*[0,]
-        self._data_len = 0
+        self._setup(1)
 
-        self.src = gr.vector_source_f([])
-        self.thr = blocks.throttle(gr.sizeof_float, rate)
-        self.snk = qtgui.time_sink_f(self._npts, samp_rate,
-                                     self._name, 1)
-        self.snk.enable_autoscale(True)
+    def stem(self, en=True):
+        self.snk.enable_stem_plot(en)
 
-        self.connect(self.src, self.thr, (self.snk, 0))
+    def get_qtsink(self):
+        snk = qtgui.time_sink_c(self._npts, 1.0,
+                                self._name, self._ncons)
+        snk.enable_autoscale(True)
+        return snk
 
-        self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget)
+    def get_vecsource(self):
+        return gr.vector_source_c([])
 
-    def __del__(self):
-        pass
+    def get_npts(self):
+        self._npts = self.snk.nsamps()
+        return self._npts
 
-    def qwidget(self):
-        return self.py_window
+    def set_line_label(self, n, name):
+        self.snk.set_line_label(2*n+0, "Re{" + self.knobnames[n] + "}")
+        self.snk.set_line_label(2*n+1, "Im{" + self.knobnames[n] + "}")
 
-    def name(self):
-        return self._name
 
-    def semilogy(self, en=True):
-        self.snk.enable_semilogy(en)
+class GrDataPlotterF(GrDataPlotParent):
+    def __init__(self, name, rate, pmin=None, pmax=None, stripchart=False):
+        GrDataPlotParent.__init__(self, name, rate, pmin, pmax)
 
-    def stem(self, en=True):
-        self.snk.enable_stem_plot(en)
+        self._stripchart = stripchart
+        self._datasize = gr.sizeof_float
+        self._iscomplex = False
 
-    def update(self, data):
-        # Ask GUI if there has been a change in nsamps
-        npts = self.snk.nsamps()
-        if(self._npts != npts):
+        self._setup(1)
 
-            # Adjust buffers to accomodate new settings
-            if(npts < self._npts):
-                if(self._data_len < npts):
-                    self._last_data = self._last_data[0:npts]
-                else:
-                    self._last_data = self._last_data[self._data_len-npts:self._data_len]
-                    self._data_len = npts
-            else:
-                self._last_data += (npts - self._npts)*[0,]
-            self._npts = npts
-            self.snk.reset()
+    def stem(self, en=True):
+        self.snk.enable_stem_plot(en)
 
-        if(self._stripchart):
-            # Update the plot data depending on type
-            if(type(data) == list):
-                if(len(data) > self._npts):
-                    self.src.set_data(data)
-                    self._last_data = data[-self._npts:]
-                else:
-                    newdata = self._last_data[-(self._npts-len(data)):]
-                    newdata += data
-                    self.src.set_data(newdata)
-                    self._last_data = newdata
-
-            else: # single value update
-                if(self._data_len < self._npts):
-                    self._last_data[self._data_len] = data
-                    self._data_len += 1
-                else:
-                    self._last_data = self._last_data[1:]
-                    self._last_data.append(data)
-                self.src.set_data(self._last_data)
-        else:
-            if(type(data) != list):
-                data = [data,]
-            self.src.set_data(data)
+    def get_qtsink(self):
+        snk = qtgui.time_sink_f(self._npts, 1.0,
+                                self._name, self._ncons)
+        snk.enable_autoscale(True)
+        return snk
+    
+    def get_vecsource(self):
+        return gr.vector_source_f([])
+
+    def get_npts(self):
+        self._npts = self.snk.nsamps()
+        return self._npts
+
+    def set_line_label(self, n, name):
+        self.snk.set_line_label(n, self.knobnames[n])
             
-class GrDataPlotterConst(gr.top_block):
+
+class GrDataPlotterConst(GrDataPlotParent):
     def __init__(self, name, rate, pmin=None, pmax=None):
-        gr.top_block.__init__(self)
+        GrDataPlotParent.__init__(self, name, rate, pmin, pmax)
 
-        self._name = name
-        self._npts = 500
-        samp_rate = 1.0
+        self._datasize = gr.sizeof_gr_complex
+        self._stripchart = False
+        self._iscomplex = True
 
-        self._last_data = self._npts*[0,]
-        self._data_len = 0
+        self._setup(1)
 
-        self.src = gr.vector_source_c([])
-        self.thr = blocks.throttle(gr.sizeof_gr_complex, rate)
-        self.snk = qtgui.const_sink_c(self._npts,
-                                      self._name, 1)
-        self.snk.enable_autoscale(True)
+    def get_qtsink(self):
+        snk = qtgui.const_sink_c(self._npts,
+                                 self._name,
+                                 self._ncons)
+        snk.enable_autoscale(True)
+        return snk
 
-        self.connect(self.src, self.thr, (self.snk, 0))
+    def get_vecsource(self):
+        return gr.vector_source_c([])
 
-        self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget)
-
-    def __del__(self):
-        pass
-
-    def qwidget(self):
-        return self.py_window
-
-    def name(self):
-        return self._name
+    def get_npts(self):
+        self._npts = self.snk.nsamps()
+        return self._npts
 
     def scatter(self, en=True):
         if(en):
@@ -232,327 +266,133 @@ class GrDataPlotterConst(gr.top_block):
         else:
             self.snk.set_line_style(0, 1)
 
-    def update(self, data):
-        # Ask GUI if there has been a change in nsamps
-        npts = self.snk.nsamps()
-        if(self._npts != npts):
-
-            # Adjust buffers to accomodate new settings
-            if(npts < self._npts):
-                if(self._data_len < npts):
-                    self._last_data = self._last_data[0:npts]
-                else:
-                    self._last_data = self._last_data[self._data_len-npts:self._data_len]
-                    self._data_len = npts
-            else:
-                self._last_data += (npts - self._npts)*[0,]
-            self._npts = npts
-            self.snk.reset()
-        
-        # Update the plot data depending on type
-        if(type(data) == list):
-            data_r = data[0::2]
-            data_i = data[1::2]
-            data = [complex(r,i) for r,i in zip(data_r, data_i)]
-            if(len(data) > self._npts):
-                self.src.set_data(data)
-                self._last_data = data[-self._npts:]
-            else:
-                newdata = self._last_data[-(self._npts-len(data)):]
-                newdata += data
-                self.src.set_data(newdata)
-                self._last_data = newdata
-
-        else: # single value update
-            if(self._data_len < self._npts):
-                self._last_data[self._data_len] = data
-                self._data_len += 1
-            else:
-                self._last_data = self._last_data[1:]
-                self._last_data.append(data)
-            self.src.set_data(self._last_data)
+    def set_line_label(self, n, name):
+        self.snk.set_line_label(n, self.knobnames[n])
 
 
-class GrDataPlotterPsdC(gr.top_block):
+class GrDataPlotterPsdC(GrDataPlotParent):
     def __init__(self, name, rate, pmin=None, pmax=None):
-        gr.top_block.__init__(self)
+        GrDataPlotParent.__init__(self, name, rate, pmin, pmax)
 
-        self._name = name
-        self._samp_rate = 1.0
-        self._fftsize = 2048
+        self._datasize = gr.sizeof_gr_complex
+        self._stripchart = True
+        self._iscomplex = True
+
+        self._npts = 2048
         self._wintype = gr.firdes.WIN_BLACKMAN_hARRIS
         self._fc = 0
-        
-        self._last_data = self._fftsize*[0,]
-        self._data_len = 0
-
-        self.src = gr.vector_source_c([])
-        self.thr = blocks.throttle(gr.sizeof_gr_complex, rate)
-        self.snk = qtgui.freq_sink_c(self._fftsize, self._wintype,
-                                     self._fc, self._samp_rate,
-                                     self._name, 1)
-        self.snk.enable_autoscale(True)
 
-        self.connect(self.src, self.thr, (self.snk, 0))
+        self._setup(1)
 
-        self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget)
+    def get_qtsink(self):
+        snk = qtgui.freq_sink_c(self._npts, self._wintype,
+                                self._fc, 1.0,
+                                self._name,
+                                self._ncons)
+        snk.enable_autoscale(True)
+        return snk
 
-    def __del__(self):
-        pass
+    def get_vecsource(self):
+        return gr.vector_source_c([])
 
-    def qwidget(self):
-        return self.py_window
+    def get_npts(self):
+        self._npts = self.snk.fft_size()
+        return self._npts
 
-    def name(self):
-        return self._name
+    def set_line_label(self, n, name):
+        self.snk.set_line_label(n, self.knobnames[n])
 
-    def update(self, data):
-        # Ask GUI if there has been a change in nsamps
-        fftsize = self.snk.fft_size()
-        if(self._fftsize != fftsize):
 
-            # Adjust buffers to accomodate new settings
-            if(fftsize < self._fftsize):
-                if(self._data_len < fftsize):
-                    self._last_data = self._last_data[0:fftsize]
-                else:
-                    self._last_data = self._last_data[self._data_len-fftsize:self._data_len]
-                    self._data_len = fftsize
-            else:
-                self._last_data += (fftsize - self._fftsize)*[0,]
-            self._fftsize = fftsize
-            self.snk.reset()
-        
-        # Update the plot data depending on type
-        if(type(data) == list):
-            data_r = data[0::2]
-            data_i = data[1::2]
-            data = [complex(r,i) for r,i in zip(data_r, data_i)]
-            if(len(data) > self._fftsize):
-                self.src.set_data(data)
-                self._last_data = data[-self._fftsize:]
-            else:
-                newdata = self._last_data[-(self._fftsize-len(data)):]
-                newdata += data
-                self.src.set_data(newdata)
-                self._last_data = newdata
-
-        else: # single value update
-            if(self._data_len < self._fftsize):
-                self._last_data[self._data_len] = data
-                self._data_len += 1
-            else:
-                self._last_data = self._last_data[1:]
-                self._last_data.append(data)
-            self.src.set_data(self._last_data)
-
-class GrDataPlotterPsdF(gr.top_block):
+class GrDataPlotterPsdF(GrDataPlotParent):
     def __init__(self, name, rate, pmin=None, pmax=None):
-        gr.top_block.__init__(self)
+        GrDataPlotParent.__init__(self, name, rate, pmin, pmax)
 
-        self._name = name
-        self._samp_rate = 1.0
-        self._fftsize = 2048
+        self._datasize = gr.sizeof_float
+        self._stripchart = True
+        self._iscomplex = False
+
+        self._npts = 2048
         self._wintype = gr.firdes.WIN_BLACKMAN_hARRIS
         self._fc = 0
-        
-        self._last_data = self._fftsize*[0,]
-        self._data_len = 0
-
-        self.src = gr.vector_source_f([])
-        self.thr = blocks.throttle(gr.sizeof_float, rate)
-        self.snk = qtgui.freq_sink_f(self._fftsize, self._wintype,
-                                     self._fc, self._samp_rate,
-                                     self._name, 1)
-        self.snk.enable_autoscale(True)
 
-        self.connect(self.src, self.thr, (self.snk, 0))
+        self._setup(1)
 
-        self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget)
+    def get_qtsink(self):
+        snk = qtgui.freq_sink_f(self._npts, self._wintype,
+                                self._fc, 1.0,
+                                self._name,
+                                self._ncons)
+        snk.enable_autoscale(True)
+        return snk
 
-    def __del__(self):
-        pass
-
-    def qwidget(self):
-        return self.py_window
-
-    def name(self):
-        return self._name
+    def get_vecsource(self):
+        return gr.vector_source_f([])
 
-    def update(self, data):
-        # Ask GUI if there has been a change in nsamps
-        fftsize = self.snk.fft_size()
-        if(self._fftsize != fftsize):
+    def get_npts(self):
+        self._npts = self.snk.fft_size()
+        return self._npts
 
-            # Adjust buffers to accomodate new settings
-            if(fftsize < self._fftsize):
-                if(self._data_len < fftsize):
-                    self._last_data = self._last_data[0:fftsize]
-                else:
-                    self._last_data = self._last_data[self._data_len-fftsize:self._data_len]
-                    self._data_len = fftsize
-            else:
-                self._last_data += (fftsize - self._fftsize)*[0,]
-            self._fftsize = fftsize
-            self.snk.reset()
-        
-        # Update the plot data depending on type
-        if(type(data) == list):
-            data_r = data[0::2]
-            data_i = data[1::2]
-            data = [complex(r,i) for r,i in zip(data_r, data_i)]
-            if(len(data) > self._fftsize):
-                self.src.set_data(data)
-                self._last_data = data[-self._fftsize:]
-            else:
-                newdata = self._last_data[-(self._fftsize-len(data)):]
-                newdata += data
-                self.src.set_data(newdata)
-                self._last_data = newdata
-
-        else: # single value update
-            if(self._data_len < self._fftsize):
-                self._last_data[self._data_len] = data
-                self._data_len += 1
-            else:
-                self._last_data = self._last_data[1:]
-                self._last_data.append(data)
-            self.src.set_data(self._last_data)
+    def set_line_label(self, n, name):
+        self.snk.set_line_label(n, self.knobnames[n])
 
 
-class GrTimeRasterF(gr.top_block):
+class GrTimeRasterF(GrDataPlotParent):
     def __init__(self, name, rate, pmin=None, pmax=None):
-        gr.top_block.__init__(self)
-
-        self._name = name
-        self._npts = 100
-        self._rows = 100
-        samp_rate = 1.0
-
-        self._last_data = self._npts*[0,]
-        self._data_len = 0
+        GrDataPlotParent.__init__(self, name, rate, pmin, pmax)
 
-        self.src = gr.vector_source_f([])
-        self.thr = blocks.throttle(gr.sizeof_float, rate)
-        self.snk = qtgui.time_raster_sink_f(samp_rate, self._npts, self._rows,
-                                            [], [], self._name, 1)
+        self._npts = 10
+        self._rows = 40
 
-        self.connect(self.src, self.thr, (self.snk, 0))
+        self._datasize = gr.sizeof_float
+        self._stripchart = False
+        self._iscomplex = False
 
-        self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget)
+        self._setup(1)
 
-    def __del__(self):
-        pass
+    def get_qtsink(self):
+        snk = qtgui.time_raster_sink_f(1.0, self._npts, self._rows,
+                                       [], [], self._name,
+                                       self._ncons)
+        return snk
 
-    def qwidget(self):
-        return self.py_window
+    def get_vecsource(self):
+        return gr.vector_source_f([])
 
-    def name(self):
-        return self._name
+    def get_npts(self):
+        self._npts = self.snk.num_cols()
+        return self._npts
 
-    def update(self, data):
-        # Ask GUI if there has been a change in nsamps
-        npts = int(self.snk.num_cols())
-        if(self._npts != npts):
-
-            # Adjust buffers to accomodate new settings
-            if(npts < self._npts):
-                if(self._data_len < npts):
-                    self._last_data = self._last_data[0:npts]
-                else:
-                    self._last_data = self._last_data[self._data_len-npts:self._data_len]
-                    self._data_len = npts
-            else:
-                self._last_data += (npts - self._npts)*[0,]
-            self._npts = npts
-            self.snk.reset()
-        
-        # Update the plot data depending on type
-        if(type(data) == list):
-            if(len(data) > self._npts):
-                self.src.set_data(data)
-                self._last_data = data[-self._npts:]
-            else:
-                newdata = self._last_data[-(self._npts-len(data)):]
-                newdata += data
-                self.src.set_data(newdata)
-                self._last_data = newdata
-
-        else: # single value update
-            if(self._data_len < self._npts):
-                self._last_data[self._data_len] = data
-                self._data_len += 1
-            else:
-                self._last_data = self._last_data[1:]
-                self._last_data.append(data)
-            self.src.set_data(self._last_data)
+    def set_line_label(self, n, name):
+        self.snk.set_line_label(n, self.knobnames[n])
 
-class GrTimeRasterB(gr.top_block):
+class GrTimeRasterB(GrDataPlotParent):
     def __init__(self, name, rate, pmin=None, pmax=None):
-        gr.top_block.__init__(self)
-
-        self._name = name
-        self._npts = 100
-        self._rows = 100
-        samp_rate = 1.0
+        GrDataPlotParent.__init__(self, name, rate, pmin, pmax)
 
-        self._last_data = self._npts*[0,]
-        self._data_len = 0
+        self._npts = 10
+        self._rows = 40
 
-        self.src = gr.vector_source_b([])
-        self.thr = blocks.throttle(gr.sizeof_char, rate)
-        self.snk = qtgui.time_raster_sink_b(samp_rate, self._npts, self._rows,
-                                            [], [], self._name, 1)
+        self._datasize = gr.sizeof_char
+        self._stripchart = False
+        self._iscomplex = False
 
-        self.connect(self.src, self.thr, (self.snk, 0))
+        self._setup(1)
 
-        self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget)
+    def get_qtsink(self):
+        snk = qtgui.time_raster_sink_b(1.0, self._npts, self._rows,
+                                       [], [], self._name,
+                                       self._ncons)
+        return snk
 
-    def __del__(self):
-        pass
-
-    def qwidget(self):
-        return self.py_window
-
-    def name(self):
-        return self._name
+    def get_vecsource(self):
+        return gr.vector_source_b([])
 
-    def update(self, data):
-        # Ask GUI if there has been a change in nsamps
-        npts = self.snk.num_cols()
-        if(self._npts != npts):
+    def get_npts(self):
+        self._npts = self.snk.num_cols()
+        return self._npts
 
-            # Adjust buffers to accomodate new settings
-            if(npts < self._npts):
-                if(self._data_len < npts):
-                    self._last_data = self._last_data[0:npts]
-                else:
-                    self._last_data = self._last_data[self._data_len-npts:self._data_len]
-                    self._data_len = npts
-            else:
-                self._last_data += (npts - self._npts)*[0,]
-            self._npts = npts
-            self.snk.reset()
-        
-        # Update the plot data depending on type
-        if(type(data) == list):
-            if(len(data) > self._npts):
-                self.src.set_data(data)
-                self._last_data = data[-self._npts:]
-            else:
-                newdata = self._last_data[-(self._npts-len(data)):]
-                newdata += data
-                self.src.set_data(newdata)
-                self._last_data = newdata
-
-        else: # single value update
-            if(self._data_len < self._npts):
-                self._last_data[self._data_len] = data
-                self._data_len += 1
-            else:
-                self._last_data = self._last_data[1:]
-                self._last_data.append(data)
-            self.src.set_data(self._last_data)
+    def set_line_label(self, n, name):
+        self.snk.set_line_label(n, self.knobnames[n])
 
 
 class GrDataPlotterValueTable:
diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/gr-ctrlport-monitor b/gnuradio-core/src/python/gnuradio/ctrlport/gr-ctrlport-monitor
index 3f717da5ca..d5d349a38a 100755
--- a/gnuradio-core/src/python/gnuradio/ctrlport/gr-ctrlport-monitor
+++ b/gnuradio-core/src/python/gnuradio/ctrlport/gr-ctrlport-monitor
@@ -55,7 +55,7 @@ class MAINWindow(QtGui.QMainWindow):
     def __init__(self, radio, port, interface):
         
         super(MAINWindow, self).__init__()
-        self.updateRate = 2000;
+        self.updateRate = 1000;
         self.conns = []
         self.plots = []
         self.knobprops = []
@@ -83,7 +83,7 @@ class MAINWindow(QtGui.QMainWindow):
         icon = QtGui.QIcon(ctrlport.__path__[0] + "/icon.png" )
         self.setWindowIcon(icon)
 
-        # Locally turn off ControlPort export from GR.  This prevents
+        # 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).
@@ -222,17 +222,44 @@ class MAINWindow(QtGui.QMainWindow):
             else:
                 self.newPlotPsdC(tag, uid, title)
 
+    def startDrag(self, e):
+        drag = QtGui.QDrag(self)
+        mime_data = QtCore.QMimeData()
+
+        mime_data.setText(e.text(0))
+        drag.setMimeData(mime_data)
+
+        drop = drag.start()
+
     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.mdiArea.addSubWindow(plot)
+        plot.setWindowTitle("{0}: {1}".format(title, plot.name()))
         self.connect(plot.qwidget(),
                      QtCore.SIGNAL('destroyed(QObject*)'),
                      self.destroyPlot)
-        plot.qwidget().show()
 
+        # 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:
@@ -280,7 +307,9 @@ class MAINWindow(QtGui.QMainWindow):
     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
+            data = []
+            for n in plot.knobnames:
+                data.append(knobs[n].value)
             plot.update(data)
             plot.stop()
             plot.wait()
@@ -636,7 +665,7 @@ class MForm(QtGui.QWidget):
         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)
@@ -644,7 +673,7 @@ class MForm(QtGui.QWidget):
         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)
 
@@ -662,6 +691,11 @@ class MForm(QtGui.QWidget):
                      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])
-- 
cgit v1.2.3