列表中mpl.figure.Figure对象无法显示绘图的问题排查与解决问询
我来帮你拆解这个问题,找到根因并给出可行的解决方案~
问题原因分析
首先看你给出的示例代码,能发现一个明显的差异:self.fig是创建了坐标轴并绘制了折线的,而self.figs里的两个Figure完全是空的——没有任何坐标轴或绘图元素。如果这就是你测试时的情况,那“失效”的直接原因就是空Figure无法被Qt后端正常渲染,要么弹出空窗口,要么因为没有可绘制内容触发异常。
但你提到在其他测试中怀疑和对象销毁、列表可变特性有关,那大概率你的实际场景里,列表中的Figure是有绘图内容的,但还是无法正常显示。这种情况下,问题核心出在matplotlib Figure的生命周期管理和Qt后端的关联机制上:
- 当你用
mpl.figure.Figure()直接创建对象时,它不会被matplotlib的全局Figure管理器跟踪。如果这个Figure没有被Canvas、Manager这类对象持续持有,Python的垃圾回收机制可能会提前回收它,导致显示时找不到有效对象。 - 列表虽然持有Figure的引用,但如果在创建到显示的过程中,Figure的后端关联属性(比如Canvas引用)被意外重置,也会导致渲染失败。
- 至于你尝试的pickle复制方法,之所以没用是因为matplotlib的Figure包含大量和后端交互的复杂对象(比如Renderer、Canvas实例),这些对象是不可序列化的,pickle无法完整复制Figure的运行状态。
解决方案
针对不同场景,我整理了几个可行的方案:
方案1:先给列表中的Figure初始化绘图内容(针对示例空Figure场景)
如果你的问题只是因为列表里的Figure是空的,那只需要在创建时给它们添加坐标轴和绘图即可,修改__init__方法:
def __init__(self): self.figs = [] # 为每个列表中的Figure创建坐标轴并绘图 for _ in range(2): fig = mpl.figure.Figure() ax = fig.subplots() ax.plot([1,2],[3,4]) self.figs.append(fig) self.fig = mpl.figure.Figure() ax = self.fig.subplots() ax.plot([1,2],[3,4])
这样再取出self.figs[0]时,它已经有可渲染的内容,就能正常显示了。
方案2:用matplotlib全局管理器创建Figure
改用plt.figure()创建Figure,这样Figure会被matplotlib的全局管理器跟踪,避免被意外回收:
import matplotlib.pyplot as plt import matplotlib as mpl from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, FigureManagerQT from PyQt5.QtWidgets import QApplication import sys class Test: def __init__(self): # 使用plt.figure()创建,会被全局管理器自动跟踪 self.figs = [plt.figure(), plt.figure()] # 给列表中的Figure添加绘图内容 for fig in self.figs: ax = fig.subplots() ax.plot([1,2],[3,4]) self.fig = plt.figure() ax = self.fig.subplots() ax.plot([1,2],[3,4]) def show(self): # fig = self.fig # works fig = self.figs[0] # 现在可以正常工作 canvas = FigureCanvasQTAgg(fig) figManager = FigureManagerQT(canvas, 0) a = Test() a.show() # 必须启动Qt事件循环,否则窗口会立即关闭 app = QApplication(sys.argv) sys.exit(app.exec_())
方案3:手动维护Figure与Canvas的引用关联
如果你必须用mpl.figure.Figure()创建对象,可以在创建Figure时就关联Canvas,并额外持有Canvas的引用,确保Figure不会被回收:
import matplotlib as mpl from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, FigureManagerQT from PyQt5.QtWidgets import QApplication import sys class Test: def __init__(self): self.figs = [] self.canvases = [] # 额外保存Canvas引用,防止Figure被垃圾回收 for _ in range(2): fig = mpl.figure.Figure() ax = fig.subplots() ax.plot([1,2],[3,4]) # 提前创建Canvas并关联到Figure canvas = FigureCanvasQTAgg(fig) self.figs.append(fig) self.canvases.append(canvas) self.fig = mpl.figure.Figure() ax = self.fig.subplots() ax.plot([1,2],[3,4]) def show(self): fig = self.figs[0] # 直接使用之前创建的Canvas,无需重新初始化 canvas = self.canvases[0] figManager = FigureManagerQT(canvas, 0) a = Test() a.show() app = QApplication(sys.argv) sys.exit(app.exec_())
重要提醒
无论用哪个方案,都要记得启动Qt的事件循环——也就是代码末尾的QApplication初始化和exec_()调用,否则Qt窗口会无法正常显示或瞬间关闭。
内容的提问来源于stack exchange,提问作者Markus Dutschke
相关产品推荐
相关产品推荐

