【问题标题】:matplotlib tight_layout with annotation causes figure to get smaller and smaller带有注释的 matplotlib 紧密布局导致图形越来越小
【发布时间】:2022-01-18 12:59:11
【问题描述】:

我有一个FigureCanvasQTAgg,它通过用户选择要绘制的数据来创建绘图,并且画布通过调用self.fig.clear() 并创建新绘图来更新绘图。这工作得很好,但我遇到了一个问题。我在轴下设计了一个带有注释的绘图,但是每次更新图形时,绘图都会变得越来越小。这段代码复制了这个问题:

import sys
from PyQt5 import QtCore, QtWidgets
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg

class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):

        self.fig, self.axes = plt.subplots(figsize=(width, height), dpi=dpi, tight_layout=True)
        super(MplCanvas, self).__init__(self.fig)


    def make_plot(self, x, y, fmt='.k'):

        self.fig.clear()
        axes = self.fig.subplots()

        axes.plot(x, y, fmt)
        axes.invert_yaxis()
        axes.annotate('I make the plot get smaller and smaller', xy=(0,0), xycoords='figure points', fontsize='x-small', fontstyle='italic', annotation_clip=False)

        self.draw()

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        #Create a layout with central alignment
        self.layout1 = QtWidgets.QVBoxLayout()
        self.layout1.setAlignment(QtCore.Qt.AlignCenter)

        # Create a placeholder widget to hold our toolbar and canvas.
        self.widget1 = QtWidgets.QWidget()
        self.widget1.setLayout(self.layout1)
        self.setCentralWidget(self.widget1)

        #Create a matplotlib canvas and add it to the layout
        self.sc = MplCanvas(self, width=5, height=4, dpi=100)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed)
        self.sc.setSizePolicy(sizePolicy)
        self.layout1.addWidget(self.sc)

        # Create a button
        self.button = QtWidgets.QPushButton()
        self.button.setText("Press me")
        self.layout1.addWidget(self.button)

        #Create the connection
        self.button.clicked.connect(self.plot_something)

        self.show()

    def plot_something(self):
        print('plotting')
        x = [0,1,2,3,4]
        y = [10,1,20,3,40]
        self.sc.make_plot(x, y)

app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()

在这个例子中,摆弄xycoords 参数有时可以解决它,例如使用xycoords='axes points''data' 似乎有效。但这并不能解决我实际应用中的问题。禁用紧凑布局似乎也可以解决它,但由于各种原因我需要紧凑布局。

轴每次都变得越来越小的事实表明self.fig.clear() 实际上并没有清除所有内容 - 在情节的迭代之间会记住一些东西。有没有办法可以完全清除 fig 对象并开始一个新对象?还是最好每次关闭实际画布并创建一个新画布?

【问题讨论】:

    标签: python matplotlib pyqt5


    【解决方案1】:

    当艺术家参与紧密布局(或更好的约束布局)时,它会尝试使轴足够小,使其不与其他轴重叠。在这种情况下,您在轴上放置注释,但在图形坐标中绘制它。在这种情况下,最好将其从自动布局中删除:

    an = axes.annotate('I make the plot get smaller and smaller', 
                       xy=(0,0), xycoords='figure points', 
                       fontsize='x-small', fontstyle='italic',
                       annotation_clip=False)
    an.set_in_layout(False)
    

    至于为什么Axes越来越小,tight_layout使用subplots_adjust,它设置了Figure.subplotpars。这些在Fig.clear() 上没有被归零。您实际上可以打开一个错误报告,因为我认为它们应该被重置。

    但是,对于您的代码,我可能不会继续清除该数字,因为这在 cpu 周期方面会变得昂贵。如果可能,最好更新轴中的数据。

    【讨论】:

    • 这是一个很好的解决方法,我可能会接受它。我只是想知道问题的迭代方面。该代码在第一次迭代时运行良好,并且仅在第二次绘制该图时才会出现问题,并且在每次绘制该图时都会变得更糟。这表明即使调用了 fig.clear() 也会保留一些数据。这是为什么呢?
    • 查看编辑,但这似乎是fig.clear 的缺失功能;虽然使用 constrained_layout 不应该有这种不对称性..
    • 非常有趣。使用此信息,我创建了另一个解决方案。当画布类启动时,我放了self.spp = self.fig.subplotpars,然后在self.fig.clear()之后使用self.fig.subplotpars = copy.deepcopy(self.spp)这行这会将subplotpars重置为它们在开始时的样子。
    • 看起来这是一个已知的错误。该线程中还有其他一些解决方案。 github.com/matplotlib/matplotlib/issues/11059
    • 公平。但同样,如果您使用 constrained_layout,则无需执行任何此操作,另外还有一个好处是 constrained_layout 更加灵活。
    猜你喜欢
    • 2014-05-09
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    • 2019-12-26
    • 1970-01-01
    • 2020-06-06
    • 2021-05-29
    • 2022-01-05
    相关资源
    最近更新 更多