树莓派监控脚本:Tkinter窗口无法及时关闭及终端忙碌问题
树莓派性能监控脚本问题解决方案
问题描述
- 点击窗口右上角关闭按钮后,需等待50次采样全部完成才会关闭窗口
- 采样完成后关闭窗口,终端仍处于忙碌状态,脚本未完全退出
问题根源
- 原脚本先执行阻塞式的
for循环(包含time.sleep()),之后才启动Tkinter主循环,导致GUI事件(如关闭窗口)无法被及时处理 - 未正确处理窗口关闭事件,也未清理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
相关产品推荐
相关产品推荐

