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

时间:2017-01-27 13:54:21

标签: python python-2.7 matplotlib plot interactive

我有以下代码生成可以交互式修改的绘图。单击/按住鼠标左键设置标记位置,按住右键并移动鼠标将绘制的数据沿x方向移动并使用鼠标滚轮放大/缩小。此外,调整窗口大小调用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()

实现工作正常,但在显示很多行时渲染非常慢。如何更快地渲染渲染?正如您在上面的实现中所看到的,每次发生任何不必要的更改时,都会完整绘制整个图。我对此的看法:

  • 调整窗口大小:绘制所有内容
  • 缩放:画出一切
  • 移动标记:只需重绘标记(一行)而不是绘制所有内容
  • 沿x方向移动绘图:向左/向右移动绘图中当前显示的像素,仅绘制移动到可见区域的像素

在调整大小/缩放时绘制所有内容对我来说很好,但我真的需要更快地绘制后两个修改。我已经查看了matplotlib的动画,但据我所知,它们对我的情况没有帮助。非常感谢任何帮助,谢谢!

2 个答案:

答案 0 :(得分:1)

解决方案似乎是缓存重新绘制的元素,如你所说:

重新绘制的一个重要事项是背景:

    # cache the background
    background = fig.canvas.copy_from_bbox(ax.bbox)

缓存后使用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)

答案 1 :(得分:0)

可以使用优化绘图blitting。只有给定的艺术家(那些被改变的)将被渲染而不是整个人物。

Motplotlib在animation模块内部使用该技术。您可以在其中使用Animation类作为参考,以在代码中实现相同的行为。在源代码中查看_blit_draw()及其后的几个相关函数。