树莓派SD卡存储空间指示器:基于Progress组件的strokeWidth属性实现

爱学习的小齐哥哥
发布于 2025-6-19 09:12
浏览
0收藏

引言

树莓派系统通常依赖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卡支持、历史记录和报警功能

该工具可有效帮助树莓派用户监控存储资源,避免因空间不足导致的系统故障,是树莓派系统维护的实用工具。

已于2025-6-19 09:13:11修改
收藏
回复
举报
回复
    相关推荐