[英]Python Matplotlib: reduce render time for interactive plot
I've got the following code that produces a plot that can interactively be modified.我有以下代码可以生成一个可以交互修改的图。 Clicking / holding the left mouse button sets the marker position, Holding the right button and moving the mouse moves the plotted data in direction x and using the mouse wheel zooms in/out.
单击/按住鼠标左键设置标记位置,按住右键并移动鼠标将绘制的数据沿 x 方向移动,并使用鼠标滚轮放大/缩小。 Additionally, resizing the window calls
figure.tight_layout()
so that the size of the axes is adapted to the window size.此外,调整窗口大小调用
figure.tight_layout()
以便轴的大小适应窗口大小。
# coding=utf-8
from __future__ import division
from Tkinter import *
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from numpy import arange, sin, pi
matplotlib.use('TkAgg')
class PlotFrame(Frame):
def __init__(self, master, **ops):
Frame.__init__(self, master, **ops)
self.figure = Figure()
self.axes_main = self.figure.add_subplot(111)
for i in range(10):
t = arange(0, 300, 0.01)
s = sin(0.02 * pi * (t + 10 * i))
self.axes_main.plot(t, s)
self.plot = FigureCanvasTkAgg(self.figure, master=self)
self.plot.show()
self.plot.get_tk_widget().pack(fill=BOTH, expand=1)
self.dragging = False
self.dragging_button = None
self.mouse_pos = [0, 0]
self.marker = self.figure.axes[0].plot((0, 0), (-1, 1), 'black', linewidth=3)[0]
self.plot.mpl_connect('button_press_event', self.on_button_press)
self.plot.mpl_connect('button_release_event', self.on_button_release)
self.plot.mpl_connect('motion_notify_event', self.on_mouse_move)
self.plot.mpl_connect('scroll_event', self.on_mouse_scroll)
self.plot.mpl_connect("resize_event", self.on_resize)
def on_resize(self, _):
self.figure.tight_layout()
def axes_size(self):
pos = self.axes_main.get_position()
bbox = self.figure.get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
width, height = bbox.width * self.figure.dpi, bbox.height * self.figure.dpi
axis_size = [(pos.x1 - pos.x0) * width, (pos.y1 - pos.y0) * height]
return axis_size
def on_button_press(self, event):
# right mouse button clicked
if not self.dragging and event.button in (1, 3):
self.dragging = True
self.dragging_button = event.button
self.mouse_pos = [event.x, event.y]
# left mouse button clicked
if event.button == 1 and event.xdata is not None:
self.move_marker(event.xdata)
def on_button_release(self, event):
if self.dragging and self.dragging_button == event.button:
self.dragging = False
def on_mouse_move(self, event):
if self.dragging and self.dragging_button == 3:
dx = event.x - self.mouse_pos[0]
self.mouse_pos = [event.x, event.y]
x_min, x_max = self.figure.axes[0].get_xlim()
x_range = x_max - x_min
x_factor = x_range / self.axes_size()[0]
self.figure.axes[0].set_xlim([x_min - dx * x_factor, x_max - dx * x_factor])
self.plot.draw()
elif self.dragging and self.dragging_button == 1:
self.move_marker(event.xdata)
def on_mouse_scroll(self, event):
if event.xdata is None:
return
zoom_direction = -1 if event.button == 'up' else 1
zoom_factor = 1 + .4 * zoom_direction
x_min, x_max = self.figure.axes[0].get_xlim()
min = event.xdata + (x_min - event.xdata) * zoom_factor
max = event.xdata + (x_max - event.xdata) * zoom_factor
self.figure.axes[0].set_xlim([min, max])
self.plot.draw()
def move_marker(self, x_position):
y_min, y_max = self.figure.axes[0].get_ylim()
self.marker.set_data((x_position, x_position), (y_min, y_max))
self.plot.draw()
if __name__ == '__main__':
gui = Tk()
vf = PlotFrame(gui)
vf.pack(fill=BOTH, expand=1)
gui.mainloop()
The implementation works fine, but rendering is really slow when displaying a lot of lines.实现工作正常,但在显示很多行时渲染真的很慢。 How can I make rendering faster?
如何使渲染速度更快? As you can see in the implementation above, the whole plot is drawn completely every time anything changes which shouldn't be necessary.
正如您在上面的实现中看到的那样,每次发生不必要的更改时,都会完全绘制整个图。 My thoughts on this:
我对此的看法:
Drawing everything when resizing/zooming is fine for me, but I really need faster drawing of the latter two modifications.调整大小/缩放时绘制所有内容对我来说很好,但我真的需要更快地绘制后两个修改。 I already looked into matplotlib's animations, but as far as I understood, they won't help in my case.
我已经研究过 matplotlib 的动画,但据我所知,它们对我的情况没有帮助。 Any help is greatly appreciated, thanks!
非常感谢任何帮助,谢谢!
The solution seems to be to cache elements that get redrawn as you said:解决方案似乎是缓存像你说的那样重绘的元素:
One major thing that gets redrawn is the background:重绘的一件主要事情是背景:
# cache the background
background = fig.canvas.copy_from_bbox(ax.bbox)
After caching restore it using restore region
then just re-draw the points/line at every call you need缓存后使用
restore region
恢复它然后只需在您需要的每次调用时重新绘制点/线
# restore background
fig.canvas.restore_region(background)
# redraw just the points
ax.draw_artist(points)
# fill in the axes rectangle
fig.canvas.blit(ax.bbox)
To optimize drawing blitting can be used.可以使用图形块来优化绘图。 With it only given artists (those that were changed) will be rendered instead of the whole figure.
有了它,只有给定的艺术家(那些被改变的)才会被渲染,而不是整个人物。
Motplotlib uses that technique internally in the animation module. Motplotlib 在动画模块内部使用该技术。 You can use
Animation
class in it as a reference to implement the same behaviour in your code.您可以使用其中的
Animation
类作为参考,以在您的代码中实现相同的行为。 Look at the _blit_draw()
and several related functions after it in the sources.查看源代码中的
_blit_draw()
及其后的几个相关函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.