commit-gnuradio
[Top][All Lists]
Advanced

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

[Commit-gnuradio] [gnuradio] 05/50: controlport: working gr-perf-monitor


From: git
Subject: [Commit-gnuradio] [gnuradio] 05/50: controlport: working gr-perf-monitorx application
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 8824c9a6a5c40b8573eaa157ae27d18dbd587a86
Author: Tom Rondeau <address@hidden>
Date:   Thu Feb 12 15:19:19 2015 -0500

    controlport: working gr-perf-monitorx application
---
 .../include/gnuradio/rpcserver_booter_thrift.h     |  52 ++
 .../include/gnuradio/thrift_application_base.h     | 191 +++++
 .../include/gnuradio/thrift_server_template.h      |  95 +++
 .../lib/controlport/thrift/gnuradio.thrift         | 106 +++
 .../controlport/thrift/rpcpmtconverters_thrift.cc  | 173 +++++
 .../controlport/thrift/rpcserver_booter_thrift.cc  |  58 ++
 .../lib/controlport/thrift/rpcserver_thrift.cc     | 202 +++++
 .../controlport/thrift/thrift_application_base.cc  |  45 ++
 .../python/gnuradio/ctrlport/ThriftRadioClient.py  |  44 ++
 .../python/gnuradio/ctrlport/gr-perf-monitorx      | 861 +++++++++++++++++++++
 10 files changed, 1827 insertions(+)

diff --git a/gnuradio-runtime/include/gnuradio/rpcserver_booter_thrift.h 
b/gnuradio-runtime/include/gnuradio/rpcserver_booter_thrift.h
new file mode 100644
index 0000000..836d431
--- /dev/null
+++ b/gnuradio-runtime/include/gnuradio/rpcserver_booter_thrift.h
@@ -0,0 +1,52 @@
+/* -*- c++ -*- */
+/*
+ * 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 GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef RPCSERVER_BOOTER_THRIFT_H
+#define RPCSERVER_BOOTER_THRIFT_H
+
+#include <gnuradio/rpcserver_booter_base.h>
+#include <gnuradio/thrift_server_template.h>
+#include <ControlPort.h>
+
+class rpcserver_base;
+class rpcserver_thrift;
+
+class rpcserver_booter_thrift
+  : public virtual rpcserver_booter_base,
+    public virtual thrift_server_template<rpcserver_base,
+                                          rpcserver_thrift,
+                                          rpcserver_booter_thrift,
+                                          
boost::shared_ptr<GNURadio::ControlPortIf> >
+{
+ public:
+  rpcserver_booter_thrift();
+  ~rpcserver_booter_thrift();
+
+  rpcserver_base* i();
+  const std::string & type() {return d_type;}
+  const std::vector<std::string> endpoints();
+
+ private:
+  std::string d_type;
+};
+
+#endif /* RPCSERVER_BOOTER_THRIFT_H */
diff --git a/gnuradio-runtime/include/gnuradio/thrift_application_base.h 
b/gnuradio-runtime/include/gnuradio/thrift_application_base.h
new file mode 100644
index 0000000..1bf8416
--- /dev/null
+++ b/gnuradio-runtime/include/gnuradio/thrift_application_base.h
@@ -0,0 +1,191 @@
+/* -*- c++ -*- */
+/*
+ * 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 GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THRIFT_APPLICATION_BASE_H
+#define THRIFT_APPLICATION_BASE_H
+
+#ifdef HAVE_WINDOWS_H
+#include <winsock2.h>
+#include <sys/time.h>
+#endif
+
+#include <gnuradio/api.h>
+#include <gnuradio/prefs.h>
+#include <thrift/Thrift.h>
+#include <boost/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <stdio.h>
+#include <iostream>
+#include <set>
+#include <string>
+#include <stdio.h>
+
+#include <thrift/Thrift.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/server/TSimpleServer.h>
+#include <gnuradio/rpcserver_thrift.h>
+#include <ControlPort.h>
+
+using namespace apache;
+
+namespace {
+  static const unsigned int THRIFTAPPLICATION_ACTIVATION_TIMEOUT_MS(600);
+};
+
+class GR_RUNTIME_API thrift_application_common
+{
+ public:
+  template<typename TserverBase, typename TserverClass> friend class 
thrift_application_base;
+  static boost::shared_ptr<thrift_application_common> Instance();
+  ~thrift_application_common() {;}
+  static int d_reacquire_attributes;
+
+ protected:
+  static bool d_main_called;
+  static bool d_have_thrift_config;
+  static std::string d_endpointStr;
+  static boost::shared_ptr<boost::thread> d_thread;
+
+  thrift::server::TSimpleServer* d_thriftserver;
+
+  thrift_application_common() {;}
+  int run(int, char*[]);
+};
+
+template<typename TserverBase, typename TserverClass>
+class thrift_application_base
+{
+public:
+  boost::shared_ptr<thrift_application_common> d_application;
+  thrift_application_base(TserverClass* _this);
+  ~thrift_application_base() {;}
+
+  static TserverBase* i();
+  static const std::vector<std::string> endpoints();
+
+protected:
+  bool have_thrift_config() { return d_application->d_have_thrift_config; }
+  void set_endpoint(const std::string& endpoint) { 
d_application->d_endpointStr = endpoint;}
+
+  //this one is the key... overwrite in templated/inherited variants
+  virtual TserverBase* i_impl() = 0;
+
+  static TserverClass* d_this;
+
+  thrift::server::TSimpleServer* d_thriftserver;
+
+private:
+  bool d_is_running;
+
+  void start_thrift();
+
+  bool application_started();
+
+  int run(int, char*[]);
+
+  static void kickoff();
+};
+
+template<typename TserverBase, typename TserverClass>
+TserverClass* thrift_application_base<TserverBase, TserverClass>::d_this(0);
+
+
+template<typename TserverBase, typename TserverClass>
+thrift_application_base<TserverBase, 
TserverClass>::thrift_application_base(TserverClass* _this)
+{
+  //std::cerr << "thrift_application_base: ctor" << std::endl;
+  d_is_running = false;
+  d_this = _this;
+
+  //d_application->d_thriftserver = d_this->d_thriftserver;
+}
+
+template<typename TserverBase, typename TserverClass>
+void thrift_application_base<TserverBase, TserverClass>::start_thrift()
+{
+  //char* argv[2];
+  //argv[0] = (char*)"";
+  //
+  //std::string conffile = gr::prefs::singleton()->get_string("ControlPort", 
"config", "");
+  //
+  //if(conffile.size() > 0) {
+  //  std::stringstream thriftconf;
+  //  d_have_thrift_config = true;
+  //  d_main_called = true;
+  //  thriftconf << conffile;
+  //  main(0, argv, thriftconf.str().c_str());
+  //}
+  //else {
+  //  d_have_thrift_config = false;
+  //  d_main_called = true;
+  //  main(0, argv);
+  //}
+
+  //std::cerr << "thrift_application_base: start_thrift" << std::endl;
+  d_thriftserver->serve();
+  d_is_running = true;
+}
+
+template<typename TserverBase, typename TserverClass>
+void thrift_application_base<TserverBase, TserverClass>::kickoff()
+{
+  //std::cerr << "thrift_application_base: kickoff" << std::endl;
+
+  static bool run_once = false;
+
+  if(!run_once) {
+    thrift_application_common::d_thread = boost::shared_ptr<boost::thread>
+      (new boost::thread(boost::bind(&thrift_application_base::start_thrift, 
d_this)));
+
+    run_once = true;
+  }
+
+  return;
+}
+
+
+template<typename TserverBase, typename TserverClass>
+const std::vector<std::string> thrift_application_base<TserverBase, 
TserverClass>::endpoints()
+{
+  std::vector<std::string> ep;
+  ep.push_back(d_this->d_application->d_endpointStr);
+  return ep;
+}
+
+
+template<typename TserverBase, typename TserverClass>
+TserverBase* thrift_application_base<TserverBase, TserverClass>::i()
+{
+  if(!d_this->application_started()) {
+    kickoff();
+  }
+  return d_this->i_impl();
+}
+
+template<typename TserverBase, typename TImplClass>
+bool thrift_application_base<TserverBase, TImplClass>::application_started()
+{
+  return d_is_running;
+}
+
+#endif
diff --git a/gnuradio-runtime/include/gnuradio/thrift_server_template.h 
b/gnuradio-runtime/include/gnuradio/thrift_server_template.h
new file mode 100644
index 0000000..261893d
--- /dev/null
+++ b/gnuradio-runtime/include/gnuradio/thrift_server_template.h
@@ -0,0 +1,95 @@
+/* -*- c++ -*- */
+/*
+ * 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 GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THRIFT_SERVER_TEMPLATE_H
+#define THRIFT_SERVER_TEMPLATE_H
+
+#include <gnuradio/rpcserver_thrift.h>
+#include <gnuradio/thrift_application_base.h>
+#include <iostream>
+
+#include <thrift/Thrift.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <ControlPort.h>
+
+using namespace apache;
+
+template<typename TserverBase, typename TserverClass, typename TImplClass, 
typename TThriftClass>
+class thrift_server_template : public thrift_application_base<TserverBase, 
TImplClass>
+{
+public:
+  thrift_server_template(TImplClass* _this,
+                         const std::string& contolPortName,
+                         const std::string& endpointName);
+  ~thrift_server_template();
+
+protected:
+  TserverBase* i_impl();
+  friend class thrift_application_base<TserverBase, TImplClass>;
+
+  TserverBase* d_server;
+  const std::string d_contolPortName, d_endpointName;
+};
+
+template<typename TserverBase, typename TserverClass, typename TImplClass, 
typename TThriftClass>
+thrift_server_template<TserverBase, TserverClass, TImplClass, 
TThriftClass>::thrift_server_template
+(TImplClass* _this, const std::string& controlPortName, const std::string& 
endpointName)
+  : thrift_application_base<TserverBase, TImplClass>(_this),
+    d_contolPortName(controlPortName),
+    d_endpointName(endpointName)
+{
+  //std::cerr << "thrift_server_template: ctor" << std::endl;
+
+  boost::shared_ptr<TserverClass> handler(new TserverClass());
+
+  boost::shared_ptr<thrift::TProcessor>
+    processor(new GNURadio::ControlPortProcessor(handler));
+
+  boost::shared_ptr<thrift::transport::TServerTransport>
+    serverTransport(new thrift::transport::TServerSocket(9090));
+
+  boost::shared_ptr<thrift::transport::TTransportFactory>
+    transportFactory(new thrift::transport::TBufferedTransportFactory());
+
+  boost::shared_ptr<thrift::protocol::TProtocolFactory>
+    protocolFactory(new thrift::protocol::TBinaryProtocolFactory());
+
+  thrift_application_base<TserverBase, TImplClass>::d_thriftserver =
+    new thrift::server::TSimpleServer(processor, serverTransport, 
transportFactory, protocolFactory);
+
+  d_server = (TserverBase*)handler.get();
+}
+
+template<typename TserverBase, typename TserverClass, typename TImplClass, 
typename TThriftClass>
+thrift_server_template<TserverBase, TserverClass,TImplClass, 
TThriftClass>::~thrift_server_template()
+{
+}
+
+template<typename TserverBase, typename TserverClass, typename TImplClass, 
typename TThriftClass>
+TserverBase* thrift_server_template<TserverBase, TserverClass, TImplClass, 
TThriftClass>::i_impl()
+{
+  //std::cerr << "thrift_server_template: i_impl" << std::endl;
+  return d_server;
+}
+
+#endif /* THRIFT_SERVER_TEMPLATE_H */
diff --git a/gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift 
b/gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift
new file mode 100644
index 0000000..141dc3a
--- /dev/null
+++ b/gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014 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.
+ */
+
+namespace cpp GNURadio
+namespace py GNURadio
+
+struct complex {
+  1: double re;
+  2: double im;
+}
+
+typedef list<bool>    VectorB
+typedef list<byte>    VectorC
+typedef list<i16>     VectorT
+typedef list<i32>     VectorI
+typedef list<i64>     VectorL
+typedef list<double>  VectorF
+typedef list<double>  VectorD
+typedef list<string>  VectorS
+typedef list<complex> VectorZ
+
+enum BaseTypes { BOOL, BYTE, SHORT, INT, LONG, DOUBLE, STRING, COMPLEX }
+
+union KnobBase {
+  1: bool a_bool;
+  2: byte a_byte;
+  3: i16 a_short;
+  4: i32 a_int;
+  5: i64 a_long;
+  6: double a_double;
+  7: string a_string;
+  8: complex a_complex;
+  9: VectorF a_f32vector;
+  10: VectorD a_f64vector;
+  11: VectorL a_s64vector;
+  12: VectorI a_s32vector;
+  13: VectorT a_s16vector;
+  14: VectorC a_s8vector;
+  15: VectorZ a_c32vector;
+}
+
+struct Knob {
+  1: BaseTypes type;
+  2: KnobBase value;
+}
+
+enum KnobType { KNOBBOOL, KNOBCHAR, KNOBINT, KNOBDOUBLE, KNOBSTRING,
+                KNOBLONG, KNOBVECBOOL, KNOBVECCHAR, KNOBVECINT,
+                KNOBVECDOUBLE, KNOBVECSTRING, KNOBVECLONG, KNOBSHORT}
+
+const i32 DISPNULL = 0x0000
+const i32 DISPTIME = 0x0001
+const i32 DISPXY   = 0x0002
+const i32 DISPPSD  = 0x0004
+const i32 DISPSPEC = 0x0008
+const i32 DISPRAST = 0x0010
+const i32 DISPOPTCPLX    = 0x0100
+const i32 DISPOPTLOG     = 0x0200
+const i32 DISPOPTSTEM    = 0x0400
+const i32 DISPOPTSTRIP   = 0x0800
+const i32 DISPOPTSCATTER = 0x1000
+
+struct KnobProp {
+  1: KnobType    type,
+  2: string      units,
+  3: string      description,
+  4: i32         display,
+  5: Knob        min,
+  6: Knob        max,
+  7: Knob        defaultvalue
+}
+
+typedef list<string> KnobIDList
+typedef map<string, Knob> KnobMap
+typedef map<string, KnobProp> KnobPropMap
+typedef map<string, string> WaveformArgMap
+
+service StreamReceiver {
+        void push(1:VectorC data);
+}
+
+service ControlPort {
+        void setKnobs(1:KnobMap knobs);
+        KnobMap getKnobs(1:KnobIDList knobs);
+        KnobMap getRe(1:KnobIDList knobs);
+        KnobPropMap properties(1:KnobIDList knobs);
+        void shutdown();
+}
diff --git a/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc 
b/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc
new file mode 100644
index 0000000..213dca0
--- /dev/null
+++ b/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014,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 GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gnuradio/rpcpmtconverters_thrift.h>
+#include "gnuradio_types.h"
+#include <gnuradio/gr_complex.h>
+#include <iostream>
+
+GNURadio::Knob
+rpcpmtconverter::from_pmt(const pmt::pmt_t& knob)
+{
+  if(pmt::is_real(knob)) {
+
+    GNURadio::Knob result;
+    result.type = GNURadio::BaseTypes::DOUBLE;
+    result.value.__set_a_double(pmt::to_double(knob));
+    return result;
+  }
+  else if(pmt::is_symbol(knob)) {
+    std::string value = pmt::symbol_to_string(knob);
+    GNURadio::Knob result;
+    result.type = GNURadio::BaseTypes::STRING;
+    result.value.__set_a_string(value);
+    return result;
+  }
+  else if(pmt::is_integer(knob)) {
+    GNURadio::Knob result;
+    result.type = GNURadio::BaseTypes::LONG;
+    result.value.__set_a_long(pmt::to_long(knob));
+    return result;
+  }
+  else if(pmt::is_bool(knob)) {
+    GNURadio::Knob result;
+    result.type = GNURadio::BaseTypes::BOOL;
+    result.value.__set_a_bool(pmt::to_bool(knob));
+    return result;
+  }
+  else if(pmt::is_uint64(knob)) {
+    GNURadio::Knob result;
+    result.type = GNURadio::BaseTypes::LONG;
+    result.value.__set_a_long(pmt::to_uint64(knob));
+    return result;
+  }
+  else if(pmt::is_complex(knob)) {
+    GNURadio::Knob result;
+    result.type = GNURadio::BaseTypes::COMPLEX;
+    std::complex<double> tmp = pmt::to_complex(knob);
+    GNURadio::complex cpx;
+    cpx.re = tmp.real();
+    cpx.im = tmp.imag();
+    result.value.__set_a_complex(cpx);
+    return result;
+  }
+  else if(pmt::is_f32vector(knob)) {
+    GNURadio::Knob result;
+    size_t size(pmt::length(knob));
+    const float* start((const float*)pmt::f32vector_elements(knob,size));
+    result.value.__set_a_f32vector(std::vector<double>(start,start+size));
+    return result;
+  }
+  else if(pmt::is_f64vector(knob)) {
+    GNURadio::Knob result;
+    size_t size(pmt::length(knob));
+    const double* start((const double*)pmt::f64vector_elements(knob,size));
+    result.value.__set_a_f64vector(std::vector<double>(start,start+size));
+    return result;
+  }
+  else if(pmt::is_s64vector(knob)) {
+    GNURadio::Knob result;
+    size_t size(pmt::length(knob));
+    const int64_t* start((const int64_t*)pmt::s64vector_elements(knob,size));
+    result.value.__set_a_s64vector(std::vector<int64_t>(start,start+size));
+    return result;
+  }
+  else if(pmt::is_s32vector(knob)) {
+    GNURadio::Knob result;
+    size_t size(pmt::length(knob));
+    const int32_t* start((const int32_t*)pmt::s32vector_elements(knob,size));
+    result.value.__set_a_s32vector(std::vector<int32_t>(start,start+size));
+    return result;
+  }
+  else if(pmt::is_s16vector(knob)) {
+    GNURadio::Knob result;
+    size_t size(pmt::length(knob));
+    const int16_t* start((const int16_t*)pmt::s16vector_elements(knob,size));
+    result.value.__set_a_s16vector(std::vector<int16_t>(start,start+size));
+    return result;
+  }
+  else if(pmt::is_s8vector(knob)) {
+    GNURadio::Knob result;
+    size_t size(pmt::length(knob));
+    const int8_t* start((const int8_t*)pmt::s8vector_elements(knob,size));
+    result.value.__set_a_s8vector(std::vector<int8_t>(start,start+size));
+    return result;
+  }
+  else if(pmt::is_c32vector(knob)) {
+    std::vector< GNURadio::complex > z;
+
+    GNURadio::Knob result;
+    size_t size(pmt::length(knob));
+    const gr_complex* start((const 
gr_complex*)pmt::c32vector_elements(knob,size));
+    for(size_t s = 0; s < size; s++) {
+      GNURadio::complex z0;
+      gr_complex z1 = gr_complex(*(start+s));
+      z0.__set_re(z1.real());
+      z0.__set_im(z1.imag());
+      z.push_back(z0);
+    }
+    result.value.__set_a_c32vector(z);
+    return result;
+  }
+  else {
+    std::cerr << "Error: Don't know how to handle Knob Type (from): " << knob 
<< std::endl;
+    assert(0);
+  }
+  return GNURadio::Knob();
+}
+
+pmt::pmt_t
+rpcpmtconverter::to_pmt(const GNURadio::Knob& knob)
+{
+
+  if(knob.type == GNURadio::BaseTypes::BYTE) {
+    return pmt::mp(knob.value.a_byte);
+  }
+  else if(knob.type == GNURadio::BaseTypes::SHORT) {
+    return pmt::mp(knob.value.a_short);
+  }
+  else if(knob.type == GNURadio::BaseTypes::INT) {
+    return pmt::mp(knob.value.a_int);
+  }
+  else if(knob.type == GNURadio::BaseTypes::LONG) {
+    return pmt::mp(knob.value.a_long);
+  }
+  else if(knob.type == GNURadio::BaseTypes::DOUBLE) {
+    return pmt::mp(knob.value.a_double);
+  }
+  else if(knob.type == GNURadio::BaseTypes::STRING) {
+    return pmt::string_to_symbol(knob.value.a_string);
+  }
+  else if(knob.type == GNURadio::BaseTypes::BOOL) {
+    if (knob.value.a_bool)
+      return pmt::PMT_T;
+    else
+      return pmt::PMT_F;
+  }
+  else if(knob.type == GNURadio::BaseTypes::COMPLEX) {
+    gr_complexd cpx(knob.value.a_complex.re, knob.value.a_complex.im);
+    return pmt::from_complex(cpx);
+  }
+  else {
+    std::cerr << "Error: Don't know how to handle Knob Type: " << knob.type << 
std::endl; assert(0);
+  }
+  return pmt::pmt_t();
+}
diff --git a/gnuradio-runtime/lib/controlport/thrift/rpcserver_booter_thrift.cc 
b/gnuradio-runtime/lib/controlport/thrift/rpcserver_booter_thrift.cc
new file mode 100644
index 0000000..90fce2b
--- /dev/null
+++ b/gnuradio-runtime/lib/controlport/thrift/rpcserver_booter_thrift.cc
@@ -0,0 +1,58 @@
+/* -*- c++ -*- */
+/*
+ * 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 GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gnuradio/rpcserver_thrift.h>
+#include <gnuradio/rpcserver_booter_thrift.h>
+
+namespace {
+  static const char* const CONTROL_PORT_CLASS("thrift");
+  static const char* const CONTROL_PORT_NAME("ControlPort");
+  static const char* const ENDPOINT_NAME("gnuradio");
+};
+
+rpcserver_booter_thrift::rpcserver_booter_thrift() :
+  thrift_server_template<rpcserver_base,
+                         rpcserver_thrift,
+                         rpcserver_booter_thrift,
+                         boost::shared_ptr<GNURadio::ControlPortIf> >
+  (this, std::string(CONTROL_PORT_NAME), std::string(ENDPOINT_NAME)),
+  d_type(std::string(CONTROL_PORT_CLASS))
+{;}
+
+rpcserver_booter_thrift::~rpcserver_booter_thrift()
+{;}
+
+rpcserver_base*
+rpcserver_booter_thrift::i()
+{
+  return thrift_server_template<rpcserver_base, rpcserver_thrift,
+                                rpcserver_booter_thrift,
+                                GNURadio::ControlPortIf>::i();
+}
+
+const std::vector<std::string>
+rpcserver_booter_thrift::endpoints()
+{
+  return thrift_server_template<rpcserver_base, rpcserver_thrift,
+                                rpcserver_booter_thrift,
+                                GNURadio::ControlPortIf>::endpoints();
+}
diff --git a/gnuradio-runtime/lib/controlport/thrift/rpcserver_thrift.cc 
b/gnuradio-runtime/lib/controlport/thrift/rpcserver_thrift.cc
new file mode 100644
index 0000000..9839e03
--- /dev/null
+++ b/gnuradio-runtime/lib/controlport/thrift/rpcserver_thrift.cc
@@ -0,0 +1,202 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gnuradio/rpcserver_thrift.h>
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include <pmt/pmt.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TTransportUtils.h>
+#include <boost/xpressive/xpressive.hpp>
+#include "ControlPort.h"
+
+#define DEBUG 0
+
+using namespace rpcpmtconverter;
+
+rpcserver_thrift::rpcserver_thrift()
+{
+  //std::cerr << "rpcserver_thrift::ctor" << std::endl;
+}
+
+rpcserver_thrift::~rpcserver_thrift()
+{
+  //std::cerr << "rpcserver_thrift::dtor" << std::endl;
+}
+
+void
+rpcserver_thrift::registerConfigureCallback(const std::string &id, const 
configureCallback_t callback)
+{
+  {
+    std::cerr << "thrift::registerConfigureCallback: " << id << std::endl;
+    ConfigureCallbackMap_t::const_iterator iter(d_setcallbackmap.find(id));
+    if(iter != d_setcallbackmap.end()) {
+      std::stringstream s;
+      s << "rpcserver_thrift:: rpcserver_thrift ERROR registering set, already 
registered: "
+              << id << std::endl;
+      throw std::runtime_error(s.str().c_str());
+    }
+  }
+
+  if(DEBUG) {
+    std::cerr << "rpcserver_thrift registering set: " << id << std::endl;
+  }
+  d_setcallbackmap.insert(ConfigureCallbackMap_t::value_type(id, callback));
+}
+
+void
+rpcserver_thrift::unregisterConfigureCallback(const std::string &id)
+{
+  //std::cerr << "thrift::unregisterConfigureCallback: " << id << std::endl;
+  ConfigureCallbackMap_t::iterator iter(d_setcallbackmap.find(id));
+  if(iter == d_setcallbackmap.end()) {
+    std::stringstream s;
+    s << "rpcserver_thrift:: rpcserver_thrift ERROR unregistering set, not 
registered: "
+            << id << std::endl;
+    throw std::runtime_error(s.str().c_str());
+  }
+
+  if(DEBUG)
+    std::cerr << "rpcserver_thrift unregistering set: " << id << std::endl;
+
+  d_setcallbackmap.erase(iter);
+}
+
+void
+rpcserver_thrift::registerQueryCallback(const std::string &id,
+                                        const queryCallback_t callback)
+{
+  {
+    std::cerr << "thrift::registerQueryCallback: " << id << std::endl;
+    QueryCallbackMap_t::const_iterator iter(d_getcallbackmap.find(id));
+    if(iter != d_getcallbackmap.end()) {
+      std::stringstream s;
+      s << "rpcserver_thrift:: rpcserver_thrift ERROR registering get, already 
registered: "
+              << id << std::endl;
+      throw std::runtime_error(s.str().c_str());
+    }
+  }
+
+  if(DEBUG) {
+    std::cerr << "rpcserver_thrift registering get: " << id << std::endl;
+  }
+  d_getcallbackmap.insert(QueryCallbackMap_t::value_type(id, callback));
+}
+
+void
+rpcserver_thrift::unregisterQueryCallback(const std::string &id)
+{
+  //std::cerr << "thrift::unregisterQueryCallback: " << id << std::endl;
+  QueryCallbackMap_t::iterator iter(d_getcallbackmap.find(id));
+  if(iter == d_getcallbackmap.end()) {
+    std::stringstream s;
+    s << "rpcserver_thrift:: rpcserver_thrift ERROR unregistering get,  
registered: "
+            << id << std::endl;
+    throw std::runtime_error(s.str().c_str());
+  }
+
+  if(DEBUG) {
+    std::cerr << "rpcserver_thrift unregistering get: " << id << std::endl;
+  }
+
+  d_getcallbackmap.erase(iter);
+}
+
+void
+rpcserver_thrift::setKnobs(const GNURadio::KnobMap& knobs)
+{
+  std::for_each(knobs.begin(), knobs.end(),
+                set_f<GNURadio::KnobMap::value_type,ConfigureCallbackMap_t>
+                (d_setcallbackmap, cur_priv));
+}
+
+
+void
+rpcserver_thrift::getKnobs(GNURadio::KnobMap& _return, const 
GNURadio::KnobIDList& knobs)
+{
+  GNURadio::KnobMap outknobs;
+
+  if(knobs.size() == 0) {
+    std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(),
+            get_all_f<QueryCallbackMap_t::value_type, QueryCallbackMap_t, 
GNURadio::KnobMap>
+                    (d_getcallbackmap, cur_priv, outknobs));
+  }
+  else {
+    std::for_each(knobs.begin(), knobs.end(),
+            get_f<GNURadio::KnobIDList::value_type, QueryCallbackMap_t>
+                    (d_getcallbackmap, cur_priv, outknobs));
+  }
+  _return = outknobs;
+}
+
+void
+rpcserver_thrift::getRe(GNURadio::KnobMap& _return, const 
GNURadio::KnobIDList& knobs)
+{
+  GNURadio::KnobMap outknobs;
+
+  if(knobs.size() == 0) {
+    std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(),
+                  get_all_f<QueryCallbackMap_t::value_type, 
QueryCallbackMap_t, GNURadio::KnobMap>
+                  (d_getcallbackmap, cur_priv, outknobs));
+  }
+  else {
+    QueryCallbackMap_t::iterator it;
+    for(it = d_getcallbackmap.begin(); it != d_getcallbackmap.end(); it++){
+      for(size_t j=0; j<knobs.size(); j++){
+        const boost::xpressive::sregex 
re(boost::xpressive::sregex::compile(knobs[j]));
+        if(boost::xpressive::regex_match(it->first, re)){
+          get_f<GNURadio::KnobIDList::value_type, QueryCallbackMap_t>
+            (d_getcallbackmap, cur_priv, outknobs)(it->first);
+          break;
+        }
+      }
+    }
+  }
+  _return = outknobs;
+}
+
+void
+rpcserver_thrift::properties(GNURadio::KnobPropMap& _return, const 
GNURadio::KnobIDList& knobs)
+{
+  GNURadio::KnobPropMap outknobs;
+
+  if(knobs.size() == 0) {
+    std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(),
+            properties_all_f<QueryCallbackMap_t::value_type,
+                    QueryCallbackMap_t, 
GNURadio::KnobPropMap>(d_getcallbackmap, cur_priv, outknobs));
+  }
+  else {
+    std::for_each(knobs.begin(), knobs.end(),
+            properties_f<GNURadio::KnobIDList::value_type,
+                    QueryCallbackMap_t, 
GNURadio::KnobPropMap>(d_getcallbackmap, cur_priv, outknobs));
+  }
+  _return = outknobs;
+}
+
+void
+rpcserver_thrift::shutdown() {
+  if (DEBUG) {
+    std::cerr << "Shutting down..." << std::endl;
+  }
+}
diff --git a/gnuradio-runtime/lib/controlport/thrift/thrift_application_base.cc 
b/gnuradio-runtime/lib/controlport/thrift/thrift_application_base.cc
new file mode 100644
index 0000000..5f190c0
--- /dev/null
+++ b/gnuradio-runtime/lib/controlport/thrift/thrift_application_base.cc
@@ -0,0 +1,45 @@
+/* -*- c++ -*- */
+/*
+ * 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 GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gnuradio/thrift_application_base.h>
+
+int thrift_application_common::d_reacquire_attributes(0);
+bool thrift_application_common::d_main_called(false);
+bool thrift_application_common::d_have_thrift_config(false);
+boost::shared_ptr<boost::thread> thrift_application_common::d_thread;
+std::string thrift_application_common::d_endpointStr("");
+
+boost::shared_ptr<thrift_application_common>
+thrift_application_common::Instance()
+{
+  static boost::shared_ptr<thrift_application_common>
+    instance(new thrift_application_common());
+  return instance;
+}
+
+int
+thrift_application_common::run(int, char**)
+{
+  std::cerr << "thrift_application_common: run" << std::endl;
+  d_thriftserver->serve();
+  return EXIT_SUCCESS;
+}
diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/ThriftRadioClient.py 
b/gnuradio-runtime/python/gnuradio/ctrlport/ThriftRadioClient.py
new file mode 100644
index 0000000..6e6faec
--- /dev/null
+++ b/gnuradio-runtime/python/gnuradio/ctrlport/ThriftRadioClient.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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 thrift import Thrift
+from thrift.transport import TSocket
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol
+#from ControlPort.GNURadio import ControlPort
+from gnuradio.ControlPort.GNURadio import ControlPort
+import sys
+
+class ThriftRadioClient:
+    def __init__(self, host, port):
+        self.tsocket = TSocket.TSocket(host, port)
+        self.transport = TTransport.TBufferedTransport(self.tsocket)
+        self.protocol = TBinaryProtocol.TBinaryProtocol(self.transport)
+
+        self.radio = ControlPort.Client(self.protocol)
+        self.transport.open()
+
+    def __del__(self):
+        self.transport.close()
+
+    def getRadio(self, host, port):
+        return self.radio
diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx 
b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx
new file mode 100755
index 0000000..5907837
--- /dev/null
+++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx
@@ -0,0 +1,861 @@
+#!/usr/bin/env python2
+#
+# 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.
+#
+
+import random,math,operator
+import networkx as nx;
+import matplotlib
+matplotlib.use("Qt4Agg");
+import matplotlib.pyplot as plt
+from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as 
FigureCanvas
+from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as 
NavigationToolbar
+from matplotlib.figure import Figure
+
+from gnuradio import gr, ctrlport
+
+from PyQt4 import QtCore,Qt,Qwt5
+import PyQt4.QtGui as QtGui
+import sys, time, re, pprint
+import itertools
+
+
+from gnuradio.ctrlport.GrDataPlotter import *
+#from gnuradio.ctrlport import GNURadio
+from gnuradio.ctrlport.ThriftRadioClient import ThriftRadioClient
+
+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.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 Performance Monitor")
+        self.setUnifiedTitleAndToolBarOnMac(True)
+
+        self.newCon(radio, port)
+        icon = QtGui.QIcon(ctrlport.__path__[0] + "/icon.png" )
+        self.setWindowIcon(icon)
+
+    def newSubWindow(self, window, title):
+        child = window;
+        child.setWindowTitle(title)
+        self.mdiArea.addSubWindow(child)
+        self.conns.append(child)
+        child.show();
+        self.mdiArea.currentSubWindow().showMaximized()
+
+
+    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))
+#            horizbar = QtGui.QScrollArea()
+#            horizbar.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
+#            horizbar.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
+#            horizbar.setWidget(child)
+#            self.mdiArea.addSubWindow(horizbar)
+            self.mdiArea.addSubWindow(child)
+            self.mdiArea.currentSubWindow().showMaximized()
+
+        self.conns.append(child)
+        self.plots.append([])
+
+    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.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.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 = 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 DataTable(QtGui.QWidget):
+    def update(self):
+        print "update"
+
+    def __init__(self, radio, G):
+        QtGui.QWidget.__init__( self)
+
+        self.layout = QtGui.QVBoxLayout(self);
+        self.hlayout = QtGui.QHBoxLayout();
+        self.layout.addLayout(self.hlayout);
+
+        self.G = G;
+        self.radio = radio;
+
+        self._keymap = None
+
+        # Create a combobox to set the type of statistic we want.
+        self._statistic = "Instantaneous"
+        self._statistics_table = {"Instantaneous": "",
+                                  "Average": "avg ",
+                                  "Variance": "var "}
+        self.stattype = QtGui.QComboBox()
+        self.stattype.addItem("Instantaneous")
+        self.stattype.addItem("Average")
+        self.stattype.addItem("Variance")
+        self.stattype.setMaximumWidth(200)
+        self.hlayout.addWidget(self.stattype);
+        self.stattype.currentIndexChanged.connect(self.stat_changed)
+
+        # Create a checkbox to toggle sorting of graphs
+        self._sort = False
+        self.checksort = QtGui.QCheckBox("Sort")
+        self.checksort.setCheckState(self._sort)
+        self.hlayout.addWidget(self.checksort);
+        self.checksort.stateChanged.connect(self.checksort_changed)
+
+        # set up table
+        self.perfTable = Qt.QTableWidget();
+        self.perfTable.setColumnCount(2)
+        self.perfTable.verticalHeader().hide();
+        self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent 
Runtime"] );
+        self.perfTable.horizontalHeader().setStretchLastSection(True);
+        self.perfTable.setSortingEnabled(True)
+        nodes = self.G.nodes(data=True)
+
+        # set up plot
+        self.f = plt.figure(figsize=(10,8), dpi=90)
+        self.sp = self.f.add_subplot(111);
+        self.sp.autoscale_view(True,True,True);
+        self.sp.set_autoscale_on(True)
+        self.canvas = FigureCanvas(self.f)
+
+        # set up tabs
+        self.tabber = QtGui.QTabWidget();
+        self.layout.addWidget(self.tabber);
+        self.tabber.addTab(self.perfTable,"Table View");
+        self.tabber.addTab(self.canvas, "Graph View");
+
+        # set up timer
+        self.timer = QtCore.QTimer()
+        self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update)
+        self.timer.start(500)
+
+        for i in range(0,len(nodes)):
+            self.perfTable.setItem(
+                i,0,
+                Qt.QTableWidgetItem(nodes[i][0]))
+
+    def table_update(self,data):
+        for k in data.keys():
+            weight = data[k]
+            existing = 
self.perfTable.findItems(str(k),QtCore.Qt.MatchFixedString)
+            if(len(existing) == 0):
+                i = self.perfTable.rowCount();
+                self.perfTable.setRowCount( i+1)
+                self.perfTable.setItem( i,0, Qt.QTableWidgetItem(str(k)))
+                self.perfTable.setItem( i,1, Qt.QTableWidgetItem(str(weight)))
+            else:
+                self.perfTable.setItem( self.perfTable.row(existing[0]),1, 
Qt.QTableWidgetItem(str(weight)))
+
+    def stat_changed(self, index):
+        self._statistic = str(self.stattype.currentText())
+
+    def checksort_changed(self, state):
+        self._sort = state > 0
+
+class DataTableBuffers(DataTable):
+    def __init__(self, radio, G):
+        DataTable.__init__(self,radio,G)
+        self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent 
Buffer Full"] );
+
+    def update(self):
+        nodes = self.G.nodes();
+
+        # get buffer fullness for all blocks
+        kl = map(lambda x: "%s::%soutput %% full" % \
+                     (x, self._statistics_table[self._statistic]),
+                 nodes);
+        buf_knobs = self.radio.getKnobs(kl)
+
+        # strip values out of ctrlport response
+        buffer_fullness = dict(zip(
+                    map(lambda x: x.split("::")[0], buf_knobs.keys()),
+                    map(lambda x: x.value, buf_knobs.values())))
+
+        blockport_fullness = {}
+        for blk in buffer_fullness:
+            bdata = buffer_fullness[blk].a_f32vector
+            for port in range(0,len(bdata)):
+                blockport_fullness["%s:%d"%(blk,port)] =  bdata[port];
+
+        self.table_update(blockport_fullness);
+
+        if(self._sort):
+            sorted_fullness = sorted(blockport_fullness.iteritems(), 
key=operator.itemgetter(1))
+            self._keymap = map(operator.itemgetter(0), sorted_fullness)
+        else:
+            if self._keymap:
+                sorted_fullness = len(self._keymap)*['',]
+                for b in blockport_fullness:
+                    sorted_fullness[self._keymap.index(b)] = (b, 
blockport_fullness[b])
+            else:
+                sorted_fullness = blockport_fullness.items()
+
+        self.sp.clear();
+        plt.figure(self.f.number)
+        plt.subplot(111);
+        self.sp.bar(range(0,len(sorted_fullness)), map(lambda x: x[1], 
sorted_fullness),
+                    alpha=0.5)
+        self.sp.set_ylabel("% Buffers Full");
+        self.sp.set_xticks( map(lambda x: x+0.5, 
range(0,len(sorted_fullness))))
+        self.sp.set_xticklabels( map(lambda x: "  " + x, map(lambda x: x[0], 
sorted_fullness)),
+                                 rotation="vertical", 
verticalalignment="bottom" )
+        self.canvas.draw();
+        self.canvas.show();
+
+class DataTableRuntimes(DataTable):
+    def __init__(self, radio, G):
+        DataTable.__init__(self,radio,G)
+        #self.perfTable.setRowCount(len( self.G.nodes() ))
+
+    def update(self):
+        nodes = self.G.nodes();
+
+        # get work time for all blocks
+        kl = map(lambda x: "%s::%swork time" % \
+                     (x, self._statistics_table[self._statistic]),
+                 nodes);
+        wrk_knobs = self.radio.getKnobs(kl)
+
+        # strip values out of ctrlport response
+        total_work = sum(map(lambda x: x.value.a_double, wrk_knobs.values()))
+        if(total_work == 0):
+            total_work = 1
+        work_times = dict(zip(
+            map(lambda x: x.split("::")[0], wrk_knobs.keys()),
+            map(lambda x: x.value.a_double/total_work, wrk_knobs.values())))
+
+        # update table view
+        self.table_update(work_times)
+
+        if(self._sort):
+            sorted_work = sorted(work_times.iteritems(), 
key=operator.itemgetter(1))
+            self._keymap = map(operator.itemgetter(0), sorted_work)
+        else:
+            if self._keymap:
+                sorted_work = len(self._keymap)*['',]
+                for b in work_times:
+                    sorted_work[self._keymap.index(b)] = (b, work_times[b])
+            else:
+                sorted_work = work_times.items()
+
+        self.sp.clear();
+        plt.figure(self.f.number)
+        plt.subplot(111);
+        self.sp.bar(range(0,len(sorted_work)), map(lambda x: x[1], 
sorted_work),
+                    alpha=0.5)
+        self.sp.set_ylabel("% Runtime");
+        self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_work))))
+        self.sp.set_xticklabels( map(lambda x: "  " + x[0], sorted_work),
+                                 rotation="vertical", 
verticalalignment="bottom" )
+
+        self.canvas.draw();
+        self.canvas.show();
+
+class MForm(QtGui.QWidget):
+    def update(self):
+        try:
+            try:
+                # update current clock type
+                self.prevent_clock_change = True;
+                kl1 = None;
+                if(self.clockKey == None):
+                    kl1 = self.radio.getRe([".*perfcounter_clock"])
+                else:
+                    kl1 = self.radio.getKnobs([self.clockKey])
+                self.clockKey = kl1.keys()[0]
+                self.currClock = kl1[self.clockKey].value.a_long
+                self.clockSelIdx = self.clocks.values()[self.currClock]
+                self.clockSel.setCurrentIndex(self.clockSelIdx)
+                self.prevent_clock_change = False
+            except:
+                print "WARNING: Failed to get current clock setting!"
+
+            nodes_stream = self.G_stream.nodes()
+            nodes_msg = self.G_msg.nodes()
+
+            # get current buffer depths of all output buffers
+            kl = map(lambda x: "%s::%soutput %% full" % \
+                     (x, self._statistics_table[self._statistic]),
+                     nodes_stream);
+
+            st = time.time()
+            buf_knobs = self.radio.getKnobs(kl)
+            td1 = time.time() - st;
+
+            # strip values out of ctrlport response
+            buf_vals = dict(zip(
+                map(lambda x: x.split("::")[0], buf_knobs.keys()),
+                map(lambda x: x.value, buf_knobs.values())))
+
+            # get work time for all blocks
+            kl = map(lambda x: "%s::%swork time" % \
+                         (x, self._statistics_table[self._statistic]),
+                     nodes_stream);
+            st = time.time()
+            wrk_knobs = self.radio.getKnobs(kl)
+            td2 = time.time() - st;
+
+            # strip values out of ctrlport response
+            total_work = sum(map(lambda x: x.value.a_double, 
wrk_knobs.values()))
+            if(total_work == 0):
+                total_work = 1
+            work_times = dict(zip(
+                        map(lambda x: x.split("::")[0], wrk_knobs.keys()),
+                        map(lambda x: x.value.a_double/total_work, 
wrk_knobs.values())))
+            work_times_padded = dict(zip(
+                        self.G.nodes(),
+                        [0.1]*len(self.G.nodes())))
+            work_times_padded.update(work_times)
+
+            for n in nodes_stream:
+                # ne is the list of edges away from this node!
+                ne = self.G.edges([n],True);
+                #for e in ne: # iterate over edges from this block
+                for e in ne: # iterate over edges from this block
+                    # get the right output buffer/port weight for each edge
+                    sourceport = e[2]["sourceport"];
+                    if(e[2]["type"] == "stream"):
+                        #newweight = buf_vals[n][sourceport]
+                        newweight = buf_vals[n].a_f32vector[0]
+                        e[2]["weight"] = newweight;
+
+            for n in nodes_msg:
+                ne = self.G.edges([n],True);
+                for e in ne: # iterate over edges from this block
+                    sourceport = e[2]["sourceport"];
+                    if(e[2]["type"] == "msg"):
+                        #newweight = buf_vals[n][sourceport]
+                        newweight = 0.01;
+                        e[2]["weight"] = newweight;
+
+            # set updated weights
+            #self.node_weights = map(lambda x: 20+2000*work_times[x], 
nodes_stream);
+            self.node_weights = map(lambda x: 20+2000*work_times_padded[x], 
self.G.nodes());
+            self.edge_weights = map(lambda x: 100.0*x[2]["weight"], 
self.G.edges(data=True));
+
+            # draw graph updates
+            self.updateGraph();
+
+            latency = td1 + td2;
+            self.parent.statusBar().showMessage("Current GNU Radio Control 
Port Query Latency: %f ms"%\
+                                                    (latency*1000))
+
+        except Exception, e:
+            sys.stderr.write("ctrlport-monitor: radio.getKnobs 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)
+
+                # 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
+
+    def rtt(self):
+        self.parent.newSubWindow(  DataTableRuntimes(self.radio, 
self.G_stream),  "Runtime Table" );
+
+    def bpt(self):
+        self.parent.newSubWindow(  DataTableBuffers(self.radio, 
self.G_stream),  "Buffers Table" );
+
+    def resetPCs(self):
+        knob = GNURadio.ttypes.KnobBase()
+        knob.a_bool = False
+        km = {}
+        for b in self.blocks_list:
+            km[b + "::reset_perf_counters"] = knob
+        k = self.radio.setKnobs(km)
+
+    def toggleFlowgraph(self):
+        if self.pauseFGAct.isChecked():
+            self.pauseFlowgraph()
+        else:
+            self.unpauseFlowgraph()
+
+    def pauseFlowgraph(self):
+        knob = GNURadio.ttypes.KnobBase()
+        knob.a_bool = False
+        km = {}
+        km[self.top_block + "::lock"] = knob
+        km[self.top_block + "::stop"] = knob
+        k = self.radio.setKnobs(km)
+
+    def unpauseFlowgraph(self):
+        knob = GNURadio.ttypes.KnobBase()
+        knob.a_bool = False
+        km = {}
+        km[self.top_block + "::unlock"] = knob
+        k = self.radio.setKnobs(km)
+
+    def stat_changed(self, index):
+        self._statistic = str(self.stattype.currentText())
+
+    def update_clock(self, clkidx):
+        if(self.prevent_clock_change):
+            return;
+        idx = self.clockSel.currentIndex();
+        clk = self.clocks.values()[idx]
+#        print "UPDATE CLOCK!!! %d -> %d"%(idx,clk);
+        k = self.radio.getKnobs([self.clockKey]);
+        k[self.clockKey].value = clk;
+        km = {};
+        km[self.clockKey] = k[self.clockKey];
+        self.radio.setKnobs(km);
+
+    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.layoutTop = QtGui.QVBoxLayout(self)
+        self.ctlBox = QtGui.QHBoxLayout();
+        self.layout = QtGui.QHBoxLayout()
+
+        self.layoutTop.addLayout(self.ctlBox);
+        self.layoutTop.addLayout(self.layout);
+
+        self.rttAct = QtGui.QAction("Runtime Table",
+                self, statusTip="Runtime Table", triggered=self.rtt)
+        self.rttBut = Qt.QToolButton()
+        self.rttBut.setDefaultAction(self.rttAct);
+        self.ctlBox.addWidget(self.rttBut);
+
+        self.bptAct = QtGui.QAction("Buffer Table",
+                self, statusTip="Buffer Table", triggered=self.bpt)
+        self.bptBut = Qt.QToolButton()
+        self.bptBut.setDefaultAction(self.bptAct);
+        self.ctlBox.addWidget(self.bptBut);
+
+        self.resetPCsAct = QtGui.QAction("Reset", self,
+                statusTip="Reset all Performance Counters",
+                triggered=self.resetPCs)
+        self.resetPCsBut = Qt.QToolButton()
+        self.resetPCsBut.setDefaultAction(self.resetPCsAct);
+        self.ctlBox.addWidget(self.resetPCsBut);
+
+        self.pauseFGAct = QtGui.QAction("Pause", self,
+                statusTip="Pause the Flowgraph",
+                triggered=self.toggleFlowgraph)
+        self.pauseFGAct.setCheckable(True)
+        self.pauseFGBut = Qt.QToolButton()
+        self.pauseFGBut.setDefaultAction(self.pauseFGAct);
+        self.ctlBox.addWidget(self.pauseFGBut);
+
+        self.prevent_clock_change = True;
+        self.clockKey = None;
+        self.clocks = {"MONOTONIC":1, "THREAD":3};
+        self.clockSel = QtGui.QComboBox(self);
+        map(lambda x: self.clockSel.addItem(x), self.clocks.keys());
+        self.ctlBox.addWidget(self.clockSel);
+        self.clockSel.currentIndexChanged.connect(self.update_clock);
+        self.prevent_clock_change = False;
+
+        self._statistic = "Instantaneous"
+        self._statistics_table = {"Instantaneous": "",
+                                  "Average": "avg ",
+                                  "Variance": "var "}
+        self.stattype = QtGui.QComboBox()
+        self.stattype.addItem("Instantaneous")
+        self.stattype.addItem("Average")
+        self.stattype.addItem("Variance")
+        self.stattype.setMaximumWidth(200)
+        self.ctlBox.addWidget(self.stattype);
+        self.stattype.currentIndexChanged.connect(self.stat_changed)
+
+#        self.setLayout(self.layout);
+
+        self.radio = radio.radio
+        self.knobprops = self.radio.properties([])
+        self.parent.knobprops.append(self.knobprops)
+
+        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"
+
+        tmplist = []
+        knobs = self.radio.getKnobs([])
+        edgelist = None
+        msgedgelist = None
+        for k in knobs:
+            propname = k.split("::")
+            blockname = propname[0]
+            keyname = propname[1]
+            if(keyname == "edge list"):
+                edgelist = knobs[k].value
+                self.top_block = blockname
+            elif(keyname == "msg edges list"):
+                msgedgelist = knobs[k].value
+            elif(blockname not in tmplist):
+                # only take gr_blocks (no hier_block2)
+                if(knobs.has_key(input_name(blockname))):
+                    tmplist.append(blockname)
+
+
+        if not edgelist:
+            sys.stderr.write("Could not find list of edges from flowgraph. " + 
\
+                                 "Make sure the option 'edges_list' is enabled 
" + \
+                                 "in the ControlPort configuration.\n\n")
+            sys.exit(1)
+
+        self.blocks_list = tmplist
+        edges = edgelist.a_string.split("\n")[0:-1]
+        msgedges = msgedgelist.a_string.split("\n")[0:-1]
+
+        edgepairs_stream = [];
+        edgepairs_msg = [];
+
+        # add stream connections
+        for e in edges:
+            _e = e.split("->")
+            edgepairs_stream.append( (_e[0].split(":")[0], _e[1].split(":")[0],
+                               {"type":"stream", 
"sourceport":int(_e[0].split(":")[1])}) );
+
+        # add msg connections
+        for e in msgedges:
+            _e = e.split("->")
+            edgepairs_msg.append( (_e[0].split(":")[0], _e[1].split(":")[0],
+                               {"type":"msg", 
"sourceport":_e[0].split(":")[1]}) );
+
+        self.G = nx.MultiDiGraph();
+        self.G_stream = nx.MultiDiGraph();
+        self.G_msg = nx.MultiDiGraph();
+
+        self.G.add_edges_from(edgepairs_stream);
+        self.G.add_edges_from(edgepairs_msg);
+
+        self.G_stream.add_edges_from(edgepairs_stream);
+        self.G_msg.add_edges_from(edgepairs_msg);
+
+        n_edges = self.G.edges(data=True);
+        for e in n_edges:
+            e[2]["weight"] = 5+random.random()*10;
+
+        self.G.clear();
+        self.G.add_edges_from(n_edges);
+
+
+        self.f = plt.figure(figsize=(10,8), dpi=90)
+        self.sp = self.f.add_subplot(111);
+        self.sp.autoscale_view(True,True,True);
+        self.sp.set_autoscale_on(True)
+
+        self.canvas = FigureCanvas(self.f)
+        self.layout.addWidget(self.canvas);
+
+        self.pos = nx.graphviz_layout(self.G);
+        #self.pos = nx.pygraphviz_layout(self.G);
+        #self.pos = nx.spectral_layout(self.G);
+        #self.pos = nx.circular_layout(self.G);
+        #self.pos = nx.shell_layout(self.G);
+        #self.pos = nx.spring_layout(self.G);
+
+        # generate weights and plot
+        self.update();
+
+        # set up timer
+        self.timer = QtCore.QTimer()
+        self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update)
+        self.timer.start(1000)
+
+        # Set up mouse callback functions to move blocks around.
+        self._grabbed = False
+        self._current_block = ''
+        self.f.canvas.mpl_connect('button_press_event',
+                                  self.button_press)
+        self.f.canvas.mpl_connect('motion_notify_event',
+                                  self.mouse_move)
+        self.f.canvas.mpl_connect('button_release_event',
+                                  self.button_release)
+
+    def button_press(self, event):
+        x, y = event.xdata, event.ydata
+        thrsh = 100
+
+        if(x is not None and y is not None):
+            nearby = map(lambda z: math.sqrt( math.pow(x-z[0],2) + 
math.pow(y-z[1],2)), self.pos.values())
+            i = nearby.index(min(nearby))
+            if(abs(self.pos.values()[i][0] - x) < thrsh and
+               abs(self.pos.values()[i][1]-y) < thrsh):
+                self._current_block = self.pos.keys()[i]
+                #print "MOVING BLOCK: ", self._current_block
+                #print "CUR POS: ", self.pos.values()[i]
+                self._grabbed = True
+
+    def mouse_move(self, event):
+        if self._grabbed:
+            x, y = event.xdata, event.ydata
+            if(x is not None and y is not None):
+                #print "NEW POS: ", (x,y)
+                self.pos[self._current_block] = (x,y)
+                self.updateGraph();
+
+    def button_release(self, event):
+        self._grabbed = False
+
+
+    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 updateGraph(self):
+
+        self.canvas.updateGeometry()
+        self.sp.clear();
+        plt.figure(self.f.number)
+        plt.subplot(111);
+        nx.draw(self.G, self.pos,
+                edge_color=self.edge_weights,
+                node_color='#A0CBE2',
+                width=map(lambda x: 3+math.log(x), self.edge_weights),
+                node_shape="s",
+                node_size=self.node_weights,
+                #edge_cmap=plt.cm.Blues,
+                edge_cmap=plt.cm.Reds,
+                ax=self.sp,
+                arrows=False
+        )
+        nx.draw_networkx_labels(self.G, self.pos,
+                                font_size=12)
+
+        self.canvas.draw();
+        self.canvas.show();
+
+#class MyClient(ThriftRadioClient):
+#    def __init__(self):
+#        ThriftRadioClient.__init__(self, MAINWindow)
+#
+#sys.exit(MyClient().main(sys.argv))
+
+app = QtGui.QApplication(sys.argv)
+
+host = "127.0.0.1"
+port = 9090
+iface = "lo"
+radio = ThriftRadioClient(host, port)
+mainwin = MAINWindow(radio, port, iface)
+mainwin.show()
+app.exec_()



reply via email to

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