吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4572|回复: 45
上一主题 下一主题
收起左侧

[Python 转载] 【开源分享】Python 电脑配置助手 - 硬件信息 + 性能监控 + Temp 管理(修复导出)

  [复制链接]
跳转到指定楼层
楼主
fboy19871218 发表于 2026-3-9 10:05 回帖奖励
本帖最后由 fboy19871218 于 2026-3-9 16:47 编辑

一、工具简介
这款工具是基于 Python 3.11.9 开发的轻量级系统监控工具,采用 tkinter + ttkbootstrap 构建美观的 GUI 界面,无需安装复杂环境,普通用户也能轻松使用。核心定位是「一站式查看电脑硬件信息 + 实时监控系统性能」,适合普通用户、运维人员、电脑爱好者快速了解设备状态,尤其适合需要排查硬件问题、监控系统资源的场景。开发环境 / 依赖说明核心依赖:tkinter(Python 自带)、ttkbootstrap、psutil、wmi兼容系统:Windows(WMI 模块仅支持 Windows)界面风格:支持自定义主题(默认 cosmo)
二、核心功能详解
1. 硬件信息页
(一站式查看全硬件参数)
支持查看的硬件信息:
系统信息:
操作系统版本、架构、内核版本CPU 信息:型号、频率、核心数 / 线程数主板信息:型号、制造商、序列号内存信息:总容量、可用容量、插槽 / 品牌 / 频率(单条内存也能识别)显卡信息:型号、显存大小硬盘信息:型号、容量、序列号、
接口类型分区信息:各盘符总容量 / 已用容量 / 使用率网卡信息:型号、MAC 地址、IP 地址电池信息:电量、充电状态(仅笔记本)
实用功能:「刷新」按钮:实时更新硬件信息「导出」按钮:将所有硬件信息保存为 TXT 文件(默认保存到程序运行目录,命名带时间戳)加载动画:避免硬件信息加载时界面卡顿,提升体验
2. 性能监控页
(实时监控系统动态)
实时监控项(1 秒刷新一次):
CPU 使用率(带进度条可视化)
内存使用率(带进度条可视化)
磁盘读写速度(KB/s)网络上传 / 下载速度(KB/s)
Temp 目录占用空间(自动缓存 1 分钟,避免重复计算)
实用功能:
「打开 Temp」按钮:
一键打开系统临时目录(支持多系统兼容)硬盘容量监控:区分系统盘 / 非系统盘,剩余空间低于 10GB 标红提醒动态样式:系统盘标蓝色、低空间标红色、普通盘标灰色,直观识别风险
三、附源代码(V1.1)
[Python] 纯文本查看 复制代码
import tkinter as tk
from tkinter import ttk
import ttkbootstrap as ttkb
from ttkbootstrap.constants import *
import psutil
import wmi
import platform
import time
from datetime import datetime
import os
import tempfile
import sys
import webbrowser

# 常量定义
BYTES_TO_GB = 1024 ** 3
CACHE_DURATION = 60  # 缓存1分钟
LOW_SPACE_THRESHOLD = 10  # 低空间阈值(GB)

class SystemMonitorApp:
    """电脑配置助手应用类
    
    功能:
    - 显示硬件信息
    - 监控系统性能
    - 管理Temp目录
    - 导出硬件信息
    """
    
    def __init__(self, root: tk.Tk):
        """初始化应用
        
        Args:
            root: tkinter根窗口
        """
        self.root = root
        self.root.title("电脑配置助手")
        
        # 屏幕居中 - 必须在withdraw之前设置
        width = 800
        height = 600
        x = (self.root.winfo_screenwidth() // 2) - (width // 2)
        y = (self.root.winfo_screenheight() // 2) - (height // 2)
        self.root.geometry(f"{width}x{height}+{x}+{y}")
        
        # 支持窗口大小调整
        self.root.resizable(True, True)
        
        # 统一字体和颜色风格
        self.fonts = {
            'title': ("Arial", 12, "bold"),
            'label': ("Arial", 12),
            'small': ("Arial", 10)
        }
        
        # 初始化缓存
        self.temp_space_cache = {}
        self.temp_cache_time = 0
        
        # 创建标签页
        self.notebook = ttkb.Notebook(root)
        self.notebook.pack(fill=BOTH, expand=True, padx=10, pady=10)
        
        # 硬件信息页
        self.hardware_frame = ttkb.Frame(self.notebook)
        self.notebook.add(self.hardware_frame, text="硬件信息")
        self._create_hardware_tab()
        
        # 性能监控页
        self.performance_frame = ttkb.Frame(self.notebook)
        self.notebook.add(self.performance_frame, text="性能监控")
        self._create_performance_tab()
        
        # 延迟初始化WMI和性能监控(在窗口显示后)
        self.root.after(100, self._delayed_init)

    def _delayed_init(self):
        """延迟初始化
        
        在窗口显示后初始化WMI和性能监控,避免阻塞UI
        """
        # 显示窗口(UI创建完成后)
        self.root.deiconify()
        
        # 延迟加载硬件信息,让用户看到加载动画
        self.root.after(1000, self._load_hardware_info)

    def _load_hardware_info(self):
        """加载硬件信息
        
        初始化WMI对象,加载硬件信息,启动性能监控
        """
        # 初始化WMI对象(在窗口显示后执行,不阻塞UI)
        self.wmi_obj = wmi.WMI()
        self.last_disk_io = psutil.disk_io_counters()
        self.last_net_io = psutil.net_io_counters()
        self.last_time = time.time()
        
        # 加载硬件信息
        self.refresh_hardware_info()
        
        # 启动性能监控
        self.update_performance()

    def _create_hardware_tab(self):
        # 创建滚动条
        scrollbar = ttk.Scrollbar(self.hardware_frame, orient="vertical")
        
        # 硬件信息表格
        self.hardware_tree = ttkb.Treeview(
            self.hardware_frame,
            columns=("硬件信息", "硬件内容"),
            show="headings",
            bootstyle="secondary",
            yscrollcommand=scrollbar.set
        )
        
        # 关联滚动条
        scrollbar.config(command=self.hardware_tree.yview)
        
        self.hardware_tree.heading("硬件信息", text="硬件信息")
        self.hardware_tree.heading("硬件内容", text="硬件内容")
        self.hardware_tree.column("硬件信息", width=200, anchor=W)
        self.hardware_tree.column("硬件内容", width=500, anchor=W)
        
        # 添加表格线 - 使用标准ttk样式
        style = ttk.Style()
        style.configure("Treeview", 
                      rowheight=25, 
                      borderwidth=1,
                      relief="solid")
        style.configure("Treeview.Heading", 
                      borderwidth=1,
                      relief="solid")
        style.map("Treeview", 
                  background=[("selected", "#0078d7")],
                  foreground=[("selected", "white")])
        style.configure("Treeview.Cell", 
                      borderwidth=1)
        
        # 使用grid布局来放置表格和滚动条
        self.hardware_tree.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
        scrollbar.grid(row=0, column=1, sticky="ns", pady=10)
        
        # 配置网格列权重
        self.hardware_frame.grid_columnconfigure(0, weight=1)
        self.hardware_frame.grid_rowconfigure(0, weight=1)
        
        # 加载中提示
        self.loading_label = ttkb.Label(
            self.hardware_frame,
            text="正在加载硬件信息...",
            font=self.fonts['label'],
            bootstyle="secondary"
        )
        self.loading_label.grid(row=1, column=0, columnspan=2, pady=10)
        
        # 加载动画
        self.loading_progress = ttkb.Progressbar(
            self.hardware_frame,
            bootstyle="striped",
            mode="indeterminate",
            maximum=100
        )
        self.loading_progress.grid(row=2, column=0, columnspan=2, sticky="ew", padx=50, pady=5)
        self.loading_progress.start(10)
        
        # 按钮栏
        btn_frame = ttkb.Frame(self.hardware_frame)
        btn_frame.grid(row=3, column=0, columnspan=2, sticky="ew", padx=10, pady=5)
        
        refresh_btn = ttkb.Button(
            btn_frame,
            text="刷新",
            command=self.refresh_hardware_info,
            bootstyle=PRIMARY
        )
        refresh_btn.pack(side=LEFT, padx=5)
        
        export_btn = ttkb.Button(
            btn_frame,
            text="导出",
            command=self.export_hardware_info,
            bootstyle=INFO
        )
        export_btn.pack(side=LEFT, padx=5)

    def _get_hardware_info(self):
        info = []
        
        # 系统信息
        info.extend(self._get_system_info())
        # CPU信息
        info.extend(self._get_cpu_info())
        # 主板信息
        info.extend(self._get_motherboard_info())
        # 内存信息
        info.extend(self._get_memory_info())
        # 显卡信息
        info.extend(self._get_gpu_info())
        # 硬盘信息
        info.extend(self._get_disk_info())
        # 分区信息
        info.extend(self._get_partition_info())
        # 网卡信息
        info.extend(self._get_network_info())
        # 电池信息
        info.extend(self._get_battery_info())
        
        return info
    
    def _get_system_info(self):
        """获取系统信息"""
        info = []
        info.append(("操作系统", platform.platform()))
        info.append(("系统版本", platform.version()))
        info.append(("系统架构", platform.architecture()[0]))
        return info
    
    def _get_cpu_info(self):
        """获取CPU信息"""
        info = []
        try:
            cpu_info = platform.processor()
            cpu_freq = psutil.cpu_freq().current if psutil.cpu_freq() else "N/A"
            cpu_cores = psutil.cpu_count(logical=False)
            cpu_logical = psutil.cpu_count(logical=True)
            info.append(("处理器", f"{cpu_info}"))
            info.append(("处理器频率", f"{cpu_freq:.2f}MHz" if isinstance(cpu_freq, (int, float)) else "N/A"))
            info.append(("核心数", f"{cpu_cores} 核心 {cpu_logical} 线程"))
        except Exception as e:
            info.append(("处理器", f"获取失败: {str(e)}"))
        return info
    
    def _get_motherboard_info(self):
        """获取主板信息"""
        info = []
        try:
            for board in self.wmi_obj.Win32_BaseBoard():
                info.append(("主板型号", board.Product))
                info.append(("主板制造商", board.Manufacturer))
                info.append(("主板序列号", board.SerialNumber))
        except Exception as e:
            info.append(("主板信息", f"获取失败: {str(e)}"))
        return info
    
    def _get_memory_info(self):
        """获取内存信息"""
        info = []
        try:
            mem_total = round(psutil.virtual_memory().total / (1024**3), 2)
            mem_available = round(psutil.virtual_memory().available / (1024**3), 2)
            info.append(("内存总容量", f"{mem_total} GB"))
            info.append(("可用内存", f"{mem_available} GB"))
            info.append(("内存使用率", f"{psutil.virtual_memory().percent}%"))
            
            slot_count = 1
            for mem in self.wmi_obj.Win32_PhysicalMemory():
                slot = mem.DeviceLocator if mem.DeviceLocator else f"插槽{slot_count}"
                capacity = int(mem.Capacity)//(1024**3)
                speed = mem.Speed if mem.Speed else "未知"
                manufacturer = mem.Manufacturer if mem.Manufacturer else "未知"
                info.append((f"内存{slot}", f"{manufacturer} {capacity}GB {speed}MHz"))
                slot_count += 1
        except Exception as e:
            info.append(("内存信息", f"获取失败: {str(e)}"))
        return info
    
    def _get_gpu_info(self):
        """获取显卡信息"""
        info = []
        try:
            gpu_count = 1
            for gpu in self.wmi_obj.Win32_VideoController():
                info.append((f"显卡{gpu_count}", gpu.Name))
                if hasattr(gpu, 'AdapterRAM') and gpu.AdapterRAM:
                    vram = int(gpu.AdapterRAM)/(1024**3)
                    info.append((f"显存{gpu_count}", f"{vram:.1f}GB"))
                gpu_count += 1
        except Exception as e:
            info.append(("显卡信息", f"获取失败: {str(e)}"))
        return info
    
    def _get_disk_info(self):
        """获取硬盘信息"""
        info = []
        try:
            disk_count = 1
            for disk in self.wmi_obj.Win32_DiskDrive():
                disk_size = round(int(disk.Size)/(1024**3), 2)
                info.append((f"硬盘{disk_count}型号", disk.Model))
                info.append((f"硬盘{disk_count}容量", f"{disk_size}GB"))
                info.append((f"硬盘{disk_count}序列号", disk.SerialNumber.strip()))
                info.append((f"硬盘{disk_count}接口", disk.InterfaceType))
                disk_count += 1
        except Exception as e:
            info.append(("硬盘信息", f"获取失败: {str(e)}"))
        return info
    
    def _get_partition_info(self):
        """获取分区信息"""
        info = []
        try:
            for part in psutil.disk_partitions():
                if 'cdrom' not in part.opts and part.fstype != '':
                    try:
                        usage = psutil.disk_usage(part.mountpoint)
                        total_gb = round(usage.total/(1024**3), 2)
                        used_gb = round(usage.used/(1024**3), 2)
                        used_percent = usage.percent
                        info.append((
                            f"分区 {part.mountpoint}",
                            f"{total_gb}GB 总容量,{used_gb}GB 已用 ({used_percent}%)"
                        ))
                    except Exception as e:
                        info.append((f"分区 {part.mountpoint}", f"获取失败: {str(e)}"))
        except Exception as e:
            info.append(("分区信息", f"获取失败: {str(e)}"))
        return info
    
    def _get_network_info(self):
        """获取网卡信息"""
        info = []
        try:
            nic_count = 1
            for nic in self.wmi_obj.Win32_NetworkAdapterConfiguration():
                if nic.IPEnabled:
                    info.append((f"网卡{nic_count}", nic.Description))
                    info.append((f"MAC地址{nic_count}", nic.MACAddress))
                    if nic.IPAddress:
                        info.append((f"IP地址{nic_count}", nic.IPAddress[0]))
                    nic_count += 1
        except Exception as e:
            info.append(("网卡信息", f"获取失败: {str(e)}"))
        return info
    
    def _get_battery_info(self):
        """获取电池信息"""
        info = []
        try:
            battery = psutil.sensors_battery()
            if battery:
                info.append(("电池电量", f"{battery.percent}%"))
                info.append(("电池状态", "充电中" if battery.power_plugged else "放电中"))
        except Exception as e:
            # 笔记本才会有电池,所以这里的异常可以忽略
            pass
        return info

    def refresh_hardware_info(self):
        # 清空表格
        for item in self.hardware_tree.get_children():
            self.hardware_tree.delete(item)
        
        # 分批插入新信息
        hardware_info = self._get_hardware_info()
        
        def insert_batch(start=0, batch_size=10):
            end = min(start + batch_size, len(hardware_info))
            for i in range(start, end):
                component, detail = hardware_info[i]
                self.hardware_tree.insert("", END, values=(component, detail))
            if end < len(hardware_info):
                self.root.after(50, insert_batch, end, batch_size)
            else:
                # 隐藏加载动画
                if hasattr(self, 'loading_label'):
                    self.loading_label.grid_forget()
                if hasattr(self, 'loading_progress'):
                    self.loading_progress.stop()
                    self.loading_progress.grid_forget()
        
        insert_batch()

    def export_hardware_info(self):
        """导出硬件信息到文本文件
        
        将硬件信息导出到程序所在目录,支持开发环境和打包环境
        """
        hardware_info = self._get_hardware_info()
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        # 确保导出到主程序所在目录
        # 处理开发环境和打包环境
        if getattr(sys, 'frozen', False):
            # 打包环境
            program_dir = os.path.dirname(sys.executable)
        else:
            # 开发环境
            program_dir = os.path.dirname(os.path.abspath(__file__))
        
        filename = os.path.join(program_dir, f"硬件信息_{timestamp}.txt")
        
        try:
            with open(filename, "w", encoding="utf-8") as f:
                f.write("电脑硬件信息\n")
                f.write("="*30 + "\n")
                for component, detail in hardware_info:
                    f.write(f"{component}: {detail}\n")
            ttkb.dialogs.Messagebox.show_info("导出成功", f"硬件信息已导出到:\n{filename}")
        except Exception as e:
            ttkb.dialogs.Messagebox.show_error("导出失败", f"导出硬件信息时出错:\n{str(e)}")

    def _create_performance_tab(self):
        # CPU使用率
        cpu_label = ttkb.Label(self.performance_frame, text="CPU使用率", font=self.fonts['label'])
        cpu_label.grid(row=0, column=0, padx=10, pady=5, sticky=W)
        self.cpu_value_label = ttkb.Label(self.performance_frame, text="0%", font=self.fonts['label'])
        self.cpu_value_label.grid(row=0, column=1, padx=10, pady=5, sticky=W)
        self.cpu_progress = ttkb.Progressbar(
            self.performance_frame,
            bootstyle=PRIMARY,
            maximum=100
        )
        self.cpu_progress.grid(row=0, column=2, padx=10, pady=5, sticky=EW)
        
        # 内存使用率
        mem_label = ttkb.Label(self.performance_frame, text="内存使用率", font=self.fonts['label'])
        mem_label.grid(row=1, column=0, padx=10, pady=5, sticky=W)
        self.mem_value_label = ttkb.Label(self.performance_frame, text="0%", font=self.fonts['label'])
        self.mem_value_label.grid(row=1, column=1, padx=10, pady=5, sticky=W)
        self.mem_progress = ttkb.Progressbar(
            self.performance_frame,
            bootstyle=SUCCESS,
            maximum=100
        )
        self.mem_progress.grid(row=1, column=2, padx=10, pady=5, sticky=EW)
        
        # 磁盘读取
        disk_read_label = ttkb.Label(self.performance_frame, text="磁盘读取", font=self.fonts['label'])
        disk_read_label.grid(row=2, column=0, padx=10, pady=5, sticky=W)
        self.disk_read_value_label = ttkb.Label(self.performance_frame, text="0 KB/s", font=self.fonts['label'])
        self.disk_read_value_label.grid(row=2, column=1, padx=10, pady=5, sticky=W)
        
        # 磁盘写入
        disk_write_label = ttkb.Label(self.performance_frame, text="磁盘写入", font=self.fonts['label'])
        disk_write_label.grid(row=3, column=0, padx=10, pady=5, sticky=W)
        self.disk_write_value_label = ttkb.Label(self.performance_frame, text="0 KB/s", font=self.fonts['label'])
        self.disk_write_value_label.grid(row=3, column=1, padx=10, pady=5, sticky=W)
        
        # 网络上传速度
        net_upload_label = ttkb.Label(self.performance_frame, text="网络上传", font=self.fonts['label'])
        net_upload_label.grid(row=4, column=0, padx=10, pady=5, sticky=W)
        self.net_upload_label = ttkb.Label(self.performance_frame, text="0 KB/s", font=self.fonts['label'])
        self.net_upload_label.grid(row=4, column=1, padx=10, pady=5, sticky=W)
        
        # 网络下载速度
        net_download_label = ttkb.Label(self.performance_frame, text="网络下载", font=self.fonts['label'])
        net_download_label.grid(row=5, column=0, padx=10, pady=5, sticky=W)
        self.net_download_label = ttkb.Label(self.performance_frame, text="0 KB/s", font=self.fonts['label'])
        self.net_download_label.grid(row=5, column=1, padx=10, pady=5, sticky=W)
        
        # Temp目录空间
        temp_label = ttkb.Label(self.performance_frame, text="Temp目录", font=self.fonts['label'])
        temp_label.grid(row=6, column=0, padx=10, pady=5, sticky=W)
        self.temp_value_label = ttkb.Label(self.performance_frame, text="计算中...", font=self.fonts['label'])
        self.temp_value_label.grid(row=6, column=1, padx=10, pady=5, sticky=W)
        
        # 打开Temp目录按钮
        open_temp_btn = ttkb.Button(
            self.performance_frame,
            text="打开Temp",
            command=self.open_temp_directory,
            bootstyle=INFO
        )
        open_temp_btn.grid(row=6, column=2, padx=10, pady=5, sticky=W)
        
        # 硬盘容量监控
        disk_frame = ttkb.LabelFrame(self.performance_frame, text="硬盘容量监控")
        disk_frame.grid(row=7, column=0, columnspan=3, padx=10, pady=10, sticky=EW)
        
        # 硬盘信息容器
        self.disk_info_frame = ttkb.Frame(disk_frame)
        self.disk_info_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)
        
        # 网格列权重配置
        self.performance_frame.grid_columnconfigure(1, weight=1)
        self.performance_frame.grid_columnconfigure(2, weight=2)

    def update_performance(self):
        # CPU使用率
        cpu_percent = psutil.cpu_percent(interval=0)
        self.cpu_value_label.config(text=f"{cpu_percent}%")
        self.cpu_progress['value'] = cpu_percent
        
        # 内存使用率
        mem = psutil.virtual_memory()
        mem_percent = mem.percent
        self.mem_value_label.config(text=f"{mem_percent}%")
        self.mem_progress['value'] = mem_percent
        
        # 磁盘读写速度
        current_disk_io = psutil.disk_io_counters()
        current_time = time.time()
        time_delta = current_time - self.last_time
        if time_delta > 0:
            read_speed = (current_disk_io.read_bytes - self.last_disk_io.read_bytes) / time_delta / 1024
            write_speed = (current_disk_io.write_bytes - self.last_disk_io.write_bytes) / time_delta / 1024
            self.disk_read_value_label.config(text=f"{read_speed:.1f} KB/s")
            self.disk_write_value_label.config(text=f"{write_speed:.1f} KB/s")
        self.last_disk_io = current_disk_io
        
        # 网络速度
        current_net_io = psutil.net_io_counters()
        if time_delta > 0:
            upload_speed = (current_net_io.bytes_sent - self.last_net_io.bytes_sent) / time_delta / 1024
            download_speed = (current_net_io.bytes_recv - self.last_net_io.bytes_recv) / time_delta / 1024
            self.net_upload_label.config(text=f"{upload_speed:.1f} KB/s")
            self.net_download_label.config(text=f"{download_speed:.1f} KB/s")
        self.last_net_io = current_net_io
        
        # Temp目录空间
        temp_space = self.get_temp_directory_space()
        self.temp_value_label.config(text=temp_space)
        
        # 更新硬盘信息
        self.update_disk_info()
        
        self.last_time = current_time
        
        # 1秒后刷新
        self.root.after(1000, self.update_performance)
    
    def get_temp_directory_space(self) -> str:
        """获取临时目录空间信息
        
        Returns:
            str: 临时目录大小,格式为"X.XXGB"
        """
        temp_dir = tempfile.gettempdir()
        current_time = time.time()
        
        # 检查缓存是否有效
        if temp_dir in self.temp_space_cache and current_time - self.temp_cache_time < CACHE_DURATION:
            return self.temp_space_cache[temp_dir]
        
        try:
            # 计算temp目录的实际大小(使用迭代方式,避免递归栈溢出)
            def get_dir_size(path: str) -> int:
                """计算目录大小
                
                Args:
                    path: 目录路径
                    
                Returns:
                    int: 目录大小(字节)
                """
                total = 0
                stack = [path]
                
                while stack:
                    current_path = stack.pop()
                    try:
                        for entry in os.scandir(current_path):
                            if entry.is_file():
                                try:
                                    total += entry.stat().st_size
                                except:
                                    pass
                            elif entry.is_dir():
                                stack.append(entry.path)
                    except:
                        pass
                return total
            
            temp_size = get_dir_size(temp_dir)
            temp_gb = round(temp_size / BYTES_TO_GB, 2)
            result = f"{temp_gb}GB"
            
            # 更新缓存
            self.temp_space_cache[temp_dir] = result
            self.temp_cache_time = current_time
            
            return result
        except Exception as e:
            return f"无法获取Temp目录信息: {str(e)}"
    
    def open_temp_directory(self):
        """打开临时目录
        
        使用webbrowser.open打开临时目录,提高安全性
        """
        temp_dir = tempfile.gettempdir()
        try:
            # 使用webbrowser.open打开目录,更加安全
            webbrowser.open(f'file://{temp_dir}')
        except Exception as e:
            # 降级方案
            try:
                if platform.system() == 'Windows':
                    os.startfile(temp_dir)
                elif platform.system() == 'Darwin':
                    os.system(f'open "{temp_dir}"')
                else:
                    os.system(f'xdg-open "{temp_dir}"')
            except Exception as e2:
                ttkb.dialogs.Messagebox.show_error("错误", f"无法打开Temp目录: {str(e2)}")
    
    def update_disk_info(self):
        """更新硬盘容量信息
        
        获取所有磁盘分区信息,更新UI显示,根据剩余空间和是否为系统盘设置不同的样式
        """
        # 获取系统盘
        system_drive = os.environ.get('SYSTEMDRIVE', 'C:')
        
        # 获取所有磁盘分区
        disk_partitions = psutil.disk_partitions()
        
        # 过滤有效分区
        valid_partitions = []
        for partition in disk_partitions:
            if 'cdrom' not in partition.opts and partition.fstype != '':
                try:
                    usage = psutil.disk_usage(partition.mountpoint)
                    total_gb = round(usage.total / BYTES_TO_GB, 2)
                    used_gb = round(usage.used / BYTES_TO_GB, 2)
                    free_gb = round(usage.free / BYTES_TO_GB, 2)
                    percent = usage.percent
                    
                    # 确定样式
                    is_system_disk = partition.mountpoint.startswith(system_drive)
                    is_low_space = free_gb < LOW_SPACE_THRESHOLD
                    
                    if is_low_space:
                        bootstyle = "danger"
                    elif is_system_disk:
                        bootstyle = "primary"
                    else:
                        bootstyle = "secondary"
                    
                    valid_partitions.append({
                        'mountpoint': partition.mountpoint,
                        'used_gb': used_gb,
                        'total_gb': total_gb,
                        'percent': percent,
                        'bootstyle': bootstyle
                    })
                except Exception as e:
                    # 记录错误但不中断执行
                    pass
        
        # 获取现有控件
        existing_widgets = self.disk_info_frame.winfo_children()
        widget_count = len(existing_widgets) // 2  # 每个分区有两个标签
        
        # 更新或创建控件
        for i, partition in enumerate(valid_partitions):
            if i * 2 < len(existing_widgets):
                # 更新现有控件
                disk_label = existing_widgets[i * 2]
                disk_info = existing_widgets[i * 2 + 1]
                
                disk_label.config(
                    text=partition['mountpoint'],
                    bootstyle=partition['bootstyle']
                )
                disk_info.config(
                    text=f"{partition['used_gb']}GB / {partition['total_gb']}GB ({partition['percent']}%)",
                    bootstyle=partition['bootstyle']
                )
            else:
                # 创建新控件
                disk_label = ttkb.Label(
                    self.disk_info_frame,
                    text=partition['mountpoint'],
                    font=self.fonts['small'],
                    bootstyle=partition['bootstyle']
                )
                disk_label.grid(row=i, column=0, padx=10, pady=2, sticky=W)
                
                disk_info = ttkb.Label(
                    self.disk_info_frame,
                    text=f"{partition['used_gb']}GB / {partition['total_gb']}GB ({partition['percent']}%)",
                    font=self.fonts['small'],
                    bootstyle=partition['bootstyle']
                )
                disk_info.grid(row=i, column=1, padx=10, pady=2, sticky=W)
        
        # 隐藏多余的控件
        for i in range(len(valid_partitions), widget_count):
            if i * 2 < len(existing_widgets):
                existing_widgets[i * 2].grid_forget()
                existing_widgets[i * 2 + 1].grid_forget()
        
        # 配置网格列
        self.disk_info_frame.grid_columnconfigure(1, weight=1)

if __name__ == "__main__":
    # 使用cosmo主题,可替换为flatly、superhero等
    root = ttkb.Window(themename="cosmo")
    
    # 立即隐藏窗口,避免闪烁
    root.withdraw()
    
    # 设置窗口图标
    try:
        # 判断是否为打包环境
        if getattr(sys, 'frozen', False):
            # 打包环境
            base_path = sys._MEIPASS
        else:
            # 开发环境
            base_path = os.path.dirname(os.path.abspath(__file__))
        
        icon_path = os.path.join(base_path, "app_ico.ico")
        root.iconbitmap(icon_path)
    except:
        pass
    
    app = SystemMonitorApp(root)
    root.mainloop()


V1.1
优化导出功能


蓝奏云 : 下载:https://wwbbs.lanzouq.com/iBaR53k718id 密码:52pj 密码:52pj    解压密码 52pj

image.png (71.16 KB, 下载次数: 2)

硬件信息

硬件信息

image.png (43.7 KB, 下载次数: 1)

监控信息

监控信息

image.png (19.73 KB, 下载次数: 1)

导出功能

导出功能

免费评分

参与人数 9吾爱币 +14 热心值 +9 收起 理由
z_jing + 2 + 1 还不错,提一点建议,处理器信息不是很直观,建议优化一下。如果能支持联网 ...
qsj521521 + 1 + 1 谢谢@Thanks!
hjk08 + 1 我很赞同!
cd1688 + 1 谢谢@Thanks!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
gunxsword + 1 + 1 我很赞同!
naixubao + 1 + 1 谢谢@Thanks!
邪汐 + 1 + 1 我很赞同!
cw6619 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
dork 发表于 2026-3-9 15:11
修复楼主发布的导出功能失败的BUG:
[Python] 纯文本查看 复制代码
import tkinter as tk
from tkinter import ttk
import ttkbootstrap as ttkb
from ttkbootstrap.constants import *
import psutil
import wmi
import platform
import time
from datetime import datetime
import os
import tempfile
import sys
import webbrowser
 
# 常量定义
BYTES_TO_GB = 1024 ** 3
CACHE_DURATION = 60  # 缓存1分钟
LOW_SPACE_THRESHOLD = 10  # 低空间阈值(GB)
 
class SystemMonitorApp:
    """电脑配置助手应用类
     
    功能:
    - 显示硬件信息
    - 监控系统性能
    - 管理Temp目录
    - 导出硬件信息
    """
     
    def __init__(self, root: tk.Tk):
        """初始化应用
         
        Args:
            root: tkinter根窗口
        """
        self.root = root
        self.root.title("电脑配置助手")
         
        # 屏幕居中 - 必须在withdraw之前设置
        width = 800
        height = 600
        x = (self.root.winfo_screenwidth() // 2) - (width // 2)
        y = (self.root.winfo_screenheight() // 2) - (height // 2)
        self.root.geometry(f"{width}x{height}+{x}+{y}")
         
        # 支持窗口大小调整
        self.root.resizable(True, True)
         
        # 统一字体和颜色风格
        self.fonts = {
            'title': ("Arial", 12, "bold"),
            'label': ("Arial", 12),
            'small': ("Arial", 10)
        }
         
        # 初始化缓存
        self.temp_space_cache = {}
        self.temp_cache_time = 0
         
        # 创建标签页
        self.notebook = ttkb.Notebook(root)
        self.notebook.pack(fill=BOTH, expand=True, padx=10, pady=10)
         
        # 硬件信息页
        self.hardware_frame = ttkb.Frame(self.notebook)
        self.notebook.add(self.hardware_frame, text="硬件信息")
        self._create_hardware_tab()
         
        # 性能监控页
        self.performance_frame = ttkb.Frame(self.notebook)
        self.notebook.add(self.performance_frame, text="性能监控")
        self._create_performance_tab()
         
        # 延迟初始化WMI和性能监控(在窗口显示后)
        self.root.after(100, self._delayed_init)
 
    def _delayed_init(self):
        """延迟初始化
         
        在窗口显示后初始化WMI和性能监控,避免阻塞UI
        """
        # 显示窗口(UI创建完成后)
        self.root.deiconify()
         
        # 延迟加载硬件信息,让用户看到加载动画
        self.root.after(1000, self._load_hardware_info)
 
    def _load_hardware_info(self):
        """加载硬件信息
         
        初始化WMI对象,加载硬件信息,启动性能监控
        """
        # 初始化WMI对象(在窗口显示后执行,不阻塞UI)
        self.wmi_obj = wmi.WMI()
        self.last_disk_io = psutil.disk_io_counters()
        self.last_net_io = psutil.net_io_counters()
        self.last_time = time.time()
         
        # 加载硬件信息
        self.refresh_hardware_info()
         
        # 启动性能监控
        self.update_performance()
 
    def _create_hardware_tab(self):
        # 创建滚动条
        scrollbar = ttk.Scrollbar(self.hardware_frame, orient="vertical")
         
        # 硬件信息表格
        self.hardware_tree = ttkb.Treeview(
            self.hardware_frame,
            columns=("硬件信息", "硬件内容"),
            show="headings",
            bootstyle="secondary",
            yscrollcommand=scrollbar.set
        )
         
        # 关联滚动条
        scrollbar.config(command=self.hardware_tree.yview)
         
        self.hardware_tree.heading("硬件信息", text="硬件信息")
        self.hardware_tree.heading("硬件内容", text="硬件内容")
        self.hardware_tree.column("硬件信息", width=200, anchor=W)
        self.hardware_tree.column("硬件内容", width=500, anchor=W)
         
        # 添加表格线 - 使用标准ttk样式
        style = ttk.Style()
        style.configure("Treeview", 
                      rowheight=25, 
                      borderwidth=1,
                      relief="solid")
        style.configure("Treeview.Heading", 
                      borderwidth=1,
                      relief="solid")
        style.map("Treeview", 
                  background=[("selected", "#0078d7")],
                  foreground=[("selected", "white")])
        style.configure("Treeview.Cell", 
                      borderwidth=1)
         
        # 使用grid布局来放置表格和滚动条
        self.hardware_tree.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
        scrollbar.grid(row=0, column=1, sticky="ns", pady=10)
         
        # 配置网格列权重
        self.hardware_frame.grid_columnconfigure(0, weight=1)
        self.hardware_frame.grid_rowconfigure(0, weight=1)
         
        # 加载中提示
        self.loading_label = ttkb.Label(
            self.hardware_frame,
            text="正在加载硬件信息...",
            font=self.fonts['label'],
            bootstyle="secondary"
        )
        self.loading_label.grid(row=1, column=0, columnspan=2, pady=10)
         
        # 加载动画
        self.loading_progress = ttkb.Progressbar(
            self.hardware_frame,
            bootstyle="striped",
            mode="indeterminate",
            maximum=100
        )
        self.loading_progress.grid(row=2, column=0, columnspan=2, sticky="ew", padx=50, pady=5)
        self.loading_progress.start(10)
         
        # 按钮栏
        btn_frame = ttkb.Frame(self.hardware_frame)
        btn_frame.grid(row=3, column=0, columnspan=2, sticky="ew", padx=10, pady=5)
         
        refresh_btn = ttkb.Button(
            btn_frame,
            text="刷新",
            command=self.refresh_hardware_info,
            bootstyle=PRIMARY
        )
        refresh_btn.pack(side=LEFT, padx=5)
         
        export_btn = ttkb.Button(
            btn_frame,
            text="导出",
            command=self.export_hardware_info,
            bootstyle=INFO
        )
        export_btn.pack(side=LEFT, padx=5)
 
    def _get_hardware_info(self):
        info = []
         
        # 系统信息
        info.extend(self._get_system_info())
        # CPU信息
        info.extend(self._get_cpu_info())
        # 主板信息
        info.extend(self._get_motherboard_info())
        # 内存信息
        info.extend(self._get_memory_info())
        # 显卡信息
        info.extend(self._get_gpu_info())
        # 硬盘信息
        info.extend(self._get_disk_info())
        # 分区信息
        info.extend(self._get_partition_info())
        # 网卡信息
        info.extend(self._get_network_info())
        # 电池信息
        info.extend(self._get_battery_info())
         
        return info
     
    def _get_system_info(self):
        """获取系统信息"""
        info = []
        info.append(("操作系统", platform.platform()))
        info.append(("系统版本", platform.version()))
        info.append(("系统架构", platform.architecture()[0]))
        return info
     
    def _get_cpu_info(self):
        """获取CPU信息"""
        info = []
        try:
            cpu_info = platform.processor()
            cpu_freq = psutil.cpu_freq().current if psutil.cpu_freq() else "N/A"
            cpu_cores = psutil.cpu_count(logical=False)
            cpu_logical = psutil.cpu_count(logical=True)
            info.append(("处理器", f"{cpu_info}"))
            info.append(("处理器频率", f"{cpu_freq:.2f}MHz" if isinstance(cpu_freq, (int, float)) else "N/A"))
            info.append(("核心数", f"{cpu_cores} 核心 {cpu_logical} 线程"))
        except Exception as e:
            info.append(("处理器", f"获取失败: {str(e)}"))
        return info
     
    def _get_motherboard_info(self):
        """获取主板信息"""
        info = []
        try:
            for board in self.wmi_obj.Win32_BaseBoard():
                info.append(("主板型号", board.Product))
                info.append(("主板制造商", board.Manufacturer))
                info.append(("主板序列号", board.SerialNumber))
        except Exception as e:
            info.append(("主板信息", f"获取失败: {str(e)}"))
        return info
     
    def _get_memory_info(self):
        """获取内存信息"""
        info = []
        try:
            mem_total = round(psutil.virtual_memory().total / (1024**3), 2)
            mem_available = round(psutil.virtual_memory().available / (1024**3), 2)
            info.append(("内存总容量", f"{mem_total} GB"))
            info.append(("可用内存", f"{mem_available} GB"))
            info.append(("内存使用率", f"{psutil.virtual_memory().percent}%"))
             
            slot_count = 1
            for mem in self.wmi_obj.Win32_PhysicalMemory():
                slot = mem.DeviceLocator if mem.DeviceLocator else f"插槽{slot_count}"
                capacity = int(mem.Capacity)//(1024**3)
                speed = mem.Speed if mem.Speed else "未知"
                manufacturer = mem.Manufacturer if mem.Manufacturer else "未知"
                info.append((f"内存{slot}", f"{manufacturer} {capacity}GB {speed}MHz"))
                slot_count += 1
        except Exception as e:
            info.append(("内存信息", f"获取失败: {str(e)}"))
        return info
     
    def _get_gpu_info(self):
        """获取显卡信息"""
        info = []
        try:
            gpu_count = 1
            for gpu in self.wmi_obj.Win32_VideoController():
                info.append((f"显卡{gpu_count}", gpu.Name))
                if hasattr(gpu, 'AdapterRAM') and gpu.AdapterRAM:
                    vram = int(gpu.AdapterRAM)/(1024**3)
                    info.append((f"显存{gpu_count}", f"{vram:.1f}GB"))
                gpu_count += 1
        except Exception as e:
            info.append(("显卡信息", f"获取失败: {str(e)}"))
        return info
     
    def _get_disk_info(self):
        """获取硬盘信息"""
        info = []
        try:
            disk_count = 1
            for disk in self.wmi_obj.Win32_DiskDrive():
                disk_size = round(int(disk.Size)/(1024**3), 2)
                info.append((f"硬盘{disk_count}型号", disk.Model))
                info.append((f"硬盘{disk_count}容量", f"{disk_size}GB"))
                info.append((f"硬盘{disk_count}序列号", disk.SerialNumber.strip()))
                info.append((f"硬盘{disk_count}接口", disk.InterfaceType))
                disk_count += 1
        except Exception as e:
            info.append(("硬盘信息", f"获取失败: {str(e)}"))
        return info
     
    def _get_partition_info(self):
        """获取分区信息"""
        info = []
        try:
            for part in psutil.disk_partitions():
                if 'cdrom' not in part.opts and part.fstype != '':
                    try:
                        usage = psutil.disk_usage(part.mountpoint)
                        total_gb = round(usage.total/(1024**3), 2)
                        used_gb = round(usage.used/(1024**3), 2)
                        used_percent = usage.percent
                        info.append((
                            f"分区 {part.mountpoint}",
                            f"{total_gb}GB 总容量,{used_gb}GB 已用 ({used_percent}%)"
                        ))
                    except Exception as e:
                        info.append((f"分区 {part.mountpoint}", f"获取失败: {str(e)}"))
        except Exception as e:
            info.append(("分区信息", f"获取失败: {str(e)}"))
        return info
     
    def _get_network_info(self):
        """获取网卡信息"""
        info = []
        try:
            nic_count = 1
            for nic in self.wmi_obj.Win32_NetworkAdapterConfiguration():
                if nic.IPEnabled:
                    info.append((f"网卡{nic_count}", nic.Description))
                    info.append((f"MAC地址{nic_count}", nic.MACAddress))
                    if nic.IPAddress:
                        info.append((f"IP地址{nic_count}", nic.IPAddress[0]))
                    nic_count += 1
        except Exception as e:
            info.append(("网卡信息", f"获取失败: {str(e)}"))
        return info
     
    def _get_battery_info(self):
        """获取电池信息"""
        info = []
        try:
            battery = psutil.sensors_battery()
            if battery:
                info.append(("电池电量", f"{battery.percent}%"))
                info.append(("电池状态", "充电中" if battery.power_plugged else "放电中"))
        except Exception as e:
            # 笔记本才会有电池,所以这里的异常可以忽略
            pass
        return info
 
    def refresh_hardware_info(self):
        # 清空表格
        for item in self.hardware_tree.get_children():
            self.hardware_tree.delete(item)
         
        # 分批插入新信息
        hardware_info = self._get_hardware_info()
         
        def insert_batch(start=0, batch_size=10):
            end = min(start + batch_size, len(hardware_info))
            for i in range(start, end):
                component, detail = hardware_info[i]
                self.hardware_tree.insert("", END, values=(component, detail))
            if end < len(hardware_info):
                self.root.after(50, insert_batch, end, batch_size)
            else:
                # 隐藏加载动画
                if hasattr(self, 'loading_label'):
                    self.loading_label.grid_forget()
                if hasattr(self, 'loading_progress'):
                    self.loading_progress.stop()
                    self.loading_progress.grid_forget()
         
        insert_batch()
 
    def export_hardware_info(self):
        hardware_info = self._get_hardware_info()
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        # 确保导出到主程序所在目录
        #program_dir = os.path.dirname(os.path.abspath(__file__))
        program_dir = os.getcwd()
        filename = os.path.join(program_dir, f"硬件信息_{timestamp}.txt")
        with open(filename, "w", encoding="utf-8") as f:
            f.write("电脑硬件信息\n")
            f.write("="*30 + "\n")
            for component, detail in hardware_info:
                f.write(f"{component}: {detail}\n")
        ttkb.dialogs.Messagebox.show_info("导出成功", f"硬件信息已导出到:\n{filename}")
 
    def _create_performance_tab(self):
        # CPU使用率
        cpu_label = ttkb.Label(self.performance_frame, text="CPU使用率", font=self.fonts['label'])
        cpu_label.grid(row=0, column=0, padx=10, pady=5, sticky=W)
        self.cpu_value_label = ttkb.Label(self.performance_frame, text="0%", font=self.fonts['label'])
        self.cpu_value_label.grid(row=0, column=1, padx=10, pady=5, sticky=W)
        self.cpu_progress = ttkb.Progressbar(
            self.performance_frame,
            bootstyle=PRIMARY,
            maximum=100
        )
        self.cpu_progress.grid(row=0, column=2, padx=10, pady=5, sticky=EW)
         
        # 内存使用率
        mem_label = ttkb.Label(self.performance_frame, text="内存使用率", font=self.fonts['label'])
        mem_label.grid(row=1, column=0, padx=10, pady=5, sticky=W)
        self.mem_value_label = ttkb.Label(self.performance_frame, text="0%", font=self.fonts['label'])
        self.mem_value_label.grid(row=1, column=1, padx=10, pady=5, sticky=W)
        self.mem_progress = ttkb.Progressbar(
            self.performance_frame,
            bootstyle=SUCCESS,
            maximum=100
        )
        self.mem_progress.grid(row=1, column=2, padx=10, pady=5, sticky=EW)
         
        # 磁盘读取
        disk_read_label = ttkb.Label(self.performance_frame, text="磁盘读取", font=self.fonts['label'])
        disk_read_label.grid(row=2, column=0, padx=10, pady=5, sticky=W)
        self.disk_read_value_label = ttkb.Label(self.performance_frame, text="0 KB/s", font=self.fonts['label'])
        self.disk_read_value_label.grid(row=2, column=1, padx=10, pady=5, sticky=W)
         
        # 磁盘写入
        disk_write_label = ttkb.Label(self.performance_frame, text="磁盘写入", font=self.fonts['label'])
        disk_write_label.grid(row=3, column=0, padx=10, pady=5, sticky=W)
        self.disk_write_value_label = ttkb.Label(self.performance_frame, text="0 KB/s", font=self.fonts['label'])
        self.disk_write_value_label.grid(row=3, column=1, padx=10, pady=5, sticky=W)
         
        # 网络上传速度
        net_upload_label = ttkb.Label(self.performance_frame, text="网络上传", font=self.fonts['label'])
        net_upload_label.grid(row=4, column=0, padx=10, pady=5, sticky=W)
        self.net_upload_label = ttkb.Label(self.performance_frame, text="0 KB/s", font=self.fonts['label'])
        self.net_upload_label.grid(row=4, column=1, padx=10, pady=5, sticky=W)
         
        # 网络下载速度
        net_download_label = ttkb.Label(self.performance_frame, text="网络下载", font=self.fonts['label'])
        net_download_label.grid(row=5, column=0, padx=10, pady=5, sticky=W)
        self.net_download_label = ttkb.Label(self.performance_frame, text="0 KB/s", font=self.fonts['label'])
        self.net_download_label.grid(row=5, column=1, padx=10, pady=5, sticky=W)
         
        # Temp目录空间
        temp_label = ttkb.Label(self.performance_frame, text="Temp目录", font=self.fonts['label'])
        temp_label.grid(row=6, column=0, padx=10, pady=5, sticky=W)
        self.temp_value_label = ttkb.Label(self.performance_frame, text="计算中...", font=self.fonts['label'])
        self.temp_value_label.grid(row=6, column=1, padx=10, pady=5, sticky=W)
         
        # 打开Temp目录按钮
        open_temp_btn = ttkb.Button(
            self.performance_frame,
            text="打开Temp",
            command=self.open_temp_directory,
            bootstyle=INFO
        )
        open_temp_btn.grid(row=6, column=2, padx=10, pady=5, sticky=W)
         
        # 硬盘容量监控
        disk_frame = ttkb.LabelFrame(self.performance_frame, text="硬盘容量监控")
        disk_frame.grid(row=7, column=0, columnspan=3, padx=10, pady=10, sticky=EW)
         
        # 硬盘信息容器
        self.disk_info_frame = ttkb.Frame(disk_frame)
        self.disk_info_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)
         
        # 网格列权重配置
        self.performance_frame.grid_columnconfigure(1, weight=1)
        self.performance_frame.grid_columnconfigure(2, weight=2)
 
    def update_performance(self):
        # CPU使用率
        cpu_percent = psutil.cpu_percent(interval=0)
        self.cpu_value_label.config(text=f"{cpu_percent}%")
        self.cpu_progress['value'] = cpu_percent
         
        # 内存使用率
        mem = psutil.virtual_memory()
        mem_percent = mem.percent
        self.mem_value_label.config(text=f"{mem_percent}%")
        self.mem_progress['value'] = mem_percent
         
        # 磁盘读写速度
        current_disk_io = psutil.disk_io_counters()
        current_time = time.time()
        time_delta = current_time - self.last_time
        if time_delta > 0:
            read_speed = (current_disk_io.read_bytes - self.last_disk_io.read_bytes) / time_delta / 1024
            write_speed = (current_disk_io.write_bytes - self.last_disk_io.write_bytes) / time_delta / 1024
            self.disk_read_value_label.config(text=f"{read_speed:.1f} KB/s")
            self.disk_write_value_label.config(text=f"{write_speed:.1f} KB/s")
        self.last_disk_io = current_disk_io
         
        # 网络速度
        current_net_io = psutil.net_io_counters()
        if time_delta > 0:
            upload_speed = (current_net_io.bytes_sent - self.last_net_io.bytes_sent) / time_delta / 1024
            download_speed = (current_net_io.bytes_recv - self.last_net_io.bytes_recv) / time_delta / 1024
            self.net_upload_label.config(text=f"{upload_speed:.1f} KB/s")
            self.net_download_label.config(text=f"{download_speed:.1f} KB/s")
        self.last_net_io = current_net_io
         
        # Temp目录空间
        temp_space = self.get_temp_directory_space()
        self.temp_value_label.config(text=temp_space)
         
        # 更新硬盘信息
        self.update_disk_info()
         
        self.last_time = current_time
         
        # 1秒后刷新
        self.root.after(1000, self.update_performance)
     
    def get_temp_directory_space(self) -> str:
        """获取临时目录空间信息
         
        Returns:
            str: 临时目录大小,格式为"X.XXGB"
        """
        temp_dir = tempfile.gettempdir()
        current_time = time.time()
         
        # 检查缓存是否有效
        if temp_dir in self.temp_space_cache and current_time - self.temp_cache_time < CACHE_DURATION:
            return self.temp_space_cache[temp_dir]
         
        try:
            # 计算temp目录的实际大小(使用迭代方式,避免递归栈溢出)
            def get_dir_size(path: str) -> int:
                """计算目录大小
                 
                Args:
                    path: 目录路径
                     
                Returns:
                    int: 目录大小(字节)
                """
                total = 0
                stack = [path]
                 
                while stack:
                    current_path = stack.pop()
                    try:
                        for entry in os.scandir(current_path):
                            if entry.is_file():
                                try:
                                    total += entry.stat().st_size
                                except:
                                    pass
                            elif entry.is_dir():
                                stack.append(entry.path)
                    except:
                        pass
                return total
             
            temp_size = get_dir_size(temp_dir)
            temp_gb = round(temp_size / BYTES_TO_GB, 2)
            result = f"{temp_gb}GB"
             
            # 更新缓存
            self.temp_space_cache[temp_dir] = result
            self.temp_cache_time = current_time
             
            return result
        except Exception as e:
            return f"无法获取Temp目录信息: {str(e)}"
     
    def open_temp_directory(self):
        """打开临时目录
         
        使用webbrowser.open打开临时目录,提高安全性
        """
        temp_dir = tempfile.gettempdir()
        try:
            # 使用webbrowser.open打开目录,更加安全
            webbrowser.open(f'file://{temp_dir}')
        except Exception as e:
            # 降级方案
            try:
                if platform.system() == 'Windows':
                    os.startfile(temp_dir)
                elif platform.system() == 'Darwin':
                    os.system(f'open "{temp_dir}"')
                else:
                    os.system(f'xdg-open "{temp_dir}"')
            except Exception as e2:
                ttkb.dialogs.Messagebox.show_error("错误", f"无法打开Temp目录: {str(e2)}")
     
    def update_disk_info(self):
        """更新硬盘容量信息
         
        获取所有磁盘分区信息,更新UI显示,根据剩余空间和是否为系统盘设置不同的样式
        """
        # 获取系统盘
        system_drive = os.environ.get('SYSTEMDRIVE', 'C:')
         
        # 获取所有磁盘分区
        disk_partitions = psutil.disk_partitions()
         
        # 过滤有效分区
        valid_partitions = []
        for partition in disk_partitions:
            if 'cdrom' not in partition.opts and partition.fstype != '':
                try:
                    usage = psutil.disk_usage(partition.mountpoint)
                    total_gb = round(usage.total / BYTES_TO_GB, 2)
                    used_gb = round(usage.used / BYTES_TO_GB, 2)
                    free_gb = round(usage.free / BYTES_TO_GB, 2)
                    percent = usage.percent
                     
                    # 确定样式
                    is_system_disk = partition.mountpoint.startswith(system_drive)
                    is_low_space = free_gb < LOW_SPACE_THRESHOLD
                     
                    if is_low_space:
                        bootstyle = "danger"
                    elif is_system_disk:
                        bootstyle = "primary"
                    else:
                        bootstyle = "secondary"
                     
                    valid_partitions.append({
                        'mountpoint': partition.mountpoint,
                        'used_gb': used_gb,
                        'total_gb': total_gb,
                        'percent': percent,
                        'bootstyle': bootstyle
                    })
                except Exception as e:
                    # 记录错误但不中断执行
                    pass
         
        # 获取现有控件
        existing_widgets = self.disk_info_frame.winfo_children()
        widget_count = len(existing_widgets) // 2  # 每个分区有两个标签
         
        # 更新或创建控件
        for i, partition in enumerate(valid_partitions):
            if i * 2 < len(existing_widgets):
                # 更新现有控件
                disk_label = existing_widgets[i * 2]
                disk_info = existing_widgets[i * 2 + 1]
                 
                disk_label.config(
                    text=partition['mountpoint'],
                    bootstyle=partition['bootstyle']
                )
                disk_info.config(
                    text=f"{partition['used_gb']}GB / {partition['total_gb']}GB ({partition['percent']}%)",
                    bootstyle=partition['bootstyle']
                )
            else:
                # 创建新控件
                disk_label = ttkb.Label(
                    self.disk_info_frame,
                    text=partition['mountpoint'],
                    font=self.fonts['small'],
                    bootstyle=partition['bootstyle']
                )
                disk_label.grid(row=i, column=0, padx=10, pady=2, sticky=W)
                 
                disk_info = ttkb.Label(
                    self.disk_info_frame,
                    text=f"{partition['used_gb']}GB / {partition['total_gb']}GB ({partition['percent']}%)",
                    font=self.fonts['small'],
                    bootstyle=partition['bootstyle']
                )
                disk_info.grid(row=i, column=1, padx=10, pady=2, sticky=W)
         
        # 隐藏多余的控件
        for i in range(len(valid_partitions), widget_count):
            if i * 2 < len(existing_widgets):
                existing_widgets[i * 2].grid_forget()
                existing_widgets[i * 2 + 1].grid_forget()
         
        # 配置网格列
        self.disk_info_frame.grid_columnconfigure(1, weight=1)
 
if __name__ == "__main__":
    # 使用cosmo主题,可替换为flatly、superhero等
    root = ttkb.Window(themename="cosmo")
     
    # 立即隐藏窗口,避免闪烁
    root.withdraw()
     
    # 设置窗口图标
    try:
        # 判断是否为打包环境
        if getattr(sys, 'frozen', False):
            # 打包环境
            base_path = sys._MEIPASS
        else:
            # 开发环境
            base_path = os.path.dirname(os.path.abspath(__file__))
         
        icon_path = os.path.join(base_path, "app_ico.ico")
        root.iconbitmap(icon_path)
    except:
        pass
     
    app = SystemMonitorApp(root)
    root.mainloop()

推荐
dork 发表于 2026-3-9 14:32
本帖最后由 dork 于 2026-3-9 14:38 编辑

有导出功能,但有问题:

# 确保导出到主程序所在目录

        program_dir = os.path.dirname(os.path.abspath(__file__))

        filename = os.path.join(program_dir, f"硬件信息_{timestamp}.txt")

实际是没有导出到TXT文件。
沙发
fxw520 发表于 2026-3-9 10:18
好东西,给每个人发一个 运行  获取截图 统计
3#
fengxixiu 发表于 2026-3-9 10:37
感谢分享
4#
zhangshiqiang20 发表于 2026-3-9 10:51
支持下楼主,顶一下
5#
zyf4880628 发表于 2026-3-9 11:01
公司每次过完年都要资产盘点一步一步点开查看,先试用看看,谢谢楼主
6#
Maycie 发表于 2026-3-9 11:05
感谢楼主分享
7#
kingmars 发表于 2026-3-9 12:26
简单监测工具好评
8#
极速星辰 发表于 2026-3-9 12:56
还不错,,可以
9#
xjshuaishuai 发表于 2026-3-9 13:25
导出的文件找不见??
10#
haapy 发表于 2026-3-9 13:49
下载试试好不好用
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-10 06:59

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表