【问题标题】:PyQt4 Scatterplot with Clickable and Selectable Points带有可点击和可选择点的 PyQt4 散点图
【发布时间】:2011-06-30 16:04:25
【问题描述】:

我正在尝试创建一个包含大约 1000 个数据点的散点图,以便可以通过使用鼠标单击它们来选择每个数据点,这将导致弹出一个上下文菜单,允许用户删除或更改数据点的颜色。我一直在关注 matplotlib 的教程,并查看了 Draggable Rectangle 练习,但我遇到了困难。我正在使用 matplotlib.patches.Circle 类来表示每个数据点,但我无法让 'contains' 方法与 'button_press_event' 一起正常工作。主要是,似乎没有与每个 Circle 对象关联的“画布”对象。我在第 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_())

【问题讨论】:

    标签: python matplotlib pyqt4 clickable scatter-plot


    【解决方案1】:

    糟糕,我正在为每个数据点创建两个 patch.Circle 实例。将第一个实例传递给我的 SelectablePoint 类后,一切正常。

    【讨论】:

    • 您也可以在此答案中发布正确的代码。你知道,为了科学:)
    • 这是我目前所拥有的。我还没有实现上下文菜单和更改颜色以及所有这些东西,但是当您单击散点图的点时,此代码会将每个数据点的标签输出到标准输出。
    【解决方案2】:

    我对您的代码很感兴趣,但我确实通过 SelectablePoint 中的此更改获得了事件。 这是否更好 ?好吧,它现在单击整个屏幕区域,而不仅仅是在圆圈中。所以我可能错过了重点?为什么你创建了 Circle 两次?虽然我从 SelectablePoint 中将其成熟,但在我向您展示的 onClick 更改之前它没有捕获任何事件。 任何情况下为什么?

    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_())
    

    【讨论】:

    • 嗨,我在“我的解决方案”下发布的代码实际上对我有用。我意识到我正在创建两个圆实例,但我修复了它,之后它运行良好。
    • 你这样做是为了改变吗?:def __init__(self, xy, label, fig) 到 def __init__(self, c, label) ?为了传递对象Circle?如果你这样做,它对我不起作用...... python 2.6 和 python 2.7
    • 你试过我在上面“**我的解决方案**”下发布的代码吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 2013-08-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多