如何停止Matplotlib导航工具栏放大重置情节更新?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何停止Matplotlib导航工具栏放大重置情节更新?相关的知识,希望对你有一定的参考价值。

使用Matplotlib的导航工具栏,我有一个相对简单的情况。我希望能够在图形更新之间保持先前的缩放值,摄像机平移等。我已经将PyQt5嵌入到这里(我在我的项目中使用),以防两者之间需要一些额外的链接。非常感谢你看看!

import sys
import os
import random
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QGridLayout, QFileDialog, QPushButton

from numpy import arange
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure


class MyMplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.fig = fig ###
        self.axes = fig.add_subplot(111)

        self.axes.hold(False)

        self.compute_initial_figure()

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

    def compute_initial_figure(self):
        pass


class MyDynamicMplCanvas(MyMplCanvas):
    def __init__(self, *args, **kwargs):
        MyMplCanvas.__init__(self, *args, **kwargs)
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.update_figure)
        timer.start(1000)

    def compute_initial_figure(self):
        self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'b')

    def update_figure(self):
        l = [random.randint(0, 10) for i in range(4)]
        self.axes.cla()
        self.axes.plot([0, 1, 2, 3], l, 'b')
        self.draw()


class P1(QtWidgets.QWidget):

    def __init__(self, parent=None):
        super(P1, self).__init__(parent)
        layout = QGridLayout(self)

        self.plot_canvas = MyDynamicMplCanvas(self, width=5, height=4, dpi=100)
        self.navi_toolbar = NavigationToolbar(self.plot_canvas, self)


        layout.addWidget(self.plot_canvas, 1, 1, 1, 1)
        layout.addWidget(self.navi_toolbar, 2, 1, 1, 1)



class MainWindow(QtWidgets.QMainWindow):    
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.stack = QtWidgets.QStackedWidget(self)
        P1f = P1(self)
        self.stack.addWidget(P1f)
        self.setCentralWidget(self.stack)


if __name__ == '__main__':
    qApp = QtWidgets.QApplication(sys.argv)
    aw = MainWindow()
    aw.show()
    sys.exit(qApp.exec_())
答案

这里的大恶棍是axes.hold(False)axes.cla()。它们负责清除轴和图形,这些(通常)将重置绘图视图。

有了这些,你可以正确使用self.axes.autoscale(enable=False),我建议你在compute_initial_figure()的第一个情节后放置,所以情节至少在开始时有所缩放。

然后,为了能够清除以前的图,你可以为MyMplCanvas类创建另一个属性,可能是self.plotted_line或类似的东西,用None初始化。每当你调用self.axes.plot(...)时,将返回值赋给self.plotted_line,如下所示:self.plotted_line, = self.axes.plot(...)。请注意self.plotted_line之后的逗号,这是仅指定第一个返回值的方法之一,这是您感兴趣的那个。

最后,在每个新情节之前,检查并致电

    if self.plotted_line is not None:
        self.plot.remove()

这将有效地删除以前的情节。

画布类看起来像这样(变化很小)。

class MyMplCanvas(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.fig = fig ###
        self.axes = fig.add_subplot(111)

        self.plotted_line = None

        self.compute_initial_figure()

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

    def compute_initial_figure(self):
        pass


class MyDynamicMplCanvas(MyMplCanvas):
    def __init__(self, *args, **kwargs):
        MyMplCanvas.__init__(self, *args, **kwargs)
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.update_figure)
        timer.start(1000)

    def compute_initial_figure(self):
        self.plotted_line, = self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'b')
        self.axes.autoscale(enable=False)

    def update_figure(self):
        l = [random.randint(0, 10) for i in range(4)]

        if self.plotted_line is not None:
            self.plotted_line.remove()
        self.plotted_line, = self.axes.plot([0, 1, 2, 3], l, 'b')
        self.draw()
另一答案

通过连接mpl'draw_event'可以直接存储缩放值:

def __init__(self, parent=None, width=5, height=4, dpi=100):
    ...
    self.mpl_connect('draw_event', self.on_draw)

def on_draw(self, event):
    self.xlim = self.axes.get_xlim()
    self.ylim = self.axes.get_ylim()

然后在plot()调用后恢复:

def update_figure(self):
    ...
    self.axes.plot([0, 1, 2, 3], l, 'b')
    self.axes.set_xlim(self.xlim)
    self.axes.set_ylim(self.ylim)
    ...

https://matplotlib.org/users/event_handling.html

完整代码:

import random
import sys

import matplotlib

matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QGridLayout

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure


class MyMplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.fig = fig  ###
        self.axes = fig.add_subplot(111)

        self.axes.hold(False)

        self.compute_initial_figure()

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
        self.xlim = self.axes.get_xlim()
        self.ylim = self.axes.get_ylim()
        self.mpl_connect('draw_event', self.on_draw)

    def on_draw(self, event):
        self.xlim = self.axes.get_xlim()
        self.ylim = self.axes.get_ylim()

    def compute_initial_figure(self):
        pass


class MyDynamicMplCanvas(MyMplCanvas):
    def __init__(self, *args, **kwargs):
        MyMplCanvas.__init__(self, *args, **kwargs)
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.update_figure)
        timer.start(1000)

    def compute_initial_figure(self):
        self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'b')

    def update_figure(self):
        l = [random.randint(0, 10) for i in range(4)]
        self.axes.cla()
        self.axes.plot([0, 1, 2, 3], l, 'b')
        self.axes.set_xlim(self.xlim)
        self.axes.set_ylim(self.ylim)
        self.draw()


class P1(QtWidgets.QWidget):

    def __init__(self, parent=None):
        super(P1, self).__init__(parent)
        layout = QGridLayout(self)

        self.plot_canvas = MyDynamicMplCanvas(self, width=5, height=4, dpi=100)
        self.navi_toolbar = NavigationToolbar(self.plot_canvas, self)

        layout.addWidget(self.plot_canvas, 1, 1, 1, 1)
        layout.addWidget(self.navi_toolbar, 2, 1, 1, 1)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.stack = QtWidgets.QStackedWidget(self)
        P1f = P1(self)
        self.stack.addWidget(P1f)
        self.setCentralWidget(self.stack)


if __name__ == '__main__':
    qApp = QtWidgets.QApplication(sys.argv)
    aw = MainWindow()
    aw.show()
    sys.exit(qApp.exec_())

以上是关于如何停止Matplotlib导航工具栏放大重置情节更新?的主要内容,如果未能解决你的问题,请参考以下文章

通过网格在 Tkinter 中显示 Matplotlib 导航工具栏

Matplotlib pyqt 导航工具栏更改“图形选项”的背景颜色

Matplotlib 扩大情节

matplotlib colororder 和 twinx

如何在自定义相机中实现“捏放大”

在主窗口工具栏中嵌入带有自定义导航工具栏操作的 matplotlib