python - 如何使用 blit 有效地重绘多个 matplotlib 图

标签 python matplotlib pyqt5

我使用 matplotlib 和 pyqt5 将数据绘制到 3 个轴中,并且用户可以在一个图中进行选择,该选择也将在其他两个图中显示。由于我正在处理大数据(高达 1000 万个点),绘图选择可能会很慢,尤其是当我需要绘制散点图时。

我正在尝试使用 matplotlib blit 函数,但结果有一些问题。这是最简单的示例。

import matplotlib
matplotlib.use('Qt5Agg')
import numpy as np
import sys

from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure


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

        self.static_canvas = FigureCanvas(Figure(figsize=(10, 10)))
        layout.addWidget(self.static_canvas)
        layout.addWidget(NavigationToolbar(self.static_canvas, self))
        axes = self.static_canvas.figure.subplots(2, 1)
        self.ax1 = axes[0]
        self.ax2 = axes[1]
        self.ax1.cla()
        self.ax2.cla()

        button = QtWidgets.QPushButton('Click me!')
        button.clicked.connect(self.update_canvas_blit)
        layout.addWidget(button)
        # Fixing random state for reproducibility
        np.random.seed(19680801)

        # Create random data
        N = 50000
        x = np.random.rand(N)
        y = np.random.rand(N)

        self.ax1.scatter(x, y)
        self.points = self.ax1.scatter([],[], s=5, color='red')

        x = np.linspace(0, 1000, 100000)
        self.ax2.plot(x, np.sin(x))
        self.lines, = self.ax2.plot([],[], color='red')
        self.static_canvas.draw()

        self.background1 = self.static_canvas.copy_from_bbox(self.ax1.bbox)
        self.background2 = self.static_canvas.copy_from_bbox(self.ax2.bbox)

    def update_canvas_blit(self):
        N = 50
        x = np.random.rand(N)
        y = np.random.rand(N)

        self.static_canvas.restore_region(self.background1)
        self.points.set_offsets(np.c_[x,y])
        self.ax1.draw_artist(self.points)
        self.ax1.figure.canvas.blit(self.ax1.bbox)

        self.static_canvas.restore_region(self.background2)
        x = np.linspace(0, np.random.randint(500,1000), 1000)
        self.lines.set_data(x, np.sin(x))
        self.ax2.draw_artist(self.lines)
        self.ax2.figure.canvas.blit(self.ax2.bbox)

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

单击按钮时,预期输出应该仍然是相同的背景,并随机重绘点/线。在某种程度上,它正在发生,但有一些奇怪的工件,看起来像是轴以某种方式相互吸引。但是当我尝试将其保存为.png时,它会恢复到良好的状态。

blit_update

最佳答案

问题在于背景快照是在图形尚未显示在屏幕上的时刻拍摄的。此时,人物尺寸为 10 x 10 英寸。稍后,它会显示在 QMainWindow 内并调整大小以适合小部件。
只有当这种情况发生时,拍摄背景快照才有意义。

一种选择是使用 1 秒的计时器,然后才复制背景。这看起来如下。

import numpy as np
import sys

from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure


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

        self.static_canvas = FigureCanvas(Figure(figsize=(10, 10)))
        layout.addWidget(self.static_canvas)
        layout.addWidget(NavigationToolbar(self.static_canvas, self))
        axes = self.static_canvas.figure.subplots(2, 1)
        self.ax1 = axes[0]
        self.ax2 = axes[1]
        self.ax1.cla()
        self.ax2.cla()

        button = QtWidgets.QPushButton('Click me!')
        button.clicked.connect(self.update_canvas_blit)
        layout.addWidget(button)
        # Fixing random state for reproducibility
        np.random.seed(19680801)

        # Create random data
        N = 50000
        x = np.random.rand(N)
        y = np.random.rand(N)

        self.ax1.scatter(x, y)
        self.points = self.ax1.scatter([],[], s=5, color='red')

        x = np.linspace(0, 1000, 100000)
        self.ax2.plot(x, np.sin(x))
        self.lines, = self.ax2.plot([],[], color='red')
        self.static_canvas.draw()

        self._later()


    def _later(self, evt=None):
        self.timer = self.static_canvas.new_timer(interval=1000)
        self.timer.single_shot = True
        self.timer.add_callback(self.update_background)
        self.timer.start()


    def update_background(self, evt=None):
        self.background1 = self.static_canvas.copy_from_bbox(self.ax1.bbox)
        self.background2 = self.static_canvas.copy_from_bbox(self.ax2.bbox)

    def update_canvas_blit(self):
        N = 50
        x = np.random.rand(N)
        y = np.random.rand(N)

        self.static_canvas.restore_region(self.background1)
        self.points.set_offsets(np.c_[x,y])
        self.ax1.draw_artist(self.points)
        self.ax1.figure.canvas.blit(self.ax1.bbox)

        self.static_canvas.restore_region(self.background2)
        x = np.linspace(0, np.random.randint(500,1000), 1000)
        self.lines.set_data(x, np.sin(x))
        self.ax2.draw_artist(self.lines)
        self.ax2.figure.canvas.blit(self.ax2.bbox)

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

关于python - 如何使用 blit 有效地重绘多个 matplotlib 图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57303228/

相关文章:

python PDFminer 只解析部分页面

python - 如何在matplotlib中画一个没有填充的圆

python - 如何在 PyQt5 中更改 QButtonGroup 的颜色属性?

python - 使用指标 'acc' 和 tf.keras.metrics.Accuracy() 有什么区别

python - 两个 3-D 数组上的 numpy.maximum()

python - 聚类多个分类列以在 matplotlib 中制作时间序列线图

python - 如何调整子图的高度和宽度以紧密配合?

python - 为 QPolygonItem 着色

css - QTableView 小部件的 Qt 样式表中有哪些可用选项?

使用大量参数编写函数/方法的 Pythonic 方式