如何解决关闭Matplotlib子图后主图停止更新的问题?
问题
我写了一段Python脚本,用Matplotlib实现了一个能循环持续更新的主图。主图窗口里设置了按钮,点击后会弹出第二个子图,子图打开期间主图能正常更新,但关闭子图后,主图的更新循环会意外停止;更奇怪的是,关闭主图窗口后,更新循环又会恢复。请问怎么实现关闭子图后主图仍持续更新?
附上代码:
import sys import time import numpy as np import random import matplotlib.pyplot as plt from matplotlib.widgets import Button def main(): # Create a figure and axis that we will display the main window on fig, ax = plt.subplots() ax_pop = fig.add_axes([0.2, 0.14, 0.2, 0.04]) pop_up_window_button = Button(ax=ax_pop, label='Open second window', hovercolor='0.975') # A callback function for the button press event def pop_up_window(event): """ Pop up a second window """ # Plot some random values to a matplotlib graph print("Plotting some more random values in second window") x = [random.randint(1, 100) for n in range(100)] y = [random.randint(1, 100) for n in range(100)] fig1, ax1 = plt.subplots() ax1.scatter(x, y) ax1.axis('off') plt.pause(0.1) # Need to pause to allow the image to be displayed # Add a callback to the button press event pop_up_window_button.on_clicked(pop_up_window) live_view = True while live_view: # Plot some random values to a matplotlib graph print("Plotting some random values") x = [random.randint(1, 100) for n in range(100)] y = [random.randint(1, 100) for n in range(100)] ax.clear() ax.scatter(x, y) ax.axis('off') ax.set_title('Live view') plt.pause(0.01) # Need to pause to allow the image to be displayed time.sleep(1) print('Done') return True # Main entry point of script if __name__ == '__main__': if main(): sys.exit(0) else: sys.exit(1)
控制台输出示例:
Plotting some random values Plotting some random values Plotting some random values Plotting some random values Plotting some random values Plotting some random values Plotting some more random values in second window < 这里点击了按钮> Plotting some random values Plotting some random values Plotting some random values < 关闭第二个图后,控制台输出暂停,主图停止更新 .... .... 关闭主图窗口后,控制台输出又开始了> Plotting some random values Plotting some random values Plotting some random values Plotting some random values Plotting some random values
解决方案
问题根源
关闭子图时,全局的plt.pause()会陷入阻塞状态——Matplotlib的事件循环在子图关闭后出现异常,导致主循环里的plt.pause()无法正常执行,直到主窗口关闭才释放阻塞。
修改方案
- 为子图添加关闭事件回调,清理引用并避免干扰主图事件循环;
- 用窗口独立的
fig.canvas.draw()+fig.canvas.flush_events()替代全局的plt.pause(),避免全局状态冲突; - 主循环通过检查主图是否存在来控制终止,替代固定的
live_view变量。
修改后的代码
import sys import time import random import matplotlib.pyplot as plt from matplotlib.widgets import Button def main(): # 创建主图窗口 fig, ax = plt.subplots() ax_pop = fig.add_axes([0.2, 0.14, 0.2, 0.04]) pop_up_window_button = Button(ax=ax_pop, label='打开子图', hovercolor='0.975') def pop_up_window(event): print("在子图中绘制随机数据") x = [random.randint(1, 100) for _ in range(100)] y = [random.randint(1, 100) for _ in range(100)] # 创建子图 fig1, ax1 = plt.subplots() ax1.scatter(x, y) ax1.axis('off') # 子图关闭时的回调,清理引用并确保事件循环正常 def on_subplot_close(evt): nonlocal fig1, ax1 fig1 = None ax1 = None fig1.canvas.mpl_connect('close_event', on_subplot_close) # 独立绘制子图并刷新事件 fig1.canvas.draw() fig1.canvas.flush_events() pop_up_window_button.on_clicked(pop_up_window) # 主更新循环 while True: # 检查主图是否已关闭,若关闭则终止循环 if not plt.fignum_exists(fig.number): break print("更新主图数据") x = [random.randint(1, 100) for _ in range(100)] y = [random.randint(1, 100) for _ in range(100)] ax.clear() ax.scatter(x, y) ax.axis('off') ax.set_title('实时视图') # 独立绘制主图并刷新事件,替代全局plt.pause() fig.canvas.draw() fig.canvas.flush_events() time.sleep(1) print('程序结束') return True if __name__ == '__main__': if main(): sys.exit(0) else: sys.exit(1)
关键说明
- 窗口独立的绘制逻辑:每个窗口用自己的
canvas.draw()和flush_events()处理绘制和事件,避免全局plt状态的冲突; - 子图关闭回调:清理子图引用,防止事件循环出现异常;
- 主循环终止条件:通过
plt.fignum_exists()检查主图是否存在,确保主窗口关闭时循环正常终止。
内容的提问来源于stack exchange,提问作者Diarmaid O Cualain
相关产品推荐
相关产品推荐

