简体   繁体   English

Python Matplotlib:减少交互式绘图的渲染时间

[英]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:我对此的看法:

  • Resizing the window: draw everything调整窗口大小:绘制所有内容
  • Zooming: draw everything缩放:绘制一切
  • Moving the marker: just redraw the marker (one line) instead of drawing everything移动标记:只需重新绘制标记(一行)而不是绘制所有内容
  • Moving the plot in x direction: move the pixels currently displayed in the plot left/right and only draw pixels that are moved into the visible area在 x 方向移动绘图:将绘图中当前显示的像素向左/向右移动,只绘制移动到可见区域的像素

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM