首页 > 解决方案 > Matplotlib:在绘图中插入图像并能够用鼠标拖动它

问题描述

我写了一个脚本,灵感来自这里的可拖动矩形练习,它让我可以绘制多边形,目前是矩形和三角形,可以用鼠标拖动。

这是代码:

import numpy as np
import matplotlib.pyplot as plt
from abc import ABCMeta, abstractmethod
import logging

#
# Create and set logger
#

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)


class Polygon:
    __metaclass__ = ABCMeta

    _lock = None  # only one can be animated at a time

    @abstractmethod
    def compute_edges(self):
        raise NotImplementedError("Implemented in child classes")

    def __init__(self, center, height, width, orientation, id_, type_,
                 **kwargs):
        self.center = center
        self.height = height
        self.width = width

        if not "radian" in orientation:
            orientation["radian"] = orientation["degree"] / 180. * np.pi

        self.orientation = orientation
        self.polygon = plt.Polygon(self.compute_edges(), **kwargs)
        self.press = None
        self.background = None
        self.id_ = id_
        self.type_ = type_

    def connect(self):
        'connect to all the events we need'
        self.cidpress = self.polygon.figure.canvas.mpl_connect(
            'button_press_event', self.on_press)
        self.cidrelease = self.polygon.figure.canvas.mpl_connect(
            'button_release_event', self.on_release)
        self.cidmotion = self.polygon.figure.canvas.mpl_connect(
            'motion_notify_event', self.on_motion)
        self.cidscroll = self.polygon.figure.canvas.mpl_connect(
            'scroll_event', self.on_scroll)

    def on_press(self, event):
        'on button press we will see if the mouse is over us and store some data'
        if event.inaxes != self.polygon.axes:
            return
        if Polygon._lock is not None:
            return

        contains, attrd = self.polygon.contains(event)
        if not contains:
            return

        self.press = np.copy(self.polygon.xy), event.xdata, event.ydata
        Polygon._lock = self

        logger.debug("Pressed polygon (%s, %d) at (%f, %f)", self.type_,
                     self.id_, event.xdata, event.ydata)

        # draw everything but the selected polygon and store the pixel buffer
        canvas = self.polygon.figure.canvas
        axes = self.polygon.axes
        self.polygon.set_animated(True)
        canvas.draw()
        self.background = canvas.copy_from_bbox(self.polygon.axes.bbox)

        # now redraw just the polygon
        axes.draw_artist(self.polygon)

        # and blit just the redrawn area
        canvas.blit(axes.bbox)

    def on_scroll(self, event):
        if event.inaxes != self.polygon.axes:
            return
        if Polygon._lock is not None:
            return
        contains, attrd = self.polygon.contains(event)
        if not contains:
            return

    def on_motion(self, event):
        'on motion we will move the rect if the mouse is over us'
        if Polygon._lock is not self:
            return
        if event.inaxes != self.polygon.axes:
            return

        xy, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = event.ydata - ypress
        self.polygon.xy[:, 0] = xy[:, 0] + dx
        self.polygon.xy[:, 1] = xy[:, 1] + dy
        self.center = self.polygon.xy.mean(axis=0)

        logger.debug("Move polygon (%s, %d) to (%f, %f)", self.type_, self.id_,
                     self.center[0], self.center[1])

        canvas = self.polygon.figure.canvas
        axes = self.polygon.axes
        # restore the background region
        canvas.restore_region(self.background)

        # redraw just the current polygon
        axes.draw_artist(self.polygon)

        # blit just the redrawn area
        canvas.blit(axes.bbox)

        #plt.gcf().savefig("test.png")

    def on_release(self, event):
        'on release we reset the press data'
        if Polygon._lock is not self:
            return

        self.press = None
        Polygon._lock = None

        logger.debug("Release polygon (%s, %d) at (%f, %f)", self.type_,
                     self.id_, self.center[0], self.center[1])

        # turn off the polygon animation property and reset the background
        self.polygon.set_animated(False)
        self.background = None

        # redraw the full figure
        self.polygon.figure.canvas.draw()

    def disconnect(self):
        'disconnect all the stored connection ids'
        self.polygon.figure.canvas.mpl_disconnect(self.cidpress)
        self.polygon.figure.canvas.mpl_disconnect(self.cidrelease)
        self.polygon.figure.canvas.mpl_disconnect(self.cidmotion)
        self.polygon.figure.canvas.mpl_disconnect(self.cidscroll)


class Triangle(Polygon):
    _id = 0

    def compute_edges(self):
        self.orientation["cos"] = np.cos(self.orientation["radian"])
        self.orientation["sin"] = np.sin(self.orientation["radian"])

        return np.array(
            [[
                self.center[0] + 2 * self.height * self.orientation["cos"] / 3,
                self.center[1] + 2 * self.height * self.orientation["sin"] / 3
            ],
             [
                 self.center[0] - self.width * 0.5 * self.orientation["sin"] -
                 self.height / 3. * self.orientation["cos"],
                 self.center[1] + self.width * 0.5 * self.orientation["cos"] -
                 self.height / 3. * self.orientation["sin"]
             ],
             [
                 self.center[0] + self.width * 0.5 * self.orientation["sin"] -
                 self.height / 3. * self.orientation["cos"],
                 self.center[1] - self.width * 0.5 * self.orientation["cos"] -
                 self.height / 3. * self.orientation["sin"]
             ]])

    def __init__(self, center, height, width, orientation, **kwargs):
        Polygon.__init__(self, center, height, width, orientation,
                         Triangle._id, "target", **kwargs)
        Triangle._id += 1


class Rectangle(Polygon):
    _id = 0

    def compute_edges(self):
        self.orientation["cos"] = np.cos(self.orientation["radian"])
        self.orientation["sin"] = np.sin(self.orientation["radian"])

        return np.array(
            [[
                self.center[0] + 0.5 * self.height * self.orientation["cos"] +
                0.5 * self.width * self.orientation["sin"],
                self.center[1] + 0.5 * self.height * self.orientation["sin"] -
                0.5 * self.width * self.orientation["cos"]
            ],
             [
                 self.center[0] + 0.5 * self.height * self.orientation["cos"] -
                 0.5 * self.width * self.orientation["sin"],
                 self.center[1] + 0.5 * self.height * self.orientation["sin"] +
                 0.5 * self.width * self.orientation["cos"]
             ],
             [
                 self.center[0] - 0.5 * self.height * self.orientation["cos"] -
                 0.5 * self.width * self.orientation["sin"],
                 self.center[1] - 0.5 * self.height * self.orientation["sin"] +
                 0.5 * self.width * self.orientation["cos"]
             ],
             [
                 self.center[0] - 0.5 * self.height * self.orientation["cos"] +
                 0.5 * self.width * self.orientation["sin"],
                 self.center[1] - 0.5 * self.height * self.orientation["sin"] -
                 0.5 * self.width * self.orientation["cos"]
             ]])

    def __init__(self, center, height, width, orientation, **kwargs):
        Polygon.__init__(self, center, height, width, orientation,
                         Rectangle._id, "launcher", **kwargs)
        Rectangle._id += 1


fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlim(xmax=20)
ax.set_ylim(ymax=20)
ax.set_aspect("equal")

polygons = [
    Triangle([2, 6], 2, 1, {"degree": 30.}, color="red"),
    Triangle([6, 3], 2, 1, {"degree": 200.}, color="red"),
    Rectangle([2, 2], 2, 1, {"degree": 60.}, color="blue"),
    Rectangle([10, 2], 2, 1, {"degree": 60.}, color="blue")
]

for pol in polygons:
    ax.add_patch(pol.polygon)
    pol.connect()

plt.show()

我想做同样的事情,但使用图像(png、jpg 或 svg),也就是说用文件中的图像替换多边形。

我在 matplotlib 或与此相关的其他地方找不到任何示例。

你认为有可能吗?我该怎么办?

我想我首先必须将图像加载到一个补丁中,然后将其添加到绘图轴,但目前尚不清楚如何拖动这个可能与多边形属性不同的补丁。

问候

标签: matplotlib

解决方案


推荐阅读