python - 如何使用 blit 有效地重绘多个 matplotlib 图
问题描述
我正在使用 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 时,它会恢复到良好状态。
解决方案
问题是背景快照是在人物尚未在屏幕上显示的时刻拍摄的。那时,这个数字是 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 - 提取 grib 文件的 Lon 和 Lat 范围
- sql-server - SQL Server:Tablock,在选择之前还是之后锁定?
- python - 获取 matplotlib 绘制 x & y 轴数据
- arrays - 当 Cypher 中的元素大于零时,如何计算数组的元素?
- go - 为什么这个恐慌示例在 golang 中的类型错误下方?
- python - Django:DeleteView + HttpResponseNotAllowed
- php - 我有 Swift 面板(游戏面板),我需要启用 cron 作业
- android - 为 Twitter、Paypal 和 Linkedin 使用 AppAuth 登录
- microcontroller - 使用 pic10f222 内部绝对电压参考
- macos - 如何使用 Mac OS X 辅助功能 API 检索活动窗口 URL