可点击和可选择的PyQt4散点图
我正在尝试创建一个散点图,里面大约有1000个数据点。我的目标是让每个数据点都可以通过鼠标点击来选择,这样就会弹出一个上下文菜单,允许用户删除或更改数据点的颜色。我一直在跟着matplotlib的教程,并查看可拖动矩形的练习,但我遇到了一些困难。我使用matplotlib.patches.Circle类来表示每个数据点,但我无法让'contains'方法与'button_press_event'正常工作。主要的问题是,每个Circle对象似乎没有与之关联的'canvas'对象。我在第16行遇到了以下错误:
AttributeError: 'NoneType' object has no attribute 'canvas'
这是我的代码:
#!/usr/bin/python -tt
import sys
import numpy
#import matplotlib.pyplot
from PyQt4 import QtGui, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.patches import Circle
class SelectablePoint:
def __init__(self, xy, label):
self.point = Circle( (xy[0], xy[0]), .005 )
self.label = label
self.point.figure
self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.onClick)
def onClick(self, e):
print e.xdata, e.ydata
#print(dir(self.point))
print self.point.center
print self.point.contains(e)[0]
#if self.point.contains(e)[0]:
# print self.label
class ScatterPlot(FigureCanvas):
'''
classdocs
'''
def __init__(self, parent=None):
'''
Constructor
'''
self.fig = Figure()
FigureCanvas.__init__(self, self.fig)
self.axes = self.fig.add_subplot(111)
#x = numpy.arange(0.0, 3.0, 0.1)
#y = numpy.cos(2*numpy.pi*x)
x = [.5]
y = [.5]
#scatterplot = self.axes.scatter(x,y)
for i in range(len(x)):
c = Circle( (x[i], y[i]), .05 )
self.axes.add_patch(c)
#SelectablePoint( (x[i],y[i]), 'label for: ' + str(i), self.figure.canvas )
SelectablePoint( (x[i],y[i]), 'label for: ' + str(i) )
#self.axes.add_artist(c)
class MainContainer(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(900,600)
self.setWindowTitle('Scatter Plot')
sp = ScatterPlot(self)
self.setCentralWidget(sp)
self.center()
def center(self):
# Get the resolution of the screen
screen = QtGui.QDesktopWidget().screenGeometry()
# Get the size of widget
size = self.geometry()
self.move( (screen.width() - size.width())/2, (screen.height() - size.height())/2 )
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
b = MainContainer()
b.show()
sys.exit(app.exec_())
我不确定我是否在正确的方向上,或者是否应该考虑其他绘图模块。我看过gnuplot和chaco,但我觉得matplotlib更适合我的问题。还有其他推荐吗?非常感谢。
** 我的解决方案 **
这是我目前能正常工作的部分。当散点图中的点被点击时,它会简单地将每个数据点的标签输出到标准输出。
#!/usr/bin/python -tt
import sys
import numpy
from PyQt4 import QtGui, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.patches import Circle
class SelectablePoint:
def __init__(self, xy, label, fig):
self.point = Circle( (xy[0], xy[1]), .25, figure=fig)
self.label = label
self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.onClick)
def onClick(self, e):
if self.point.contains(e)[0]:
print self.label
class ScatterPlot(FigureCanvas):
'''
classdocs
'''
def __init__(self, parent=None):
'''
Constructor
'''
self.fig = Figure()
FigureCanvas.__init__(self, self.fig)
self.axes = self.fig.add_subplot(111)
xlim = [0,7]
ylim = [0,7]
self.axes.set_xlim(xlim)
self.axes.set_ylim(ylim)
self.axes.set_aspect( 1 )
x = [1, 1.2, 3, 4, 5, 6]
y = [1, 1.2, 3, 4, 5, 6]
labels = ['1', '2', '3', '4', '5', '6']
for i in range(len(x)):
sp = SelectablePoint( (x[i],y[i]), labels[i], self.fig)
self.axes.add_artist(sp.point)
class MainContainer(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(900,600)
self.setWindowTitle('Scatter Plot')
sp = ScatterPlot(self)
self.setCentralWidget(sp)
self.center()
def center(self):
# Get the resolution of the screen
screen = QtGui.QDesktopWidget().screenGeometry()
# Get the size of widget
size = self.geometry()
self.move( (screen.width() - size.width())/2, (screen.height() - size.height())/2 )
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
b = MainContainer()
b.show()
sys.exit(app.exec_())
2 个回答
0
我对你的代码很感兴趣,但在这个可选择点的更改中,我没有捕捉到事件。这样做更好吗?现在点击整个屏幕区域,而不仅仅是在圆圈内。所以我可能理解错了?你为什么要创建两个圆圈?我虽然从可选择点中提取了它,但在我给你展示的点击事件更改之前,它并没有捕捉到任何事件。有什么原因吗?
import sys
import numpy
from PyQt4 import QtGui, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.patches import Circle
class SelectablePoint:
def __init__(self, xy, label, fig):
self.point = Circle( (xy[0], xy[1]), .25, figure=fig)
self.label = label
self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', onClick)
def onClick(e):
print e.__dict__
class ScatterPlot(FigureCanvas):
'''
classdocs
'''
def __init__(self, parent=None):
'''
Constructor
'''
self.fig = Figure()
FigureCanvas.__init__(self, self.fig)
self.axes = self.fig.add_subplot(111)
xlim = [0,7]
ylim = [0,7]
self.axes.set_xlim(xlim)
self.axes.set_ylim(ylim)
self.axes.set_aspect( 1 )
x = [1, 1.2, 3, 4, 5, 6]
y = [1, 1.2, 3, 4, 5, 6]
labels = ['1', '2', '3', '4', '5', '6']
for i in range(len(x)):
sp = SelectablePoint( (x[i],y[i]), labels[i], self.fig)
self.axes.add_artist(sp.point)
class MainContainer(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(900,600)
self.setWindowTitle('Scatter Plot')
sp = ScatterPlot(self)
self.setCentralWidget(sp)
self.center()
def center(self):
# Get the resolution of the screen
screen = QtGui.QDesktopWidget().screenGeometry()
# Get the size of widget
size = self.geometry()
self.move( (screen.width() - size.width())/2, (screen.height() - size.height())/2 )
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
b = MainContainer()
b.show()
sys.exit(app.exec_())
3
哎呀,我在为每个数据点创建了两个 patches.Circle 的实例。把第一个实例传给我的 SelectablePoint 类后,一切就正常了。