#!/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 gnuradio import gr, ctrlport from PyQt4 import QtCore,Qt,Qwt5 import PyQt4.QtGui as QtGui import sys, time, re, pprint import itertools import Ice from gnuradio.ctrlport.IceRadioClient import * from gnuradio.ctrlport.GrDataPlotter import * from gnuradio.ctrlport import GNURadio class MAINWindow(QtGui.QMainWindow): def minimumSizeHint(self): return QtGui.QSize(800,600) def __init__(self, radio, port, interface): super(MAINWindow, self).__init__() self.conns = [] self.plots = [] self.knobprops = [] self.interface = interface self.mdiArea = QtGui.QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.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.get(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: for port in range(0,len(buffer_fullness[blk])): blockport_fullness["%s:%d"%(blk,port)] = buffer_fullness[blk][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.get(kl) # strip values out of ctrlport response total_work = sum(map(lambda x: x.value, wrk_knobs.values())) 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 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.get([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.radio.get(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.get(kl) td2 = time.time() - st; # strip values out of ctrlport response total_work = sum(map(lambda x: x.value, wrk_knobs.values())) 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 = 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.get threw exception ({0}).\n".format(e)) if(type(self.parent) is MAINWindow): # Find window of connection remove = [] for p in self.parent.mdiArea.subWindowList(): if self.parent.conns[self.uid] == p.widget(): remove.append(p) # 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 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.get([self.clockKey]); k[self.clockKey].value = clk; km = {}; km[self.clockKey] = k[self.clockKey]; self.radio.set(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.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 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.get([]) 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 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) 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.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(IceRadioClient): def __init__(self): IceRadioClient.__init__(self, MAINWindow) sys.exit(MyClient().main(sys.argv))