Python 如何通过回调更新PyQt中的散点图?

Python 如何通过回调更新PyQt中的散点图?,python,matplotlib,pyqt,pyqt5,Python,Matplotlib,Pyqt,Pyqt5,我有一个程序,当用户点击一个按钮时,我想用用户提供的新数据更新之前绘制的图。我要做的是向用户展示分类器系统的决策边界图,当用户添加新数据时,我想相应地更新散点图。这是我的密码: from matplotlib.backends.backend_qt5agg import ( FigureCanvasQTAgg, FigureManagerQT, ) from PyQt5 import QtWidgets import matplotlib.pyplot as plt from m

我有一个程序,当用户点击一个按钮时,我想用用户提供的新数据更新之前绘制的图。我要做的是向用户展示分类器系统的决策边界图,当用户添加新数据时,我想相应地更新散点图。这是我的密码:

from matplotlib.backends.backend_qt5agg import (
    FigureCanvasQTAgg,
    FigureManagerQT,
)
from PyQt5 import QtWidgets
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np
class CustomFigureCanvas(FigureCanvasQTAgg):
    def __init__(self, parent=None, cmap_name="coolwarm"):
        fig = Figure()
        self.color_map = plt.get_cmap(cmap_name)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)
        self.setParent(parent)
        self.setBaseSize(300, 300)
        self.setMaximumSize(400, 400)
        self.setMinimumSize(250, 250)
        self.setSizePolicy(
            QtWidgets.QSizePolicy.MinimumExpanding,
            QtWidgets.QSizePolicy.MinimumExpanding,
        )

    def set_clf_2d(self, clf_2d):
        self.clf = clf_2d

    def plot_new_datapoints(self, x2D):
        self.add_datapoint(x2D)

    @staticmethod
    def _make_meshgrid(x, y, h=0.02):
        x_min, x_max = x.min() - 1, x.max() + 1
        y_min, y_max = y.min() - 1, y.max() + 1
        XX, YY = np.meshgrid(
            np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)
        )
        return XX, YY

    def _plot_contours(self, xx, yy, **params):
        """Plot the decision boundaries for a classifier.

        Parameters
        ----------
        ax: matplotlib axes object
        clf: a classifier
        xx: meshgrid ndarray
        yy: meshgrid ndarray
        params: dictionary of params to pass to contourf, optional
        """
        Z = self.clf.predict(np.c_[xx.ravel(), yy.ravel()])
        Z = Z.reshape(xx.shape)
        self.axes.contourf(xx, yy, Z, **params)

    def plot_data(self, x2D, y):
        """plots the given array and the decision function bounday.

        Arguments:
            x2D {np.array} -- [2d array]
            y {np.array} -- [1d array]
        """

        x0, x1 = x2D[:, 0], x2D[:, 1]
        xx, yy = CustomFigureCanvas._make_meshgrid(x0, x1)
        labels = ["Cognitive", "Not Cognitive"]
        colors = ["r", "b"]
        self.axes.clear()
        self._plot_contours(xx, yy, cmap=self.color_map, alpha=0.8)
        target_ids = [0, 1]

        for i, c, label in zip(target_ids, colors, labels):
            print(i, label)
            self.axes.scatter(
                x0[y == i],
                x1[y == i],
                color=c,
                label=label,
                marker="o",
                s=(15, 15),
            )

        self.axes.set_xlim(xx.min(), xx.max())
        self.axes.set_ylim(yy.min(), yy.max())
        self.axes.set_title("2D Representation using PCA")
        self.axes.legend(fontsize=8)
        self.axes.plot()

    def add_datapoint(self, x2d):
        """Adds a new datapoint to the plot

        Arguments:
            x2d {a 2d single point, [x,y]} -- [np.array with shape (1,2)]
            axes {plt.axes} -- [description]

        """
        print(x2d, type(x2d))
        self.axes.scatter(
            x2d[:, 0],
            x2d[:, 1],
            color="k",
            label="Current Text",
            marker="o",
            s=(15, 15),
        )
        self.axes.legend(fontsize=8)
        self.axes.plot()
我目前遇到的问题是,调用
\u plot\u contours
后,绘图将不会更新。在阅读matplotlib中的“可更新”图形之后,我看到一些建议使用
plt.ion()
生成可更新图形。还有一些关于使用
FuncAnimation
类的建议,但这并不是我需要的解决方案,因为它不依赖于用户单击按钮的回调,而是在给定的时间间隔内刷新绘图

编辑:这是一个最小的代码,再现了我遇到的问题:

import sys
from PyQt5 import QtWidgets

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

import random
import numpy as np
from sklearn.svm import SVC


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

        # a figure instance to plot on
        self.figure = Figure()

        # this is the Canvas Widget that displays the `figure`
        # it takes the `figure` instance as a parameter to __init__
        self.canvas = CustomFigureCanvas(parent=self)

        # this is the Navigation widget
        # it takes the Canvas widget and a parent
        self.toolbar = NavigationToolbar(self.canvas, self)

        # Just some button connected to `plot` method
        self.button = QtWidgets.QPushButton("Plot")
        self.button.clicked.connect(self.plot)

        # set the layout
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)
        layout.addWidget(self.button)
        self.setLayout(layout)

    def plot(self):

        x2D = np.random.rand(50, 2)
        y = np.random.randint(0, 2, size=(50,))

        x2D_train = np.random.rand(50, 2)
        y_train = np.random.randint(0, 2, size=(50,))

        clf = SVC()
        clf.fit(x2D_train, y_train)

        print(x2D)
        self.canvas.set_clf_2d(clf)
        self.canvas.plot_data(x2D, y)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    main = Window()
    main.show()

    sys.exit(app.exec_())

我不能确切地指出添加新数据点必须发生在哪里,因为您的代码远远不是最小的,但是这里有一个简单的例子,在qt应用程序中向散点图添加新点(尽管实际上这并不重要)


对于带有Qt的matplotlib,必须刷新绘制,为此,可以使用以下方法:

self.axes.figure.canvas.draw_idle()

self.axes.figure.canvas.draw()
就你而言:

#。。。
定义图轮廓(自、xx、yy、**参数):
# ...
自轴轮廓f(xx,yy,Z,**参数)
self.axes.figure.canvas.draw()文件
def绘图数据(自身、x2D、y):
# ...
self.axes.plot()
self.axes.figure.canvas.draw()文件
# ...
输出:


@eyllanesc我添加了一个重现我的问题的代码。我有两种方法
plot\u data
add\u datapoints
,其中我添加了散点图。在第一种方法中,我首先通过生成网格网格绘制决策边界,然后调用
\u plot\u contour
函数,然后尝试在其顶部添加散点图。很抱歉,如果它不是最小的,但我无法进一步修剪我的代码,因为这是我能想到的最小的。谢谢!另一方面,我很好奇为什么会发生这种情况。qt是否冻结绘图以显示它?
import sys
import numpy as np
from matplotlib.backends.backend_qt5agg import \
    (FigureCanvasQTAgg as FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
from PyQt5 import QtCore, QtWidgets


class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        layout = QtWidgets.QVBoxLayout(self._main)

        self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
        layout.addWidget(self.canvas)
        self.addToolBar(QtCore.Qt.BottomToolBarArea,
                        NavigationToolbar(self.canvas, self))

        self.ax = self.canvas.figure.subplots()
        self.scat = self.ax.scatter([], [], marker='o', color='red', s=100)
        self.ax.set_xlim([0, 1])
        self.ax.set_ylim([0, 1])

        self.button = QtWidgets.QPushButton("Add point")
        self.button.clicked.connect(self.addPoint)
        layout.addWidget(self.button)

    def addPoint(self):
        x, y = np.random.random(size=(2,))
        old_data = self.scat.get_offsets()
        data = np.append(old_data, [[x, y]], axis=0)
        self.scat.set_offsets(data)
        self.canvas.draw_idle()


if __name__ == "__main__":
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    qapp.exec_()