matplotlib draw_artist和canvas更新删除以前的avxline

时间:2017-04-11 21:50:46

标签: python canvas matplotlib

我在使用canvas.update()时遇到麻烦,除了只在canvas.draw()上删除之前的draw_artist。我想要做的是及时地在30多个子图上绘制一条垂直线(即鼠标和垂直线位置之间没有明显的延迟)。如果我使用canvas.draw()而不是canvas.update(),则由于正在重绘的子图的数量而存在显着的延迟。我为实现垂直线的鼠标鼠标实现的代码如下所示。当用户双击时,已经初始化跨越所有子图的悬停线。如果我使用canvas.draw(),有没有办法删除延迟,或者如果我使用canvas.update(),则删除先前绘制的垂直线。

def onMove(self, event):
    if (dblclicked):
       self.hoveringLine.set_xdata(event.xdata)
    self.canvas.axis1.draw_artist(self.hoveringLine)
    self.canvas.update()

编辑#2:这是完整的代码,我为不完整的评论道歉。

from PyQt4 import QtGui, QtCore
import sys
import matplotlib

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.figure                  import Figure


class Canvas(FigureCanvasQTAgg):

    def __init__(self, parent=None):
        self.figure = Figure()
        super(Canvas, self).__init__(self.figure)

        self.ax1 = self.figure.add_subplot(1,1,1)
        self.figure.subplots_adjust(left = 0.05, bottom = 0.02, right = 0.98, top = 0.99)
        self.setMinimumWidth(1000)
        self.ax1.plot([1,2,3])
        self.draw()

    def add_subplot(self, data=[]):
        rows = len(self.figure.axes) + 1
        for index, axes in enumerate(self.figure.axes, start=1):
            axes.change_geometry(rows, 1, index)

        ax = self.figure.add_subplot(rows, 1, index+1)
        ax.plot(data)
        ax.patch.set_facecolor('None')
        self.figure.set_figheight(self.figure.get_figheight()*1.1)



class Window(QtGui.QMainWindow):

    def __init__(self):
        super(Window, self).__init__()

        self.showMaximized()
        self.widget = QtGui.QWidget()
        self.setCentralWidget(self.widget)
        self.widget.setLayout(QtGui.QVBoxLayout())
        self.widget.layout().setContentsMargins(0,0,0,0)
        self.widget.layout().setSpacing(5)

        self.canvas = Canvas(self)
        self.scroll = QtGui.QScrollArea(self.widget)
        self.scroll.setWidget(self.canvas)
        self.scroll.setWidgetResizable(False)
        self.numSubplots = 15

        for x in range(self.numSubplots):
            self.canvas.add_subplot()
            self.canvas.adjustSize()
        self.canvas.draw()
        self.widget.layout().addWidget(self.scroll)
        self.showVline = False
        self.hoveringLine = None

        self.canvas.mpl_connect("button_press_event", self.onClick)
        self.canvas.mpl_connect("motion_notify_event", self.onMove)

    def onClick(self, event):
        if event.dblclick and self.showVline == False:
            self.showVline = True
            self.hoveringLine  = self.canvas.ax1.axvline(x=event.xdata, ymin=-1.2*self.numSubplots, ymax=1.2,
                                                     lw=2, zorder=0, clip_on=False)
        elif event.dblclick and self.showVline:
            self.showVline = False
            self.hoveringLine = None
            self.canvas.ax1.axvline(x=event.xdata, ymin=-1.2*self.numSubplots, ymax=1.2,
                                                     lw=2, zorder=0, clip_on=False)
            self.canvas.draw()
        else:
            pass

    def onMove(self, event):
        if (self.showVline):
            self.hoveringLine.set_xdata(event.xdata)
        self.canvas.ax1.draw_artist(self.hoveringLine)
        self.canvas.update()
        #self.canvas.draw() works as desired but there is a delay due to redrawing
        #self.canvas.draw()


def main():
    app = QtGui.QApplication(sys.argv)
    app.aboutToQuit.connect(app.deleteLater)
    GUI = Window()
    GUI.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

1 个答案:

答案 0 :(得分:1)

一种。 draw_idle()

首先,您可以使用self.canvas.draw_idle()代替self.canvas.draw()获得一些收益。 draw_idle仅在程序不忙于执行其他操作时才绘制画布。这似乎有点违反直觉,因为它应该更少地绘制画布,但这正是重点:它不会尝试在已经重绘画布的实例中重绘它,从而限制了重绘事件的数量否则会排队并限制性能。

B中。位图传输

如果上述情况仍无法给出满意的结果,您可以使用名为 blitting 的技术。在SO上有一个非常好的答案:Efficient Matplotlib Redrawing

这个想法主要是拍摄画布的快照并存储它。在该事件中,可以将该存储的区域放回到画布中,并且仅需要重新绘制更新的艺术家。

在这个案例中,问题如下:

def onClick(self, event):

    if event.dblclick and self.showVline == False:
        self.background = self.canvas.copy_from_bbox(self.canvas.figure.bbox)
        self.showVline = True
        self.hoveringLine  = self.canvas.ax1.axvline(x=event.xdata, ymin=-1.2*self.numSubplots, ymax=1.2,
                                                 lw=2, zorder=0, clip_on=False)
    elif event.dblclick and self.showVline:
        self.showVline = False
        self.hoveringLine = None
        self.canvas.ax1.axvline(x=event.xdata, ymin=-1.2*self.numSubplots, ymax=1.2,
                                                 lw=2, zorder=0, clip_on=False)
        self.canvas.draw()
    else:
        pass

def onMove(self, event):
    if (self.showVline):
        self.canvas.restore_region(self.background)
        self.hoveringLine.set_xdata(event.xdata)
        self.canvas.ax1.draw_artist(self.hoveringLine)
        self.canvas.blit(self.canvas.figure.bbox)