diff options
author | Tom Rondeau <tom@trondeau.com> | 2015-02-25 17:12:21 -0500 |
---|---|---|
committer | Tom Rondeau <tom@trondeau.com> | 2015-04-02 15:38:56 -0700 |
commit | fe14ba8cadf3a691f459f0997e281b2d99b26b0e (patch) | |
tree | f5a72b297202d93d0f52f53bf4d281aaebf16f4f | |
parent | 8270b20580b43fd00d1f1c1df14dee1f2a2498ad (diff) |
controlport: more work on the translation layer; properties and setting parameters in gr-ctrlport-monitor now working.
5 files changed, 195 insertions, 90 deletions
diff --git a/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc b/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc index 9025784aab..27573af742 100644 --- a/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc +++ b/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc @@ -172,10 +172,38 @@ rpcpmtconverter::to_pmt(const GNURadio::Knob& knob) return pmt::from_complex(cpx); } else if(knob.type == GNURadio::BaseTypes::F32VECTOR) { - if (knob.value.a_bool) - return pmt::PMT_T; - else - return pmt::PMT_F; + std::vector<double> v_double = knob.value.a_f32vector; + std::vector<float> v(v_double.begin(), v_double.end()); + return pmt::init_f32vector(v.size(), v); + } + else if(knob.type == GNURadio::BaseTypes::F64VECTOR) { + std::vector<double> v = knob.value.a_f64vector; + return pmt::init_f64vector(v.size(), v); + } + else if(knob.type == GNURadio::BaseTypes::S64VECTOR) { + std::vector<int64_t> v = knob.value.a_s64vector; + return pmt::init_s64vector(v.size(), v); + } + else if(knob.type == GNURadio::BaseTypes::S32VECTOR) { + std::vector<int32_t> v = knob.value.a_s32vector; + return pmt::init_s32vector(v.size(), v); + } + else if(knob.type == GNURadio::BaseTypes::S16VECTOR) { + std::vector<int16_t> v = knob.value.a_s16vector; + return pmt::init_s16vector(v.size(), v); + } + else if(knob.type == GNURadio::BaseTypes::S8VECTOR) { + std::vector<int8_t> v = knob.value.a_s8vector; + return pmt::init_s8vector(v.size(), v); + } + else if(knob.type == GNURadio::BaseTypes::C32VECTOR) { + std::vector<GNURadio::complex> v0 = knob.value.a_c32vector; + std::vector<GNURadio::complex>::iterator vitr; + std::vector<gr_complex> v; + for(vitr = v0.begin(); vitr != v0.end(); vitr++) { + v.push_back(gr_complex(vitr->re, vitr->im)); + } + return pmt::init_c32vector(v.size(), v); } else { std::cerr << "Error: Don't know how to handle Knob Type: " << knob.type << std::endl; assert(0); diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py b/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py index c17a4279d0..8f8895916b 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py +++ b/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py @@ -42,7 +42,7 @@ RPCMethods = {'thrift': 'Apache Thrift', #'ice': 'Zeroc ICE' } -import exceptions +import sys, exceptions """ Base class for RPC transport clients @@ -120,6 +120,9 @@ class RPCConnection(object): def shutdown(self): raise exceptions.NotImplementedError() + def printProperties(self, props): + raise exceptions.NotImplementedError() + """ RPC Client interface for the Apache Thrift middle-ware RPC transport. @@ -129,70 +132,143 @@ Args: """ class RPCConnectionThrift(RPCConnection): + class Knob(): + def __init__(self, key, value=None, ktype=0): + (self.key, self.value, self.ktype) = (key, value, ktype) + + def __repr__(self): + return "({0} = {1})".format(self.key, self.value) + def __init__(self, host=None, port=None): - from gnuradio.ctrlport.GNURadio.ttypes import BaseTypes - self.BaseTypes = BaseTypes + from gnuradio.ctrlport.GNURadio import ttypes + self.BaseTypes = ttypes.BaseTypes + self.KnobBase = ttypes.KnobBase if port is None: port = 9090 super(RPCConnectionThrift, self).__init__(method='thrift', port=port, host=host) self.newConnection(host, port) - class Knob(): - def __init__(self, key, value): - (self.key, self.value) = (key, value) - - self.types_dict = { - self.BaseTypes.BOOL: lambda k,b: Knob(k, b.value.a_bool), - self.BaseTypes.BYTE: lambda k,b: Knob(k, b.value.a_byte), - self.BaseTypes.SHORT: lambda k,b: Knob(k, b.value.a_short), - self.BaseTypes.INT: lambda k,b: Knob(k, b.value.a_int), - self.BaseTypes.LONG: lambda k,b: Knob(k, b.value.a_long), - self.BaseTypes.DOUBLE: lambda k,b: Knob(k, b.value.a_double), - self.BaseTypes.STRING: lambda k,b: Knob(k, b.value.a_string), - self.BaseTypes.COMPLEX: lambda k,b: Knob(k, b.value.a_complex), - self.BaseTypes.F32VECTOR: lambda k,b: Knob(k, b.value.a_f32vector), - self.BaseTypes.F64VECTOR: lambda k,b: Knob(k, b.value.a_f64vector), - self.BaseTypes.S64VECTOR: lambda k,b: Knob(k, b.value.a_s64vector), - self.BaseTypes.S32VECTOR: lambda k,b: Knob(k, b.value.a_s32vector), - self.BaseTypes.S16VECTOR: lambda k,b: Knob(k, b.value.a_s16vector), - self.BaseTypes.S8VECTOR: lambda k,b: Knob(k, b.value.a_s8vector), - self.BaseTypes.C32VECTOR: lambda k,b: Knob(k, b.value.a_c32vector), + self.unpack_dict = { + self.BaseTypes.BOOL: lambda k,b: self.Knob(k, b.value.a_bool, self.BaseTypes.BOOL), + self.BaseTypes.BYTE: lambda k,b: self.Knob(k, b.value.a_byte, self.BaseTypes.BYTE), + self.BaseTypes.SHORT: lambda k,b: self.Knob(k, b.value.a_short, self.BaseTypes.SHORT), + self.BaseTypes.INT: lambda k,b: self.Knob(k, b.value.a_int, self.BaseTypes.INT), + self.BaseTypes.LONG: lambda k,b: self.Knob(k, b.value.a_long, self.BaseTypes.LONG), + self.BaseTypes.DOUBLE: lambda k,b: self.Knob(k, b.value.a_double, self.BaseTypes.DOUBLE), + self.BaseTypes.STRING: lambda k,b: self.Knob(k, b.value.a_string, self.BaseTypes.STRING), + self.BaseTypes.COMPLEX: lambda k,b: self.Knob(k, b.value.a_complex, self.BaseTypes.COMPLEX), + self.BaseTypes.F32VECTOR: lambda k,b: self.Knob(k, b.value.a_f32vector, self.BaseTypes.F32VECTOR), + self.BaseTypes.F64VECTOR: lambda k,b: self.Knob(k, b.value.a_f64vector, self.BaseTypes.F64VECTOR), + self.BaseTypes.S64VECTOR: lambda k,b: self.Knob(k, b.value.a_s64vector, self.BaseTypes.S64VECTOR), + self.BaseTypes.S32VECTOR: lambda k,b: self.Knob(k, b.value.a_s32vector, self.BaseTypes.S32VECTOR), + self.BaseTypes.S16VECTOR: lambda k,b: self.Knob(k, b.value.a_s16vector, self.BaseTypes.S16VECTOR), + self.BaseTypes.S8VECTOR: lambda k,b: self.Knob(k, b.value.a_s8vector, self.BaseTypes.S8VECTOR), + self.BaseTypes.C32VECTOR: lambda k,b: self.Knob(k, b.value.a_c32vector, self.BaseTypes.C32VECTOR), } + self.pack_dict = { + self.BaseTypes.BOOL: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_bool = k.value)), + self.BaseTypes.BYTE: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_byte = k.value)), + self.BaseTypes.SHORT: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_short = k.value)), + self.BaseTypes.INT: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_int = k.value)), + self.BaseTypes.LONG: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_long = k.value)), + self.BaseTypes.DOUBLE: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_double = k.value)), + self.BaseTypes.STRING: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_string = k.value)), + self.BaseTypes.COMPLEX: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_complex = k.value)), + self.BaseTypes.F32VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_f32vector = k.value)), + self.BaseTypes.F64VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_f64vector = k.value)), + self.BaseTypes.S64VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_s64vector = k.value)), + self.BaseTypes.S32VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_s32vector = k.value)), + self.BaseTypes.S16VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_s16vector = k.value)), + self.BaseTypes.S8VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_s8vector = k.value)), + self.BaseTypes.C32VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_c32vector = k.value)), + } + + def unpackKnob(self, key, knob): + f = self.unpack_dict.get(knob.type, None) + if(f): + return f(key, knob) + else: + sys.stderr.write("unpackKnobs: Incorrect Knob type: {0}\n".format(knob.type)) + raise exceptions.ValueError + + def packKnob(self, knob): + f = self.pack_dict.get(knob.ktype, None) + if(f): + return f(knob) + else: + sys.stderr.write("packKnobs: Incorrect Knob type: {0}\n".format(knob.type)) + raise exceptions.ValueError + def newConnection(self, host=None, port=None): from gnuradio.ctrlport.ThriftRadioClient import ThriftRadioClient - self.thriftclient = ThriftRadioClient(self.getHost(), self.getPort()) + import thrift + try: + self.thriftclient = ThriftRadioClient(self.getHost(), self.getPort()) + except thrift.transport.TTransport.TTransportException: + sys.stderr.write("Could not connect to ControlPort endpoint at {0}:{1}.\n\n".format(host, port)) + sys.exit(1) def properties(self, *args): knobprops = self.thriftclient.radio.properties(*args) for key, knobprop in knobprops.iteritems(): -# print("key:", key, "value:", knobprop, "type:", knobprop.type) - knobprops[key].min = self.types_dict[knobprop.type](key, knobprop.min) - knobprops[key].max = self.types_dict[knobprop.type](key, knobprop.max) - knobprops[key].defaultvalue = self.types_dict[knobprop.type](key, knobprop.defaultvalue) - + #print("key:", key, "value:", knobprop, "type:", knobprop.type) + knobprops[key].min = self.unpackKnob(key, knobprop.min) + knobprops[key].max = self.unpackKnob(key, knobprop.max) + knobprops[key].defaultvalue = self.unpackKnob(key, knobprop.defaultvalue) return knobprops def getKnobs(self, *args): result = {} for key, knob in self.thriftclient.radio.getKnobs(*args).iteritems(): -# print("key:", key, "value:", knob, "type:", knob.type) - result[key] = self.types_dict[knob.type](key, knob) + #print("key:", key, "value:", knob, "type:", knob.type) + result[key] = self.unpackKnob(key, knob) + return result + + def getKnobsRaw(self, *args): + result = {} + for key, knob in self.thriftclient.radio.getKnobs(*args).iteritems(): + #print("key:", key, "value:", knob, "type:", knob.type) + result[key] = knob return result def getRe(self,*args): result = {} for key, knob in self.thriftclient.radio.getRe(*args).iteritems(): - result[key] = self.types_dict[knob.type](key, knob) + result[key] = self.unpackKnob(key, knob) return result - def setKnobs(self,*args): - self.thriftclient.radio.setKnobs(*args) + def setKnobs(self, *args): + if(type(*args) == dict): + a = dict(*args) + result = {} + for key, knob in a.iteritems(): + result[key] = self.packKnob(knob) + self.thriftclient.radio.setKnobs(result) + elif(type(*args) == list or type(*args) == tuple): + a = list(*args) + result = {} + for k in a: + result[k.key] = self.packKnob(k) + self.thriftclient.radio.setKnobs(result) + else: + sys.stderr.write("setKnobs: Invalid type; must be dict, list, or tuple\n") def shutdown(self): self.thriftclient.radio.shutdown() + def printProperties(self, props): + info = "" + info += "Item:\t\t{0}\n".format(props.description) + info += "units:\t\t{0}\n".format(props.units) + info += "min:\t\t{0}\n".format(props.min.value) + info += "max:\t\t{0}\n".format(props.max.value) + info += "default:\t\t{0}\n".format(props.defaultvalue.value) + info += "Type Code:\t0x{0:x}\n".format(props.type) + info += "Disp Code:\t0x{0:x}\n".format(props.display) + return info + """ GNURadioControlPortClient is the main class for creating a GNU Radio ControlPort client application for all transports. diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py b/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py index 661705d613..c5bfd0a8cb 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py +++ b/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py @@ -23,7 +23,7 @@ from gnuradio import gr from gnuradio import blocks from gnuradio import filter -from gnuradio.ctrlport import GNURadio +from gnuradio.ctrlport.GNURadio import ControlPort import sys, time, struct try: @@ -442,7 +442,7 @@ class GrDataPlotterValueTable: units = str(knobprops[itemKey].units) descr = str(knobprops[itemKey].description) - if(type(v) == GNURadio.complex): + if(type(v) == ControlPort.complex): v = v.re + v.im*1j # If it's a byte stream, Python thinks it's a string. # Unpack and convert to floats for plotting. @@ -468,7 +468,7 @@ class GrDataPlotterValueTable: for k in knobs.keys(): if k not in foundKeys: v = knobs[k].value - if(type(v) == GNURadio.complex): + if(type(v) == ControlPort.complex): v = v.re + v.im*1j # If it's a byte stream, Python thinks it's a string. # Unpack and convert to floats for plotting. diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor b/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor index 251b4bc03c..c866776355 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor +++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor @@ -101,8 +101,7 @@ class MAINWindow(QtGui.QMainWindow): self.plots.append([]) def propertiesMenu(self, key, radio, uid): - r = str(radio).split(" ") - title = "{0}:{1}".format(r[3], r[5]) + title = str(radio) props = radio.properties([key]) @@ -495,18 +494,19 @@ class UpdaterWindow(QtGui.QDialog): self.layout = QtGui.QVBoxLayout() self.props = radio.properties([key])[key] - info = str(self.props) + info = radio.printProperties(self.props) self.infoLabel = QtGui.QLabel(info) self.layout.addWidget(self.infoLabel) # Test here to make sure that a 'set' function exists try: - a = radio.set(radio.get([key])) + radio.setKnobs(radio.getKnobs([key])) has_set = True - except Ice.UnknownException: + except: has_set = False + if(has_set is False): self.cancelButton = QtGui.QPushButton("Ok") self.cancelButton.connect(self.cancelButton, QtCore.SIGNAL('clicked()'), self.reject) @@ -523,9 +523,9 @@ class UpdaterWindow(QtGui.QDialog): self.setButton = QtGui.QPushButton("OK") self.cancelButton = QtGui.QPushButton("Cancel") - rv = radio.get([key]) + rv = radio.getKnobs([key]) val = rv[key].value - if(type(val) == GNURadio.complex): + if(type(val) == ControlPort.complex): val = val.re + val.im*1j self.textInput.setText(str(val)) @@ -580,10 +580,10 @@ class UpdaterWindow(QtGui.QDialog): val = int(round(float(self.textInput.text()))) elif(type(self.sv.value) == float): val = float(self.textInput.text()) - elif(type(self.sv.value) == GNURadio.complex): + elif(type(self.sv.value) == ControlPort.complex): t = str(self.textInput.text()) t = complex(t.strip("(").strip(")").replace(" ", "")) - val = GNURadio.complex() + val = ControlPort.complex() val.re = t.real val.im = t.imag else: @@ -593,7 +593,7 @@ class UpdaterWindow(QtGui.QDialog): self.sv.value = val km = {} km[self.key] = self.sv - self.radioclient.setKnobs(km) + self.radio.setKnobs(km) if self._set_slider_value: self._set_slider_value(self.sv.value) @@ -725,7 +725,7 @@ class MForm(QtGui.QWidget): 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) + self.parent.propertiesMenu(itemname, self.radioclient, self.uid) def get_minmax(p): @@ -734,16 +734,20 @@ def get_minmax(p): # Find min/max or real or imag for GNURadio::complex # TODO: fix complex -# if(type(pmin) == GNURadio.complex): -# pmin = min(pmin.re, pmin.im) -# if(type(pmax) == GNURadio.complex): -# pmax = max(pmax.re, pmax.im) + if(type(pmin) == ControlPort.complex): + pmin = min(pmin.re, pmin.im) + if(type(pmax) == ControlPort.complex): + pmax = max(pmax.re, pmax.im) # If it's a byte stream, Python thinks it's a string. - if(type(pmin) == str): - pmin = struct.unpack('b', pmin)[0] - if(type(pmax) == str): - pmax = struct.unpack('b', pmax)[0] + try: + if(type(pmin) == str): + pmin = struct.unpack('b', pmin)[0] + if(type(pmax) == str): + pmax = struct.unpack('b', pmax)[0] + except struct.error: + pmin = [] + pmax = [] if pmin == []: pmin = None @@ -756,15 +760,9 @@ def get_minmax(p): return pmin, pmax -# class MyClient(IceRadioClient): -# def __init__(self): -# IceRadioClient.__init__(self, MAINWindow) -# -# sys.exit(MyClient().main(sys.argv)) - class MyApp(object): def __init__(self, args): - from GNURadioControlPortClient import GNURadioControlPortClient + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient GNURadioControlPortClient(args, 'thrift', self.run, QtGui.QApplication(sys.argv).exec_) def run(self, client): diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx index c066542ac7..23e11d4174 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx +++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx @@ -23,7 +23,7 @@ import random,math,operator import networkx as nx import matplotlib -matplotlib.use("Qt4Agg"); +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 @@ -338,7 +338,7 @@ class DataTable(QtGui.QWidget): class DataTableBuffers(DataTable): def __init__(self, radioclient, G): - DataTable.__init__(self,radioclient,G) + super(DataTableBuffers, self).__init__(radioclient, G) self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Buffer Full"] ); def update(self): @@ -376,20 +376,18 @@ class DataTableBuffers(DataTable): 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(); + self.canvas.draw() + self.canvas.show() class DataTableRuntimes(DataTable): def __init__(self, radioclient, G): - DataTable.__init__(self,radioclient,G) + super(DataTableRuntimes, self).__init__( radioclient, G) #self.perfTable.setRowCount(len( self.G.nodes() )) def update(self): @@ -549,12 +547,10 @@ class MForm(QtGui.QWidget): self.parent.newSubWindow( DataTableBuffers(self.radioclient, self.G_stream), "Buffers Table" ); def resetPCs(self): - knob = GNURadio.ttypes.KnobBase() - knob.a_bool = False - km = {} + knobs = [] for b in self.blocks_list: - km[b + "::reset_perf_counters"] = knob - k = self.radioclient.setKnobs(km) + knobs += [self.radioclient.Knob(b + "::reset_perf_counters"),] + k = self.radioclient.setKnobs(knobs) def toggleFlowgraph(self): if self.pauseFGAct.isChecked(): @@ -563,19 +559,13 @@ class MForm(QtGui.QWidget): 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.radioclient.setKnobs(km) + knobs = [self.radioclient.Knob(self.top_block + "::lock"), + self.radioclient.Knob(self.top_block + "::stop")] + k = self.radioclient.setKnobs(knobs) def unpauseFlowgraph(self): - knob = GNURadio.ttypes.KnobBase() - knob.a_bool = False - km = {} - km[self.top_block + "::unlock"] = knob - k = self.radioclient.setKnobs(km) + knobs = [self.radioclient.Knob(self.top_block + "::unlock")] + k = self.radioclient.setKnobs(knobs) def stat_changed(self, index): self._statistic = str(self.stattype.currentText()) @@ -844,6 +834,19 @@ class MForm(QtGui.QWidget): class MyApp(object): def __init__(self, args): + p = gr.prefs() + cp_on = p.get_bool("ControlPort", "on", None) + cp_edges = p.get_bool("ControlPort", "edges_list", None) + pcs_on = p.get_bool("PerfCounters", "on", None) + pcs_exported = p.get_bool("PerfCounters", "export", None) + if(not (pcs_on and cp_on and pcs_exported and cp_edges)): + print("Configuration has not turned on all of the appropriate ControlPort features:") + print("\t[ControlPort] on = {0}".format(cp_on)) + print("\t[ControlPort] edges_list = {0}".format(cp_edges)) + print("\t[PerfCounters] on = {0}".format(pcs_on)) + print("\t[PerfCounters] export = {0}".format(pcs_exported)) + exit(1) + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient GNURadioControlPortClient(args, 'thrift', self.run, QtGui.QApplication(sys.argv).exec_) |