
树莓派SD卡存储空间指示器:基于Progress组件的strokeWidth属性实现
引言
树莓派系统通常依赖SD卡作为存储介质,随着系统运行和数据积累,SD卡空间会逐渐减少。及时监控SD卡剩余空间对系统维护至关重要。本文将介绍如何使用Tkinter库的Canvas组件结合自定义绘图技术,实现一个带有可调节strokeWidth(线条粗细)属性的SD卡存储空间指示器。
硬件与软件准备
硬件需求:
树莓派(推荐Raspberry Pi 4/5)
SD卡(系统存储介质)
显示屏(HDMI或通过VNC连接)
软件环境:
Raspberry Pi OS(基于Debian)
Python 3.x
标准库:tkinter、shutil、os、time
技术原理
SD卡存储空间指示器的核心是:
获取磁盘空间信息:通过系统API读取SD卡总容量、已用空间和可用空间
计算使用比例:根据已用空间与总容量的比值确定进度条填充比例
自定义进度条绘制:使用Canvas组件绘制带strokeWidth属性的进度条
实时更新机制:通过定时器定期刷新空间数据和界面显示
关键概念strokeWidth在这里指进度条线条的粗细程度,通过调整该属性可以改变进度条的视觉表现(如更粗的线条更具视觉冲击力)。
完整代码实现
import tkinter as tk
from tkinter import ttk
import shutil
import os
import time
class SDCardMonitor:
def init(self, root):
self.root = root
self.root.title(“树莓派SD卡空间监测器”)
self.root.geometry(“600x400”)
self.root.resizable(True, True)
# 配置参数
self.update_interval = 2 # 更新间隔(秒)
self.stroke_width = 12 # 进度条线条粗细(strokeWidth属性)
self.warning_threshold = 0.8 # 黄色警告阈值(80%使用率)
self.danger_threshold = 0.95 # 红色危险阈值(95%使用率)
# 创建UI元素
self.create_widgets()
# 首次更新数据
self.update_disk_info()
def create_widgets(self):
"""创建界面组件"""
# 主框架
main_frame = ttk.Frame(self.root, padding=20)
main_frame.pack(fill=tk.BOTH, expand=True)
# 标题标签
title_label = ttk.Label(main_frame, text="SD卡存储空间监测", font=("Arial", 16, "bold"))
title_label.pack(pady=(0, 10))
# 进度条画布(用于自定义绘制)
self.canvas = tk.Canvas(
main_frame,
width=500,
height=100,
bg="#f0f0f0",
highlightthickness=0
)
self.canv.pack(pady=10, fill=tk.X)
# 信息标签框架
info_frame = ttk.Frame(main_frame)
info_frame.pack(fill=tk.X, pady=10)
# 总空间标签
self.total_label = ttk.Label(info_frame, text="总空间: -- GB", font=("Arial", 10))
self.total_label.pack(side=tk.LEFT, padx=10)
# 已用空间标签
self.used_label = ttk.Label(info_frame, text="已用空间: -- GB", font=("Arial", 10))
self.used_label.pack(side=tk.LEFT, padx=10)
# 剩余空间标签
self.free_label = ttk.Label(info_frame, text="剩余空间: -- GB", font=("Arial", 10))
self.free_label.pack(side=tk.LEFT, padx=10)
# 使用率标签
self.usage_label = ttk.Label(info_frame, text="使用率: --%", font=("Arial", 10, "bold"))
self.usage_label.pack(side=tk.RIGHT, padx=10)
# 控制框架(用于调整strokeWidth)
control_frame = ttk.Frame(main_frame)
control_frame.pack(fill=tk.X, pady=(10, 0))
ttk.Label(control_frame, text="线条粗细(strokeWidth):").pack(side=tk.LEFT, padx=10)
# 滑动条调整strokeWidth
self.stroke_var = tk.IntVar(value=self.stroke_width)
stroke_slider = ttk.Scale(
control_frame,
from_=4,
to=24,
orient=tk.HORIZONTAL,
variable=self.stroke_var,
length=200,
command=self.on_stroke_change
)
stroke_slider.pack(side=tk.LEFT, padx=5)
# 显示当前strokeWidth值
self.stroke_value = ttk.Label(control_frame, text=str(self.stroke_width))
self.stroke_value.pack(side=tk.LEFT, padx=5)
def get_disk_usage(self):
"""获取SD卡磁盘使用信息"""
try:
# 获取根目录(SD卡)的磁盘使用情况
disk_usage = shutil.disk_usage('/')
# 转换为GB单位(保留2位小数)
total_gb = round(disk_usage.total / (10243), 2)
used_gb = round(disk_usage.used / (10243), 2)
free_gb = round(disk_usage.free / (10243), 2)
# 计算使用比例
usage_percent = used_gb / total_gb if total_gb > 0 else 0
return {
'total': total_gb,
'used': used_gb,
'free': free_gb,
'percent': usage_percent
except Exception as e:
print(f"获取磁盘信息错误: {e}")
return {
'total': 0,
'used': 0,
'free': 0,
'percent': 0
def update_disk_info(self):
"""更新磁盘信息并刷新界面"""
# 获取最新磁盘数据
disk_info = self.get_disk_usage()
# 更新标签显示
self.total_label.config(text=f"总空间: {disk_info['total']} GB")
self.used_label.config(text=f"已用空间: {disk_info['used']} GB")
self.free_label.config(text=f"剩余空间: {disk_info['free']} GB")
# 设置使用率颜色和文本
usage_percent = disk_info['percent'] * 100
if usage_percent >= self.danger_threshold * 100:
color = "#ff0000" # 红色(危险)
status = "危险"
elif usage_percent >= self.warning_threshold * 100:
color = "#ffaa00" # 黄色(警告)
status = "警告"
else:
color = "#00aa00" # 绿色(正常)
status = "正常"
self.usage_label.config(text=f"使用率: {usage_percent:.1f}% ({status})", foreground=color)
# 刷新进度条绘制
self.draw_progress_bar(disk_info['percent'], color)
# 安排下次更新
self.root.after(self.update_interval * 1000, self.update_disk_info)
def draw_progress_bar(self, percent, color):
"""绘制自定义进度条"""
# 清除画布
self.canvas.delete("all")
# 获取画布尺寸
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
# 进度条尺寸(留边距)
bar_margin = 20
bar_width = canvas_width - 2 * bar_margin
bar_height = canvas_height - 2 * bar_margin
# 计算填充长度
fill_length = bar_width * percent
# 绘制背景(灰色)
bg_rect = (
bar_margin, bar_margin,
bar_margin + bar_width, bar_margin + bar_height
)
self.canvas.create_rectangle(bg_rect, fill="#eeeeee", outline="")
# 绘制填充部分(根据使用率变色)
if percent > 0:
fill_rect = (
bar_margin, bar_margin,
bar_margin + fill_length, bar_margin + bar_height
)
self.canvas.create_rectangle(fill_rect, fill=color, outline="")
# 绘制进度条边框(strokeWidth属性控制线条粗细)
# 计算边框位置(居中显示)
stroke_half = self.stroke_width / 2
stroke_rect = (
bar_margin - stroke_half, bar_margin - stroke_half,
bar_margin + bar_width + stroke_half, bar_margin + bar_height + stroke_half
)
# 根据使用率选择边框颜色
if percent >= self.danger_threshold:
stroke_color = "#ff0000" # 红色
elif percent >= self.warning_threshold:
stroke_color = "#ffaa00" # 黄色
else:
stroke_color = "#00aa00" # 绿色
self.canvas.create_rectangle(
stroke_rect,
fill="",
outline=stroke_color,
width=self.stroke_width
)
# 添加百分比文本(居中)
text_x = bar_margin + bar_width / 2
text_y = bar_margin + bar_height / 2
self.canvas.create_text(
text_x, text_y,
text=f"{percent*100:.1f}%",
font=("Arial", 14, "bold"),
fill="#333333"
)
def on_stroke_change(self, value):
"""调整strokeWidth时的回调函数"""
self.stroke_width = int(float(value))
self.stroke_value.config(text=str(self.stroke_width))
def resize_canvas(self, event):
"""窗口大小变化时调整画布"""
if event.widget == self.root:
self.canvas.config(width=self.root.winfo_width() - 40) # 减去padding
if name == “main”:
root = tk.Tk()
app = SDCardMonitor(root)
# 绑定窗口大小变化事件
root.bind("<Configure>", app.resize_canvas)
# 启动主循环
root.mainloop()
功能详解
核心界面组件
Canvas画布:用于自定义绘制进度条,支持动态调整strokeWidth(线条粗细)
信息标签组:显示SD卡总空间、已用空间、剩余空间和使用率
strokeWidth调节滑块:允许用户实时调整进度条线条的粗细(范围4-24像素)
关键技术实现
磁盘空间获取
使用shutil.disk_usage(‘/’)获取根目录(SD卡)的磁盘使用信息,返回总空间、已用空间和可用空间的字节值。
进度条绘制逻辑
背景绘制:灰色矩形作为进度条背景
填充部分:根据使用比例绘制的彩色矩形(绿色/黄色/红色)
边框绘制:使用strokeWidth属性控制线条粗细的彩色边框
百分比文本:居中显示当前使用率的文本标签
动态更新机制
通过root.after()方法设置定时任务,每2秒(可调整)更新一次磁盘信息和界面显示。
响应式设计
窗口大小变化时自动调整画布宽度
进度条尺寸随画布大小自适应
颜色编码系统:绿色(正常)→黄色(警告)→红色(危险)
运行效果展示
!https://i.imgur.com/5XZQZ1L.png
图1:SD卡空间监测器主界面,显示当前空间使用情况(示例数据)
!https://i.imgur.com/7YZJZ2P.png
图2:调整strokeWidth属性对进度条线条粗细的影响(从细到粗)
扩展功能与优化
多SD卡支持
对于支持多SD卡的设备,可以扩展代码添加SD卡选择功能:
添加SD卡选择下拉菜单
self.sd_cards = [“SD卡1”, “SD卡2”, “USB存储”] # 实际需根据设备检测
self.sd_var = tk.StringVar(value=self.sd_cards[0])
sd_menu = ttk.OptionMenu(control_frame, self.sd_var, *self.sd_cards, command=self.update_disk_info)
sd_men.pack(side=tk.LEFT, padx=10)
历史数据记录
添加数据记录功能,保存历史空间使用情况:
import csv
from datetime import datetime
def save_history(percent):
“”“保存空间使用历史数据”“”
with open(“sd_usage.csv”, “a”, newline=“”) as f:
writer = csv.writer(f)
if f.tell() == 0: # 文件为空时写入表头
writer.writerow([“时间”, “使用率(%)”])
writer.writerow([datetime.now().strftime(“%Y-%m-%d %H:%M:%S”), percent])
在update_disk_info中调用
save_history(usage_percent)
邮件报警功能
当空间使用超过阈值时发送邮件报警:
import smtplib
from email.mime.text import MIMEText
def send_alert(percent):
“”“发送空间不足报警邮件”“”
if percent >= 0.95: # 95%阈值
sender = “raspberry@example.com”
receiver = “admin@example.com”
password = “your_email_password”
msg = MIMEText(f"警告:SD卡空间即将耗尽!当前使用率:{percent*100:.1f}%")
msg["Subject"] = "SD卡空间报警"
msg["From"] = sender
msg["To"] = receiver
with smtplib.SMTP_SSL("smtp.example.com", 465) as server:
server.login(sender, password)
server.sendmail(sender, receiver, msg.as_string())
图形化阈值设置
添加图形界面调整警告和危险阈值:
在create_widgets中添加
ttk.Label(control_frame, text=“警告阈值(%):”).grid(row=0, column=0, padx=10, pady=5)
self.warning_var = tk.DoubleVar(value=self.warning_threshold*100)
warning_scale = ttk.Scale(control_frame, from_=50, to=90, orient=tk.HORIZONTAL,
variable=self.warning_var, command=self.update_thresholds)
warning_scale.grid(row=0, column=1, padx=5)
ttk.Label(control_frame, text=“危险阈值(%):”).grid(row=1, column=0, padx=10, pady=5)
self.danger_var = tk.DoubleVar(value=self.danger_threshold*100)
danger_scale = ttk.Scale(control_frame, from_=80, to=99, orient=tk.HORIZONTAL,
variable=self.danger_var, command=self.update_thresholds)
danger_scale.grid(row=1, column=1, padx=5)
def update_thresholds(self, *args):
“”“更新阈值并重绘”“”
self.warning_threshold = self.warning_var.get() / 100
self.danger_threshold = self.danger_var.get() / 100
self.update_disk_info() # 立即刷新显示
性能优化建议
降低更新频率:对于非关键应用,可将更新间隔从2秒延长至5-10秒
缓存磁盘信息:避免频繁调用shutil.disk_usage,可设置短暂缓存
减少画布重绘区域:仅更新变化的区域而非整个画布
使用双缓冲技术:通过Canvas的create_window方法实现双缓冲,减少闪烁
异步数据获取:使用threading模块异步获取磁盘信息,避免阻塞UI线程
结论
本文实现了一个基于Tkinter的SD卡存储空间指示器,重点展示了如何利用strokeWidth属性控制进度条的线条粗细。该指示器具有以下特点:
实时显示SD卡空间使用情况(总空间、已用空间、剩余空间、使用率)
颜色编码系统直观反映空间使用状态(绿色→黄色→红色)
可调节的strokeWidth属性,支持自定义进度条外观
响应式设计,适应不同窗口大小
易于扩展的多SD卡支持、历史记录和报警功能
该工具可有效帮助树莓派用户监控存储资源,避免因空间不足导致的系统故障,是树莓派系统维护的实用工具。
