当前位置: 首页 > news >正文

USB设备端口识别监测嵌入式python3自动化测试脚本

软件版本:python3;编译器:IDLE编译器;库:PyAutoGUl库;cmd终端安装PyAutoGUl库命令:pip install pyautogui

一、应用场景简介

嵌入式设备测试开发中,开关机测试;监控特定USB设备的连接状态变化;记录USB端口连接断开次数;其它。——多设备测试,生产线测试,自动化测试

二、功能简介

监测电脑设备管理器的端口的连接(开机)和断开(关机),记录连接和断开的次数。

三、脚本GUI界面功能介绍

使用:配置好VOD,PID后(下方第3点描述VID,PID查看方式),点击开始监测,停止监测。

1.注:使用前查看设备端口是否被正常识别,端口是否可用。Ctrl+x,选择设备管理器,查看CMO端口。

2.日志保存路径:D:\0\test_log,不清空之前的日志,会保留之前的日志。

监控设备配置功能板块:可自行配置VID,PID(下方第3点描述VID,PID查看方式),输入后点击应用配置。

设备状态功能板块:显示端口名,连接时长。

本次监控统计功能板块:记录连接次数,断开次数,监控状态,设备连接断开状态,检查频率为:1s

事件日志功能板块:显示最新的设备连接断开信息。

最底部为监控使用的按钮,开始监控,停止监控,重置统计数量等。

3.查看VID,PID,快捷键ctal+x,设备管理器,端口右键,属性,事件。

代码如下。关注,点赞,欢迎留言评论区。

备注:日志一直在一个文档,不删除历史日志一直累计,有需可改

import tkinter as tk
from tkinter import ttk
import threading
import time
import sys
import os
from datetime import datetime
import json

try:
import serial.tools.list_ports
import serial
SERIAL_AVAILABLE = True
except ImportError:
SERIAL_AVAILABLE = False
print("请先安装pyserial模块: pip install pyserial")

# 目标设备的VID和PID
TARGET_VID = "05C6"
TARGET_PID = "902E"

# 日志保存路径
LOG_DIR = r"D:\0\test_log"
LOG_FILE = os.path.join(LOG_DIR, "usb_monitor_log.txt")
STATS_FILE = os.path.join(LOG_DIR, "usb_statistics.json")
DAILY_LOG_DIR = os.path.join(LOG_DIR, "daily_logs")

class USBMonitorApp:
def __init__(self, root):
self.root = root
self.root.title("USB设备监控器 - 带日志保存")
self.root.geometry("650x500")

# 状态变量
self.device_connected = False
self.monitoring_active = False
self.monitor_thread = None

# 本次监控统计(从开始监控时清零)
self.session_connection_count = 0 # 本次监控连接次数
self.session_disconnection_count = 0 # 本次监控断开次数
self.session_start_time = None # 本次监控开始时间
self.session_running_time = 0 # 本次监控运行时间(秒)

# 设备连接时间记录
self.device_connect_time = None
self.device_disconnect_time = None

# 日志列表
self.log_entries = []

# 创建日志目录
self.setup_log_directory()

self.setup_ui()

# 检查依赖
if not SERIAL_AVAILABLE:
self.show_error("请先安装pyserial模块:\n打开CMD并运行: pip install pyserial")
return

# 记录程序启动
self.log_program_start()

# 默认自动开始监控
self.start_monitoring()

def setup_log_directory(self):
"""创建日志目录"""
try:
os.makedirs(LOG_DIR, exist_ok=True)
os.makedirs(DAILY_LOG_DIR, exist_ok=True)
print(f"日志目录已创建: {LOG_DIR}")
except Exception as e:
print(f"创建日志目录失败: {e}")

def setup_ui(self):
# 创建主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

# 标题和路径显示
title_frame = ttk.Frame(main_frame)
title_frame.grid(row=0, column=0, columnspan=3, pady=(0, 10), sticky=(tk.W, tk.E))

title_label = ttk.Label(title_frame, text="USB设备监控系统", font=('Arial', 16, 'bold'))
title_label.grid(row=0, column=0, sticky=tk.W)

# 显示日志路径
log_path_label = ttk.Label(title_frame, text=f"日志路径: {LOG_DIR}", font=('Arial', 8), foreground="gray")
log_path_label.grid(row=1, column=0, sticky=tk.W, pady=(2, 0))

# 设备信息
info_frame = ttk.LabelFrame(main_frame, text="监控设备信息", padding="10")
info_frame.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))

ttk.Label(info_frame, text="VID:").grid(row=0, column=0, sticky=tk.W)
ttk.Label(info_frame, text=TARGET_VID, font=('Arial', 10, 'bold'), foreground="blue").grid(row=0, column=1, sticky=tk.W, padx=(5, 20))

ttk.Label(info_frame, text="PID:").grid(row=0, column=2, sticky=tk.W)
ttk.Label(info_frame, text=TARGET_PID, font=('Arial', 10, 'bold'), foreground="blue").grid(row=0, column=3, sticky=tk.W, padx=(5, 0))

# 状态显示
status_frame = ttk.LabelFrame(main_frame, text="设备状态", padding="10")
status_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))

self.status_label = ttk.Label(status_frame, text="正在检测...", font=('Arial', 12))
self.status_label.grid(row=0, column=0, sticky=tk.W, padx=(10, 20))

self.status_indicator = tk.Canvas(status_frame, width=20, height=20, bg='gray')
self.status_indicator.grid(row=0, column=1, sticky=tk.W)

# 当前连接时长显示
self.connection_duration_label = ttk.Label(status_frame, text="连接时长: --:--:--", font=('Arial', 9))
self.connection_duration_label.grid(row=0, column=2, sticky=tk.W, padx=(30, 0))

# 连接统计(本次监控)
count_frame = ttk.LabelFrame(main_frame, text="本次监控统计", padding="10")
count_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))

# 连接次数
ttk.Label(count_frame, text="连接次数:").grid(row=0, column=0, sticky=tk.W)
self.connection_label = ttk.Label(count_frame, text="0", font=('Arial', 12, 'bold'), foreground="green")
self.connection_label.grid(row=0, column=1, sticky=tk.W, padx=(5, 30))

# 断开次数
ttk.Label(count_frame, text="断开次数:").grid(row=0, column=2, sticky=tk.W)
self.disconnection_label = ttk.Label(count_frame, text="0", font=('Arial', 12, 'bold'), foreground="red")
self.disconnection_label.grid(row=0, column=3, sticky=tk.W, padx=(5, 0))

# 运行时间
ttk.Label(count_frame, text="运行时间:").grid(row=0, column=4, sticky=tk.W, padx=(20, 5))
self.runtime_label = ttk.Label(count_frame, text="00:00:00", font=('Arial', 10, 'bold'))
self.runtime_label.grid(row=0, column=5, sticky=tk.W)

# 监控状态
ttk.Label(count_frame, text="监控状态:").grid(row=1, column=0, sticky=tk.W, pady=(5, 0))
self.monitoring_status_label = ttk.Label(count_frame, text="未开始", font=('Arial', 10, 'bold'))
self.monitoring_status_label.grid(row=1, column=1, sticky=tk.W, padx=(5, 30), pady=(5, 0))

# 当前状态
ttk.Label(count_frame, text="设备状态:").grid(row=1, column=2, sticky=tk.W, pady=(5, 0))
self.device_status_label = ttk.Label(count_frame, text="未知", font=('Arial', 10, 'bold'))
self.device_status_label.grid(row=1, column=3, sticky=tk.W, padx=(5, 0), pady=(5, 0))

# 检查频率
ttk.Label(count_frame, text="检查频率:").grid(row=1, column=4, sticky=tk.W, padx=(20, 5), pady=(5, 0))
self.check_freq_label = ttk.Label(count_frame, text="1秒", font=('Arial', 10, 'bold'))
self.check_freq_label.grid(row=1, column=5, sticky=tk.W, pady=(5, 0))

# 日志区域
log_frame = ttk.LabelFrame(main_frame, text="事件日志", padding="10")
log_frame.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))

# 创建日志框架
self.log_frame_inner = ttk.Frame(log_frame)
self.log_frame_inner.pack(fill=tk.BOTH, expand=True)

# 创建滚动条
scrollbar = ttk.Scrollbar(self.log_frame_inner)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

# 创建日志文本框
self.log_text = tk.Text(
self.log_frame_inner,
height=10,
width=70,
yscrollcommand=scrollbar.set,
bg='#f5f5f5',
relief=tk.FLAT,
font=('Consolas', 9)
)
self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.log_text.yview)

# 控制按钮
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=5, column=0, columnspan=3, pady=(10, 0))

self.start_button = ttk.Button(button_frame, text="▶ 开始监控", command=self.start_monitoring)
self.start_button.grid(row=0, column=0, padx=(0, 10))

self.stop_button = ttk.Button(button_frame, text="■ 停止监控", command=self.stop_monitoring, state='disabled')
self.stop_button.grid(row=0, column=1, padx=(0, 10))

self.reset_button = ttk.Button(button_frame, text="🔄 重置统计", command=self.reset_statistics)
self.reset_button.grid(row=0, column=2, padx=(0, 10))

self.clear_button = ttk.Button(button_frame, text="🗑️ 清空日志", command=self.clear_log)
self.clear_button.grid(row=0, column=3, padx=(0, 10))

self.copy_button = ttk.Button(button_frame, text="📋 复制日志", command=self.copy_log)
self.copy_button.grid(row=0, column=4, padx=(0, 10))

self.save_button = ttk.Button(button_frame, text="💾 保存日志", command=self.save_log)
self.save_button.grid(row=0, column=5, padx=(0, 10))

self.view_button = ttk.Button(button_frame, text="📂 打开日志", command=self.open_log_folder)
self.view_button.grid(row=0, column=6, padx=(0, 10))

self.quit_button = ttk.Button(button_frame, text="✕ 退出", command=self.quit_app)
self.quit_button.grid(row=0, column=7)

# 配置网格权重
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(0, weight=1)
main_frame.rowconfigure(4, weight=1)

# 初始化运行时间更新
self.update_runtime()
self.update_connection_duration()

def update_runtime(self):
"""更新运行时间"""
if self.monitoring_active and self.session_start_time:
elapsed = int(time.time() - self.session_start_time)
self.session_running_time = elapsed
hours = elapsed // 3600
minutes = (elapsed % 3600) // 60
seconds = elapsed % 60
self.runtime_label.config(text=f"{hours:02d}:{minutes:02d}:{seconds:02d}")

# 每秒更新一次
self.root.after(1000, self.update_runtime)

def update_connection_duration(self):
"""更新连接时长"""
if self.device_connected and self.device_connect_time:
elapsed = int(time.time() - self.device_connect_time)
hours = elapsed // 3600
minutes = (elapsed % 3600) // 60
seconds = elapsed % 60
self.connection_duration_label.config(text=f"连接时长: {hours:02d}:{minutes:02d}:{seconds:02d}")
else:
self.connection_duration_label.config(text="连接时长: --:--:--")

# 每秒更新一次
self.root.after(1000, self.update_connection_duration)

def check_device(self):
"""检查目标设备是否连接"""
try:
ports = serial.tools.list_ports.comports()
for port in ports:
if hasattr(port, 'vid') and port.vid:
vid_hex = f"{port.vid:04X}".upper()
pid_hex = f"{port.pid:04X}".upper() if port.pid else "0000"

if vid_hex == TARGET_VID and pid_hex == TARGET_PID:
return True, port.device, port.description
return False, None, None
except Exception as e:
self.log_event(f"检查设备时出错: {str(e)}", "error")
return False, None, None

def update_status(self, connected, port_info=None, description=None):
"""更新UI状态"""
current_status = "connected" if connected else "disconnected"

if connected and not self.device_connected:
# 设备刚刚连接
self.session_connection_count += 1
self.device_connected = True
self.device_connect_time = time.time()

# 更新UI
self.status_label.config(text=f"已连接 - {port_info}")
self.status_indicator.config(bg='green')
self.connection_label.config(text=str(self.session_connection_count))
self.device_status_label.config(text="已连接", foreground="green")

# 保存到文件
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] 设备连接 - 端口: {port_info}, 描述: {description}"

self.log_event(f"[{timestamp}] ✓ 设备已连接 - 端口: {port_info}, 描述: {description}", "connect")
self.save_to_log_file(log_entry, "CONNECT")

# 播放连接提示音
self.root.bell()

elif not connected and self.device_connected:
# 设备刚刚断开
self.session_disconnection_count += 1
self.device_connected = False

# 计算连接时长
duration = ""
if self.device_connect_time:
connect_duration = int(time.time() - self.device_connect_time)
hours = connect_duration // 3600
minutes = (connect_duration % 3600) // 60
seconds = connect_duration % 60
duration = f" (连接时长: {hours:02d}:{minutes:02d}:{seconds:02d})"
self.device_connect_time = None

# 更新UI
self.status_label.config(text="未连接")
self.status_indicator.config(bg='red')
self.disconnection_label.config(text=str(self.session_disconnection_count))
self.device_status_label.config(text="未连接", foreground="red")

# 保存到文件
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] 设备断开{duration}"

self.log_event(f"[{timestamp}] ✗ 设备已断开{duration}", "disconnect")
self.save_to_log_file(log_entry, "DISCONNECT")

# 播放断开提示音
self.root.bell()

elif connected and self.device_connected:
# 设备保持连接状态
self.status_label.config(text=f"已连接 - {port_info}")
self.status_indicator.config(bg='green')
self.device_status_label.config(text="已连接", foreground="green")

else:
# 设备保持断开状态
self.status_label.config(text="未连接")
self.status_indicator.config(bg='red')
self.device_status_label.config(text="未连接", foreground="red")

# 保存统计数据
self.save_statistics()

def reset_statistics(self):
"""重置统计信息"""
if self.monitoring_active:
response = tk.messagebox.askyesno("确认", "监控正在进行中,重置统计将清空当前数据。\n是否继续?")
if not response:
return

# 重置统计
self.session_connection_count = 0
self.session_disconnection_count = 0
self.session_running_time = 0

# 更新UI
self.connection_label.config(text="0")
self.disconnection_label.config(text="0")
self.runtime_label.config(text="00:00:00")
self.status_label.config(text="未连接")
self.status_indicator.config(bg='gray')
self.device_status_label.config(text="未知")
self.connection_duration_label.config(text="连接时长: --:--:--")

# 清空日志
self.clear_log()

timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.log_event(f"[{timestamp}] 🔄 统计信息已重置", "info")

# 如果是监控中,重新开始计时
if self.monitoring_active:
self.session_start_time = time.time()
self.log_event(f"[{timestamp}] ⏱️ 监控计时已重置", "info")

def save_to_log_file(self, message, event_type):
"""保存日志到文件"""
try:
# 保存到主日志文件
with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write(f"{message}\n")

# 保存到每日日志文件
today = datetime.now().strftime("%Y%m%d")
daily_log_file = os.path.join(DAILY_LOG_DIR, f"usb_log_{today}.txt")
with open(daily_log_file, 'a', encoding='utf-8') as f:
f.write(f"{message}\n")

except Exception as e:
self.log_event(f"保存日志到文件失败: {str(e)}", "error")

def save_to_detailed_log(self, data):
"""保存详细日志(JSON格式)"""
try:
detailed_log = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"event": data.get("event"),
"port": data.get("port"),
"description": data.get("description"),
"duration": data.get("duration"),
"session_connections": self.session_connection_count,
"session_disconnections": self.session_disconnection_count,
"session_runtime": self.session_running_time
}

today = datetime.now().strftime("%Y%m%d")
detailed_log_file = os.path.join(DAILY_LOG_DIR, f"detailed_log_{today}.json")

# 读取现有数据或创建新列表
if os.path.exists(detailed_log_file):
with open(detailed_log_file, 'r', encoding='utf-8') as f:
logs = json.load(f)
else:
logs = []

# 添加新日志
logs.append(detailed_log)

# 保存回文件
with open(detailed_log_file, 'w', encoding='utf-8') as f:
json.dump(logs, f, ensure_ascii=False, indent=2)

except Exception as e:
self.log_event(f"保存详细日志失败: {str(e)}", "error")

def log_event(self, message, event_type="info"):
"""添加日志条目到UI"""
timestamp = datetime.now().strftime("%H:%M:%S")

# 为不同事件类型设置不同颜色
if event_type == "connect":
tag_color = "green"
elif event_type == "disconnect":
tag_color = "red"
elif event_type == "error":
tag_color = "orange"
else:
tag_color = "black"

# 插入带时间戳的日志
self.log_text.insert(tk.END, message + "\n")

# 为最近的消息设置颜色标签
start_index = self.log_text.search(message.split("\n")[0], "end-1c linestart", stopindex="1.0")
if start_index:
end_index = f"{start_index}+{len(message)}c"
self.log_text.tag_add(event_type, start_index, end_index)
self.log_text.tag_config(event_type, foreground=tag_color)

self.log_text.see(tk.END) # 自动滚动到底部

# 保存到日志列表
self.log_entries.append(message)

def load_statistics(self):
"""加载历史统计数据(现在只用于读取,不影响本次监控统计)"""
try:
if os.path.exists(STATS_FILE):
with open(STATS_FILE, 'r', encoding='utf-8') as f:
stats = json.load(f)
# 只读取,不用于显示
total_connections = stats.get("total_connections", 0)
total_disconnections = stats.get("total_disconnections", 0)

# 记录到日志但不显示在UI
timestamp = datetime.now().strftime("%H:%M:%S")
self.log_event(f"[{timestamp}] 📊 历史统计: 总连接 {total_connections} 次, 总断开 {total_disconnections} 次", "info")

except Exception as e:
self.log_event(f"读取历史统计失败: {str(e)}", "error")

def save_statistics(self):
"""保存统计数据"""
try:
# 读取现有统计数据
if os.path.exists(STATS_FILE):
with open(STATS_FILE, 'r', encoding='utf-8') as f:
stats = json.load(f)
else:
stats = {}

# 更新统计数据
total_connections = stats.get("total_connections", 0) + self.session_connection_count
total_disconnections = stats.get("total_disconnections", 0) + self.session_disconnection_count

stats.update({
"total_connections": total_connections,
"total_disconnections": total_disconnections,
"last_updated": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"last_session_connections": self.session_connection_count,
"last_session_disconnections": self.session_disconnection_count,
"last_session_runtime": self.session_running_time
})

with open(STATS_FILE, 'w', encoding='utf-8') as f:
json.dump(stats, f, ensure_ascii=False, indent=2)

except Exception as e:
self.log_event(f"保存统计数据失败: {str(e)}", "error")

def log_program_start(self):
"""记录程序启动"""
try:
start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{start_time}] ========== 程序启动 =========="

# 保存到日志文件
with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write(f"\n{log_entry}\n")

# 在UI中显示
self.log_event(f"[{datetime.now().strftime('%H:%M:%S')}] ✅ 程序启动 - 日志保存到: {LOG_DIR}", "info")

# 加载历史统计(仅显示)
self.load_statistics()

except Exception as e:
self.log_event(f"记录程序启动失败: {str(e)}", "error")

def clear_log(self):
"""清空日志"""
self.log_text.delete(1.0, tk.END)
self.log_entries.clear()
self.log_event(f"[{datetime.now().strftime('%H:%M:%S')}] 📝 日志已清空", "info")

def copy_log(self):
"""复制日志到剪贴板"""
try:
log_content = self.log_text.get(1.0, tk.END).strip()
if log_content:
self.root.clipboard_clear()
self.root.clipboard_append(log_content)
self.log_event(f"[{datetime.now().strftime('%H:%M:%S')}] 日志已复制到剪贴板", "info")
except Exception as e:
self.log_event(f"复制日志时出错: {str(e)}", "error")

def save_log(self):
"""手动保存日志"""
try:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
manual_log_file = os.path.join(LOG_DIR, f"manual_save_{timestamp}.txt")

with open(manual_log_file, 'w', encoding='utf-8') as f:
f.write(self.log_text.get(1.0, tk.END))

self.log_event(f"[{datetime.now().strftime('%H:%M:%S')}] 日志已手动保存到: {manual_log_file}", "info")
except Exception as e:
self.log_event(f"保存日志失败: {str(e)}", "error")

def open_log_folder(self):
"""打开日志文件夹"""
try:
os.startfile(LOG_DIR)
self.log_event(f"[{datetime.now().strftime('%H:%M:%S')}] 已打开日志文件夹", "info")
except Exception as e:
self.log_event(f"打开日志文件夹失败: {str(e)}", "error")

def monitor_loop(self):
"""监控循环"""
check_interval = 1 # 检查间隔(秒)

while self.monitoring_active:
try:
connected, port, description = self.check_device()

# 在UI线程中更新状态
self.root.after(0, lambda c=connected, p=port, d=description: self.update_status(c, p, d))

# 等待下次检查
for _ in range(check_interval * 10):
if not self.monitoring_active:
break
time.sleep(0.1)

except Exception as e:
self.root.after(0, lambda: self.log_event(f"监控循环错误: {str(e)}", "error"))
time.sleep(check_interval)

def start_monitoring(self):
"""开始监控"""
if self.monitoring_active:
return

self.monitoring_active = True
self.session_start_time = time.time()
self.start_button.config(state='disabled')
self.stop_button.config(state='normal')
self.monitoring_status_label.config(text="运行中", foreground="green")

# 记录监控开始时间
self.current_session_start = datetime.now()
self.save_to_log_file(f"[{self.current_session_start.strftime('%Y-%m-%d %H:%M:%S')}] 监控开始", "INFO")

# 初始检查
connected, port, description = self.check_device()
self.update_status(connected, port, description)

# 启动监控线程
self.monitor_thread = threading.Thread(target=self.monitor_loop, daemon=True)
self.monitor_thread.start()

timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.log_event(f"[{timestamp}] ✅ 开始监控设备 VID:{TARGET_VID}, PID:{TARGET_PID}", "info")
self.log_event(f"[{timestamp}] 📊 统计从此刻开始: 连接次数={self.session_connection_count}, 断开次数={self.session_disconnection_count}, 运行时间=00:00:00", "info")

def stop_monitoring(self):
"""停止监控"""
if not self.monitoring_active:
return

self.monitoring_active = False
self.start_button.config(state='normal')
self.stop_button.config(state='disabled')
self.monitoring_status_label.config(text="已停止", foreground="red")

# 记录监控结束时间
if self.session_start_time:
duration = int(time.time() - self.session_start_time)
hours = duration // 3600
minutes = (duration % 3600) // 60
seconds = duration % 60
duration_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}"

self.save_to_log_file(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 监控结束 (本次运行时长: {duration_str}, 连接: {self.session_connection_count}, 断开: {self.session_disconnection_count})", "INFO")

if self.monitor_thread:
self.monitor_thread.join(timeout=2)

timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.log_event(f"[{timestamp}] ⏹️ 停止监控", "info")
self.log_event(f"[{timestamp}] 📊 本次监控统计: 连接次数={self.session_connection_count}, 断开次数={self.session_disconnection_count}, 运行时间={self.runtime_label.cget('text')}", "info")

# 保存统计数据
self.save_statistics()

def show_error(self, message):
"""显示错误信息(在日志区域)"""
error_msg = f"[{datetime.now().strftime('%H:%M:%S')}] ❌ 错误: {message}"
self.log_event(error_msg, "error")

def quit_app(self):
"""退出应用程序"""
# 记录程序关闭
try:
end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{end_time}] ========== 程序关闭 ==========\n"

with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write(f"{log_entry}\n")
except:
pass

# 保存统计数据
self.save_statistics()

# 停止监控
self.stop_monitoring()

# 关闭窗口
self.root.quit()
self.root.destroy()

def on_closing(self):
"""窗口关闭事件处理"""
self.quit_app()

def main():
if not SERIAL_AVAILABLE:
print("请先安装pyserial模块: pip install pyserial")
input("按Enter键退出...")
return

root = tk.Tk()

# 设置窗口样式
root.style = ttk.Style()
root.style.theme_use('clam')

app = USBMonitorApp(root)

# 设置窗口关闭事件
root.protocol("WM_DELETE_WINDOW", app.on_closing)

# 设置窗口图标(可选)
try:
root.iconbitmap(default='icon.ico')
except:
pass

root.mainloop()

if __name__ == "__main__":
main()

http://www.jsqmd.com/news/490829/

相关文章:

  • 计算机毕业设计之基于Spring Boot的易家宜超市云购物系统
  • PP-DocLayoutV3在学术场景中的应用:论文PDF截图自动提取标题/公式/图表
  • Z-Image-Turbo惊艳效果:眼镜反光、发丝透光、布料纹理三维立体感呈现
  • 【鸿蒙PC命令行适配】移植bzip2命令集,新增.bz2格式解压缩能力
  • cv_unet_image-colorization惊艳效果展示:AI自动上色真实作品集(含修复前后对比)
  • 解锁 C 语言 “积木术”:大一函数总结
  • 安装openclaw时出现node.exe : npm error code EIDLETIMEOUT的解决方案
  • 丹青幻境在文旅宣传中的应用:景区古建AI写生与四季意境图生成实践
  • Qwen3-0.6B-FP8基础教程:FP8权重文件结构解析与自定义层替换调试技巧
  • 动作识别13——实时动作识别之yolo26s-pose+PoseC3D
  • C++记一次文件输入字符串解析成数字不正常的情况
  • Stable Yogi Leather-Dress-Collection实战入门:动漫风格皮衣生成全流程演示
  • 记录C++学习细节
  • 【学习记录】1.PS.2.如何给图片打马赛克?
  • Gemma-3-270m轻量部署:在2核4GB云服务器上稳定支撑20QPS并发
  • DeEAR语音情感识别效果验证:人工标注专家与DeEAR三维度评分相关性达0.83
  • OpenClaw 接入阿里云 Coding Plan 完整教程:支持 Qwen3.5/GLM-5/Kimi 多模型
  • c语言指针篇
  • 第八届信息科学、电气与自动化工程国际学术会议(ISEAE 2026)
  • FLUX.小红书极致真实V2惊艳效果:小红书风‘高级感’配色与留白美学呈现
  • Java签名防篡改:我用HMAC干翻“配置被改”资损事故!附保姆级避坑指南
  • YOLOv11改进策略【卷积层】| arXiv 2025 加权卷积Weighted Conv 密度函数提表征 + 零参扩展降负担,提升目标检测精度
  • C程序中隐藏的数据溢出陷阱
  • SmallThinker-3B-Preview效果惊艳:支持多跳推理的复杂因果关系分析实例
  • Gemini 3深度量化分析:Google的万亿参数巨兽到底有多强?
  • Tabularis:一款面向开发者的轻量级数据库管理工具
  • File的用法
  • LLM大规模数据的组织检索方法
  • 30款IDEA插件宝贝,开发效率yyds!
  • 基于博途V16的程序:传送带机械手工件搬运监控系统