首页 > 解决方案 > 如何让 matplotlib.Figure() 接受 Drop 事件来绘制数据?

问题描述

我正在设置一个启用拖放模式的QListWidget并使用一些数据的名称,我想将这些名称拖放到matplotlib.figure然后绘制它。

如何使 Figure() 接受 Drop 事件?

我正在使用库PyQt5 和 Matplotlib。

class Window(QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        # ...

        self.parametersList = QListWidget()
        self.parametersList.setDragDropMode(QAbstractItemView.DragDrop)
        self.parametersList.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.parametersList.setAcceptDrops(True)


        # Right side of the screen
        self.graphFigure = Figure()
        self.graphCanvas = FigureCanvas(self.graphFigure)

        self.toolbar = NavigationToolbar(self.graphCanvas, self)

        self.ax = self.graphFigure.add_subplot(111)
        self.ax.tick_params(axis='y', labelcolor='tab:red')

    def enterDrop (self, data):
    ''' Something to pass data via Drop '''
        self.ax.plot(data)

标签: pythonmatplotlibpyqtpyqt5

解决方案


FigureCanvas 是一个 QWidget,因此您必须实现拖放的 dragEnterEvent 和 dropEvent 方法。

import random
import string
from PyQt5 import QtCore, QtGui, QtWidgets

from matplotlib.backends.backend_qt5agg import (
    FigureCanvasQTAgg as FigureCanvas,
    NavigationToolbar2QT as NavigationToolbar,
)
from matplotlib.figure import Figure

import numpy as np


def random_string(string_length=10):
    letter = string.ascii_lowercase
    return "".join(random.sample(letter, string_length))


class DropCanvas(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        FigureCanvas.__init__(self, fig)
        self.setParent(parent)
        self.setAcceptDrops(True)
        self.axes = self.figure.add_subplot(111)
        self.axes.tick_params(axis="y", labelcolor="tab:red")
        t = np.arange(0.0, 5.0, 0.01)
        y = 100 * np.exp(-t) * np.cos(2 * np.pi * t)
        self.axes.plot(t, y)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat(
            "application/x-qabstractitemmodeldatalist"
        ):
            event.acceptProposedAction()

    def dropEvent(self, event):
        if (
            isinstance(event.source(), QtWidgets.QListView)
            and event.possibleActions() & QtCore.Qt.CopyAction
        ):
            monkey_model = QtGui.QStandardItemModel()
            if monkey_model.dropMimeData(
                event.mimeData(),
                QtCore.Qt.CopyAction,
                0,
                0,
                QtCore.QModelIndex(),
            ):
                if (
                    monkey_model.rowCount() >= 1
                    and monkey_model.columnCount() >= 1
                ):
                    it = monkey_model.item(0, 0)
                    # get text
                    text = it.text()
                    p = event.pos()
                    width, height = self.get_width_height()
                    # In matplotlib, 0,0 is the lower left corner, 
                    # whereas it's usually the upper
                    # right for most image software, so we'll flip the y-coords
                    x_pixel, y_pixel = p.x(), height - p.y()
                    x, y = self.axes.transData.inverted().transform(
                        (x_pixel, y_pixel)
                    )
                    self.axes.scatter(x, y)
                    self.axes.text(x, y, text, fontsize=12)
                    self.axes.figure.canvas.draw()
                    event.acceptProposedAction()


class Dialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)

        list_widget = QtWidgets.QListWidget(
            dragDropMode=QtWidgets.QAbstractItemView.DragOnly
        )
        for _ in range(10):
            text = random_string()
            it = QtWidgets.QListWidgetItem(text)
            list_widget.addItem(text)

        canvas = DropCanvas()
        toolbar = NavigationToolbar(canvas, self)

        hlay = QtWidgets.QHBoxLayout(self)
        vlay = QtWidgets.QVBoxLayout()

        hlay.addWidget(list_widget)
        hlay.addLayout(vlay)
        vlay.addWidget(toolbar)
        vlay.addWidget(canvas)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Dialog()
    w.show()
    sys.exit(app.exec_())

推荐阅读