#!/usr/bin/env python # # Copyright 2012-2013 Free Software Foundation, Inc. # # This file is part of GNU Radio # # GNU Radio is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. # # GNU Radio is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Radio; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. # 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 PyQt4 import QtCore,Qt,Qwt5 import PyQt4.QtGui as QtGui import sys, time, re, pprint import itertools from gnuradio import gr, ctrlport from gnuradio.ctrlport.GrDataPlotter import * class MAINWindow(QtGui.QMainWindow): def minimumSizeHint(self): return QtGui.QSize(800,600) def __init__(self, radioclient): super(MAINWindow, self).__init__() self.radioclient = radioclient self.conns = [] self.plots = [] self.knobprops = [] self.mdiArea = QtGui.QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QtCore.QSignalMapper(self) self.windowMapper.mapped[QtGui.QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.setWindowTitle("GNU Radio Performance Monitor") self.setUnifiedTitleAndToolBarOnMac(True) self.newCon(radioclient) 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, csomeBool): child = MForm(self.radioclient, len(self.conns), self, dialogprompt = not csomeBool) if(child.radioclient is not None): child.setWindowTitle(str(child.radioclient)) self.mdiArea.addSubWindow(child) self.mdiArea.currentSubWindow().showMaximized() self.conns.append(child) self.plots.append([]) def 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-perf-monitorx", 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 closeEvent(self, event): self.timer = None def __init__(self, radioclient, G): QtGui.QWidget.__init__( self) self.layout = QtGui.QVBoxLayout(self) self.hlayout = QtGui.QHBoxLayout() self.layout.addLayout(self.hlayout) self.G = G self.radioclient = radioclient self._keymap = None self.disp = 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, radioclient, G): super(DataTableBuffers, self).__init__(radioclient, 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.radioclient.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] if bdata: for port in range(0,len(bdata)): blockport_fullness["%s:%d"%(blk,port)] = bdata[port] if(self.perfTable.isVisible()): self.table_update(blockport_fullness); else: 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() if(not self.disp): self.disp = 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") else: self.sp.set_xticklabels(map(lambda x: " " + x, map(lambda x: x[0], sorted_fullness)), rotation="vertical", verticalalignment="bottom") for r,w in zip(self.disp, sorted_fullness): r.set_height(w[1]) self.canvas.draw() class DataTableRuntimes(DataTable): def __init__(self, radioclient, G): super(DataTableRuntimes, self).__init__( radioclient, G) 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.radioclient.getKnobs(kl) # strip values out of ctrlport response total_work = sum(map(lambda x: x.value, 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/total_work, wrk_knobs.values()))) # update table view if(self.perfTable.isVisible()): self.table_update(work_times) else: 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() f = plt.figure(self.f.number) if(not self.disp): self.disp = 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" ) else: self.sp.set_xticklabels( map(lambda x: " " + x[0], sorted_work), rotation="vertical", verticalalignment="bottom" ) for r,w in zip(self.disp, sorted_work): r.set_height(w[1]) self.canvas.draw() 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.radioclient.getRe([".*perfcounter_clock"]) else: kl1 = self.radioclient.getKnobs([self.clockKey]) self.clockKey = kl1.keys()[0] self.currClock = kl1[self.clockKey].value self.clockSelIdx = self.clocks.values().index(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.radioclient.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.radioclient.getKnobs(kl) td2 = time.time() - st; # strip values out of ctrlport response total_work = sum(map(lambda x: x.value, 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/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] 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 = 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("gr-perf-monitorx: 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.radioclient, self.G_stream), "Runtime Table" ); def bpt(self): self.parent.newSubWindow( DataTableBuffers(self.radioclient, self.G_stream), "Buffers Table" ); def resetPCs(self): knobs = [] for b in self.blocks_list: knobs += [self.radioclient.Knob(b + "::reset_perf_counters"),] k = self.radioclient.setKnobs(knobs) def toggleFlowgraph(self): if self.pauseFGAct.isChecked(): self.pauseFlowgraph() else: self.unpauseFlowgraph() def pauseFlowgraph(self): knobs = [self.radioclient.Knob(self.top_block + "::lock"), self.radioclient.Knob(self.top_block + "::stop")] k = self.radioclient.setKnobs(knobs) def unpauseFlowgraph(self): knobs = [self.radioclient.Knob(self.top_block + "::unlock")] k = self.radioclient.setKnobs(knobs) 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.radioclient.getKnobs([self.clockKey]); k[self.clockKey].value = clk; km = {}; km[self.clockKey] = k[self.clockKey]; self.radioclient.setKnobs(km); def __init__(self, radioclient, uid=0, parent=None, dialogprompt = False): super(MForm, self).__init__() self.radioclient = radioclient # print("before radioclient.getHost()", radioclient.getHost(), radioclient.getPort(), "prompt", prompt) if(dialogprompt or radioclient.getHost() is None or radioclient.getPort() is None): # print("before ConInfoDialog") askinfo = ConInfoDialog(self); if askinfo.exec_(): host = str(askinfo.host.text()); port = str(askinfo.port.text()); # print("before radioclient.newConnection host: %s port: %s"%(host,port)) newradio = self.radioclient.newConnection(host, port) if newradio is None: print("Error making a %s connection to %s:%s from %s" % (radioclient.getName(), host, port, radioclient)) else: self.radioclient = newradio else: self.radioclient = 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 = radioclient 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.split("\n")[0:-1] msgedges = msgedgelist.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.radioclient, 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 MyApp(object): def __init__(self, args): p = gr.prefs() cp_on = p.get_bool("ControlPort", "on", False) cp_edges = p.get_bool("ControlPort", "edges_list", False) pcs_on = p.get_bool("PerfCounters", "on", False) pcs_exported = p.get_bool("PerfCounters", "export", False) 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_) def run(self, client): MAINWindow(client).show() MyApp(sys.argv)