You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib FigureCanvasQT刷新后图形部分超出屏幕如何解决?

问题描述

使用FigureCanvasQTAgg和PySide6重复绘制Matplotlib多子图时,每次刷新子图数量和布局会变化,因此选择移除所有轴后完全重绘。首次绘制图形能正常适配父窗口,但刷新后图形部分超出屏幕,调整窗口大小后又能重新适配。需要实现刷新后图形自动适配父窗口。

复现代码
import sys

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from PySide6 import QtCore, QtWidgets


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig = plt.figure(figsize=(width, height), dpi=dpi)
        super(MplCanvas, self).__init__(self.fig)

    def setup(self, nrows: int, ncols: int) -> None:

        self.fig.set_size_inches(4 * ncols, 6 * nrows)

        for sample_idx in range(nrows * ncols):
            ax = self.fig.add_subplot(nrows, ncols, sample_idx + 1)
            rand_image = np.random.randint(100, size=(100, 200))
            im = ax.imshow(rand_image, origin="lower")
        self.fig.subplots_adjust(
            left=0.05, right=0.95, bottom=0.05, top=0.95, wspace=0.2, hspace=0.2
        )


class MainWindow(QtWidgets.QMainWindow):

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

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        self.canvas.setup(2, 2)
        self.canvas.draw()
        self.show()

        # Setup a timer to trigger the redraw by calling update_plot.
        self.timer = QtCore.QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        for ax in self.canvas.fig.get_axes():
            ax.cla()
            ax.remove()
        rows = np.random.randint(1, 3)
        cols = np.random.randint(1, 4)
        self.canvas.setup(rows, cols)
        self.canvas.draw()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()
解决方案

问题根源在于每次刷新时手动设置了fig.set_size_inches(),强制修改图形尺寸导致其与Qt控件实际尺寸不匹配。窗口调整时Matplotlib会自动触发控件尺寸适配逻辑,所以图形恢复正常。

解决思路是让图形尺寸自动跟随Qt控件尺寸,而非手动固定:

  1. 移除setup方法中手动设置图形尺寸的代码,避免强制修改尺寸。
  2. 使用tight_layout替代subplots_adjust,让Matplotlib自动优化子图布局,确保所有元素在控件范围内。
  3. 刷新后同步图形尺寸与控件尺寸,触发适配逻辑。
修改后的代码
import sys

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from PySide6 import QtCore, QtWidgets


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig = plt.figure(figsize=(width, height), dpi=dpi)
        super(MplCanvas, self).__init__(self.fig)

    def setup(self, nrows: int, ncols: int) -> None:
        for sample_idx in range(nrows * ncols):
            ax = self.fig.add_subplot(nrows, ncols, sample_idx + 1)
            rand_image = np.random.randint(100, size=(100, 200))
            im = ax.imshow(rand_image, origin="lower")
        # 用tight_layout自动调整布局,替代手动subplots_adjust
        self.fig.tight_layout(pad=1.0, w_pad=0.2, h_pad=0.2)


class MainWindow(QtWidgets.QMainWindow):

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

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        self.canvas.setup(2, 2)
        self.canvas.draw()
        self.show()

        self.timer = QtCore.QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        for ax in self.canvas.fig.get_axes():
            ax.remove()  # 直接移除轴,无需先清空
        rows = np.random.randint(1, 3)
        cols = np.random.randint(1, 4)
        self.canvas.setup(rows, cols)
        # 同步图形尺寸与控件尺寸
        widget_size = self.centralWidget().size()
        self.canvas.figure.set_size_inches(widget_size.width()/self.canvas.dpi, widget_size.height()/self.canvas.dpi)
        self.canvas.draw()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()
额外说明
  • 移除ax.cla()是因为已经要删除轴,清空操作多余。
  • tight_layout的参数可按需调整:pad控制整体边距,w_pad和h_pad控制子图间的间距。
  • 如果需要保留自定义布局,也可以继续用subplots_adjust,但必须确保不手动设置图形尺寸。

内容的提问来源于stack exchange,提问作者jonas87

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:27:14