【问题标题】:Matplotlib draggable data markerMatplotlib 可拖动数据标记
【发布时间】:2016-11-07 01:23:06
【问题描述】:

我需要一个可拖动的数据标记来手动调整数据集中的一组点。 This answer 和其他人使用 Patch.Circle 解决了这个问题。但是,在缩放绘图时,我需要标记保持相同的大小。随着圆圈,圆圈也会缩放。我还没有尝试找到一种在每次新缩放时动态调整圆圈大小的方法,因为它看起来应该有一种更简单的方法来移动标记。有人知道怎么做吗?

【问题讨论】:

  • 您希望补丁的位置在数据坐标中,但补丁的大小在轴坐标中?

标签: python matplotlib


【解决方案1】:

下面,我展示了一个 MWE,它展示了一种使用 matplotlib 和 PyQt4 的面向对象 API 的方法。数据标记可使用鼠标中键拖动。该策略是使用 line2D 艺术家绘制数据,然后通过操纵艺术家的数据并更新绘图来拖动标记。大致可以概括为3个步骤:

第 1 步 - 单击鼠标中键时,鼠标光标的坐标(以像素为单位)与 line2D 艺术家的 xy 数据(以像素为单位)进行比较。如果光标与最近标记之间的线性距离小于给定值(此处定义为标记的大小),则该数据标记的索引将保存在类属性draggable 中。否则,draggable 设置为 None

第二步 - 当鼠标移动时,如果draggable is not None,则将其索引存储在类属性draggable中的数据标记的坐标设置为鼠标光标的坐标.

第 3 步 - 当释放鼠标中键时,draggable 被设置回无。

注意:如果需要,也可以使用 pyplot 接口而不是面向对象的 API 来生成图形。

import sys
from PyQt4 import QtGui
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt4agg import FigureManagerQT
import numpy as np


class MyFigureCanvas(FigureCanvasQTAgg):
    def __init__(self):
        super(MyFigureCanvas, self).__init__(Figure())
        # init class attributes:
        self.background = None
        self.draggable = None
        self.msize = 6
        # plot some data:
        x = np.random.rand(25)
        self.ax = self.figure.add_subplot(111)
        self.markers, = self.ax.plot(x, marker='o', ms=self.msize)
        # define event connections:
        self.mpl_connect('motion_notify_event', self.on_motion)
        self.mpl_connect('button_press_event', self.on_click)
        self.mpl_connect('button_release_event', self.on_release)

    def on_click(self, event):
        if event.button == 2:  # 2 is for middle mouse button
            # get mouse cursor coordinates in pixels:
            x = event.x
            y = event.y
            # get markers xy coordinate in pixels:
            xydata = self.ax.transData.transform(self.markers.get_xydata())
            xdata, ydata = xydata.T
            # compute the linear distance between the markers and the cursor:
            r = ((xdata - x)**2 + (ydata - y)**2)**0.5
            if np.min(r) < self.msize:
                # save figure background:
                self.markers.set_visible(False)
                self.draw()
                self.background = self.copy_from_bbox(self.ax.bbox)
                self.markers.set_visible(True)
                self.ax.draw_artist(self.markers)
                self.update()
                # store index of draggable marker:
                self.draggable = np.argmin(r)
            else:
                self.draggable = None

    def on_motion(self, event):
        if self.draggable is not None:
            if event.xdata and event.ydata:
                # get markers coordinate in data units:
                xdata, ydata = self.markers.get_data()
                # change the coordinate of the marker that is
                # being dragged to the ones of the mouse cursor:
                xdata[self.draggable] = event.xdata
                ydata[self.draggable] = event.ydata
                # update the data of the artist:
                self.markers.set_xdata(xdata)
                self.markers.set_ydata(ydata)
                # update the plot:
                self.restore_region(self.background)
                self.ax.draw_artist(self.markers)
                self.update()

    def on_release(self, event):
        self.draggable = None

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)

    canvas = MyFigureCanvas()
    manager = FigureManagerQT(canvas, 1)
    manager.show()

    sys.exit(app.exec_())

【讨论】:

  • 我想知道在像素坐标中计算 on_click() 是否比在数据坐标中计算它有任何优势(如在 on_motion() 中所做的那样)。在 on_click() 中使用数据坐标不需要将数据坐标转换为像素坐标。无论如何,出于教育目的,看看这两种用途都很不错:-)
  • @martinako 像素坐标允许计算与 x 和 y 轴比例无关的线性距离。否则我们将不得不转换 xy 数据,以考虑到两个轴不具有相同比例的事实,并在缩小和缩小时考虑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-28
  • 2016-08-11
  • 2016-10-05
相关资源
最近更新 更多