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

树莓派监控脚本:Tkinter窗口无法及时关闭及终端忙碌问题

树莓派性能监控脚本问题解决方案

问题描述

  • 点击窗口右上角关闭按钮后,需等待50次采样全部完成才会关闭窗口
  • 采样完成后关闭窗口,终端仍处于忙碌状态,脚本未完全退出

问题根源

  1. 原脚本先执行阻塞式的for循环(包含time.sleep()),之后才启动Tkinter主循环,导致GUI事件(如关闭窗口)无法被及时处理
  2. 未正确处理窗口关闭事件,也未清理matplotlib的后台资源,导致残留线程持续运行

解决方案

1. 用Tkinter after() 实现非阻塞采样

替换原有的for循环和time.sleep(),使用after()定时触发采样,让主循环能同时处理GUI事件

2. 添加窗口关闭回调函数

在回调中终止采样任务,销毁窗口并清理matplotlib资源

3. 正确清理资源

确保matplotlib的图形和画布被正确关闭,避免残留线程

修改后的完整代码

#!/usr/bin/env python3

# Import required modules
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from gpiozero import CPUTemperature
import os
import psutil
import sys

# Create application GUI
Main_Window = tk.Tk()
Main_Window.title("Monitors v1.0")
window_width = 800
window_height = 400
Main_Window.resizable(False, False)
Main_Window.geometry(f'{window_width}x{window_height}')

# Set up the layout of the graphs
fig, (axs) = plt.subplots(2, 2, constrained_layout=True, figsize=(8, 4))
canvas = FigureCanvasTkAgg(fig, Main_Window)
canvas.get_tk_widget().place(x=0, y=0)
fig.suptitle('Performance Metrics')

# Initialize plot labels once
axs[0,0].set_ylabel('CPU Temperature(C)')
axs[0,0].set_xlabel('Time(Sec)')
axs[0,1].set_ylabel('RAM Usage(%)')
axs[0,1].set_xlabel('Time(Sec)')
axs[1,0].set_ylabel('CPU Usage(%)')
axs[1,0].set_xlabel('Time(Sec)')
axs[1,1].set_ylabel('Disk Usage(%)')
axs[1,1].set_xlabel('Time(Sec)')

# Initialize data buffers
temp = []
RAM = []
CPU = []
Disk = []
sample_count = 0
max_samples = 50

def update_plots():
    """更新图表数据并绘制"""
    # 清除旧绘图
    for ax in axs.flat:
        ax.clear()
        # 重新设置标签
        if ax == axs[0,0]:
            ax.set_ylabel('CPU Temperature(C)')
            ax.set_xlabel('Time(Sec)')
        elif ax == axs[0,1]:
            ax.set_ylabel('RAM Usage(%)')
            ax.set_xlabel('Time(Sec)')
        elif ax == axs[1,0]:
            ax.set_ylabel('CPU Usage(%)')
            ax.set_xlabel('Time(Sec)')
        elif ax == axs[1,1]:
            ax.set_ylabel('Disk Usage(%)')
            ax.set_xlabel('Time(Sec)')
    
    # 绘制新数据
    axs[0,0].plot(temp)
    axs[0,1].plot(RAM)
    axs[1,0].plot(CPU)
    axs[1,1].plot(Disk)
    canvas.draw()

def sample_metrics():
    """采样性能指标并触发下一次采样"""
    global sample_count
    
    if sample_count >= max_samples:
        return  # 采样完成,停止任务
    
    # 采样指标
    temp.append(CPUTemperature().temperature)
    RAM.append(psutil.virtual_memory()[2])
    load1, load5, load15 = psutil.getloadavg()
    cpu_usage = (load15 / os.cpu_count()) * 100
    CPU.append(cpu_usage)
    Disk.append(psutil.disk_usage('/').percent)
    
    # 更新图表
    update_plots()
    
    sample_count += 1
    # 200ms后触发下一次采样
    Main_Window.after(200, sample_metrics)

def on_closing():
    """窗口关闭时的清理操作"""
    # 取消所有待执行的after任务
    Main_Window.after_cancel(sample_metrics)
    # 关闭matplotlib图形
    plt.close(fig)
    # 销毁窗口
    Main_Window.destroy()
    # 退出脚本
    sys.exit()

# 设置窗口关闭回调
Main_Window.protocol("WM_DELETE_WINDOW", on_closing)

# 启动采样任务
sample_metrics()

# 启动GUI主循环
Main_Window.mainloop()

关键修改说明

  • 用Main_Window.after(200, sample_metrics)替代for循环和time.sleep(),实现非阻塞定时采样,让GUI能及时响应关闭事件
  • 添加on_closing()回调函数,处理窗口关闭事件:取消采样任务、关闭matplotlib图形、销毁窗口并退出
  • 修正了原代码中Time(%)的标签错误,统一为Time(Sec)
  • 将标签设置逻辑优化,避免重复执行相同操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 23:31:04