简体   繁体   中英

matplotlib - wxpython backend - fast update

As part of a big GUI effort that is meant to plotting complex scientific figures I am trying to speed up interactions and figure updates. So far I've been using canvas.draw() method to update any changes to any drawn object in the figure. I won't be able to reproduce an example code as it's a thousands lines of code but this is a snapshot of what I am dealing with

在此处输入图片说明

the above figure is a fairly congested example image with 3 Axes, contour plot, path, arrow, png image, different transparency objects, shadows, lines, fills, colorbar, etc

在此处输入图片说明

normally a user will be playing with a GUI like the one above to add, delete update or modify any plotted object. For such a figure any modification is slow because it calls canvas.draw() at the backend.

#self.__canvas.Refresh()
#self.__canvas.Update()
###self.__canvas.update() # 'FigureCanvasWxAgg' object has no attribute 'update'
#self.__canvas.Refresh()
#self.__canvas.flush_events()
#self.__canvas.blit(self.__selectedAxes.bbox)
self.__canvas.draw()

I have tried using all the above but only canvas.draw results in updating the figure all the others won't. So far I am not sure how to speed up re-drawing the image after updating only one object.

Also, according to this post blit results in memory leaks. Did anyone tried to verify this hypothesis ?

Any suggestion is appreciated

Instead of using self.__canvas.draw() and redrawing all data on the plots, you can use blitting. By useage of blitting you can add specific new elements to a plot instead of redrawing the whole thing. This saves a massive amount of time.

In order to start blitting, the canvas has to be drawn at least once somewhere in your code. Otherwise there will be nothing 'to blit to'. So unfortunately you can't get fully rid of self.__canvas.draw() .

To blit a certain element, for example a rectangle, you will first have to add the rectangle element to the axes. A rectangle is a matplotlib.patch and to add a patch to the axes you will have to use: self.axes.add_patch(rectangle) . Once added you will need to draw it on the axes by using: self.axes.draw_artist(rectangle) . After it has been drawn, you can blit it to the canvas using: self.canvas.blit(self.axes.bbox) . Save the plot with the blitted element as a background image using: self.background = self.canvas.copy_from_bbox(self.axes.bbox) and restore it to your canvas using: self.canvas.restore_region(self.background) .

Some example code that blits a Rectangle to the canvas:

import matplotlib
matplotlib.use('WXAgg')

from matplotlib.figure import Figure
from matplotlib.backends.backend.wxagg import FigureCanvasWxAgg as FigureCanvas

import wx

class Panel(wx.Frame):
    wx.Frame.__init__(self, parent, id, 'Title')

    self.figure = Figure()
    self.axes = self.figure.add_subplot(111)
    self.canvas = FigureCanvas(self, -1, self.figure)
    self.sizer = wx.BoxSizer(wx.VERTICAL)
    self.sizer.Add(self.canvas, proportion=1, flag= wx.ALL | wx.GROW)

    """Plot data and stuff to canvas...."""        

    self.canvas.draw()
    self.background = self.canvas.copy_from_bbox(self.axes.bbox)

    square = matplotlib.patches.Rectangle((xPos,yPos), width, height)

    self.canvas.restore_region(self.background)
    self.axes.add_patch(square)
    self.axes.draw_artist(square)
    self.canvas.blit(self.axes.bbox)
    self.background = self.canvas.copy_from_bbox(self.axes.bbox)

Can be I made some typos. But you will get the idea of it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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