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

如何在手动关闭Matplotlib绘制的图形时退出事件循环?

问题

在项目中需要用Matplotlib绘制图形,让用户通过交互定义信号起止的cutoff值并保留在内存中。当前脚本可正常选择cutoff值,按下按键能退出循环,但手动关闭图形时,虽触发了close_event函数,脚本仍陷入死循环,需通过Ctrl+C终止。环境为Python 3.9.16、Matplotlib 3.7.0,Windows 10系统。原代码如下:

import matplotlib.pyplot as plt
import numpy as np

close_flag = 0

# Creating a random dataset
x = np.arange(0, np.pi * 2 * 5, 0.1)
y = np.sin(x)

# Shamelessly stolen from Matplotlib documentation
def tellme(s):
    plt.title(s, fontsize=16)
    plt.draw()

def close_event(event):
    global close_flag
    close_flag = 1

# Creating the figure
fig, axs = plt.subplots()
fig.canvas.mpl_connect('close_event', close_event)

plt.plot(x, y)
plt.grid()

while close_flag == 0:
    pts = [] # Empty list to keep my cutoff values in memory
    _vlines = [] 
    
    # Updating the plot title
    tellme('Select the first cutoff value with the mouse') 
    
    # First cutoff value
    pt = plt.ginput(1, timeout=-1)[0][0] # We only need the value along the x axis
    pts.append(pt)
    
    # Ploting the first cutoff value as a vertical line
    tmp = plt.vlines(pt, ymin=y.min(), ymax=y.max(), colors='k', linestyles='-.')
    _vlines.append(tmp)
    
    
    # Updating the plot title a second time
    tellme('Select the second cutoff dates with the mouse')
    
    # Second cutoff value
    pt = plt.ginput(1, timeout=-1)[0][0]
    pts.append(pt)
    
    # Ploting the second cutoff value as a vertical line
    tmp = plt.vlines(pt, ymin=y.min(), ymax=y.max(), colors='k', linestyles='-.')
    _vlines.append(tmp)

    # Updating the title a third time to inform the user both cutoff values are set
    tellme('Happy? Key click for yes, mouse click for no')

    # First control to let the user escape the loop
    if plt.waitforbuttonpress():
        break

    # First control returned False, we remove the vertical lines and let the user select new values
    for v in _vlines:
        v.remove()
        
    # Second control used to check if the Figure was closed, if so we should exit the loop
    if close_flag == 1:
        break

# Rest of the script where I am using the selected values
解决方案

问题核心在于plt.ginput(timeout=-1)会无限阻塞等待鼠标输入,即使窗口关闭也不会终止,导致循环无法检测到close_flag的变化。修改思路是给ginput设置短超时,让它定期返回,同时在每次输入前检查窗口状态,具体修改如下:

  1. 将ginput和waitforbuttonpress的timeout设为100毫秒,避免无限阻塞;
  2. 在获取输入后先判断是否有有效点击,若没有且窗口已关闭则退出循环;
  3. 将pts移到循环外,确保最终能保留用户选择的有效值;
  4. 在close_event中主动关闭图形,确保窗口状态正确更新。

修改后的代码:

import matplotlib.pyplot as plt
import numpy as np

close_flag = 0
pts = []  # 移到循环外,保存最终选择的cutoff值

# 创建随机数据集
x = np.arange(0, np.pi * 2 * 5, 0.1)
y = np.sin(x)

def tellme(s):
    plt.title(s, fontsize=16)
    plt.draw()

def close_event(event):
    global close_flag
    close_flag = 1
    plt.close(event.canvas.figure)  # 主动关闭图形,避免残留

# 创建图形
fig, axs = plt.subplots()
fig.canvas.mpl_connect('close_event', close_event)

plt.plot(x, y)
plt.grid()

while close_flag == 0:
    _vlines = [] 
    tellme('Select the first cutoff value with the mouse') 
    
    # 获取第一个cutoff值,带超时
    inputs = plt.ginput(1, timeout=100)
    if not inputs:
        if close_flag:
            break
        continue
    pt = inputs[0][0]
    pts.append(pt)
    
    # 绘制第一条竖线
    tmp = plt.vlines(pt, ymin=y.min(), ymax=y.max(), colors='k', linestyles='-.')
    _vlines.append(tmp)
    
    tellme('Select the second cutoff value with the mouse')
    
    # 获取第二个cutoff值,带超时
    inputs = plt.ginput(1, timeout=100)
    if not inputs:
        if close_flag:
            break
        # 若未点击且窗口未关,移除已画的竖线并重新开始
        pts.pop()
        tmp.remove()
        continue
    pt = inputs[0][0]
    pts.append(pt)
    
    # 绘制第二条竖线
    tmp = plt.vlines(pt, ymin=y.min(), ymax=y.max(), colors='k', linestyles='-.')
    _vlines.append(tmp)

    tellme('Happy? Key click for yes, mouse click for no')

    # 等待按键/鼠标操作
    if plt.waitforbuttonpress(timeout=100):
        break

    # 用户不满意,移除竖线并清空当前选择的两个值
    for v in _vlines:
        v.remove()
    pts = pts[:-2]
        
    if close_flag == 1:
        break

# 后续使用pts中的值
print("Selected cutoff values:", pts)

修改说明

  • 短超时设置让程序定期检查close_flag,不会一直阻塞在输入等待上;
  • 增加输入有效性判断,避免因超时或窗口关闭导致的索引错误;
  • 调整pts的作用域,确保最终能保留用户确认的cutoff值;
  • 主动关闭图形的操作让窗口状态及时同步,循环能快速响应关闭事件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 21:29:52