如何为SpeedTest项目替换ProgressBar为实时Speedometer优化界面
替换ProgressBar为转速表样式的实现方案
方案一:Tkinter Canvas自定义转速表(无额外依赖)
直接用Tkinter自带的Canvas组件绘制转速表,完全自定义样式,适配你的SpeedTest需求。以下是修改后的完整代码,将原来的ProgressBar替换为三个自定义转速表(下载、上传、Ping):
import tkinter as tk from tkinter import ttk import threading from datetime import datetime import speedtest import math class Speedometer(tk.Canvas): def __init__(self, parent, min_val, max_val, unit, size=150): super().__init__(parent, width=size, height=size, bg='white', highlightthickness=0) self.min_val = min_val self.max_val = max_val self.unit = unit self.size = size self.current_val = min_val self.center_x = size // 2 self.center_y = size // 2 self.radius = size // 2 - 10 self.draw_speedometer() def draw_speedometer(self): # 绘制刻度盘背景区间 # 绿色区间(0-30%) self.create_arc( self.center_x - self.radius, self.center_y - self.radius, self.center_x + self.radius, self.center_y + self.radius, start=135, extent=54, fill='#2ecc71', outline='' ) # 黄色区间(30%-70%) self.create_arc( self.center_x - self.radius, self.center_y - self.radius, self.center_x + self.radius, self.center_y + self.radius, start=189, extent=72, fill='#f1c40f', outline='' ) # 红色区间(70%-100%) self.create_arc( self.center_x - self.radius, self.center_y - self.radius, self.center_x + self.radius, self.center_y + self.radius, start=261, extent=54, fill='#e74c3c', outline='' ) # 绘制刻度线与数值 for i in range(0, 11): angle = 135 + (i * 18) # 总角度范围180度(135到315) radians = math.radians(angle) # 刻度线 x1 = self.center_x + (self.radius - 10) * math.cos(radians) y1 = self.center_y + (self.radius - 10) * math.sin(radians) x2 = self.center_x + self.radius * math.cos(radians) y2 = self.center_y + self.radius * math.sin(radians) self.create_line(x1, y1, x2, y2, fill='black', width=2) # 刻度数值 text_radius = self.radius - 25 tx = self.center_x + text_radius * math.cos(radians) ty = self.center_y + text_radius * math.sin(radians) val = self.min_val + (self.max_val - self.min_val) * (i / 10) self.create_text(tx, ty, text=f'{int(val)}', font=('Helvetica', 10)) # 中心数值显示 self.value_text = self.create_text( self.center_x, self.center_y + 20, text=f'{self.current_val:.1f} {self.unit}', font=('Helvetica', 12, 'bold') ) # 指针初始化 self.pointer = self.create_line( self.center_x, self.center_y, self.center_x + (self.radius - 20) * math.cos(math.radians(135)), self.center_y + (self.radius - 20) * math.sin(math.radians(135)), fill='black', width=3, arrow=tk.LAST ) def update_value(self, value): # 限制数值在有效范围内 self.current_val = max(self.min_val, min(value, self.max_val)) # 计算指针角度:135度对应最小值,315度对应最大值 angle = 135 + (self.current_val - self.min_val) / (self.max_val - self.min_val) * 180 radians = math.radians(angle) # 更新指针位置 x_end = self.center_x + (self.radius - 20) * math.cos(radians) y_end = self.center_y + (self.radius - 20) * math.sin(radians) self.coords(self.pointer, self.center_x, self.center_y, x_end, y_end) # 更新中心数值 self.itemconfig(self.value_text, text=f'{self.current_val:.2f} {self.unit}') class ProgressWindow: def __init__(self): self.root = tk.Toplevel() self.root.title("进度") self.root.geometry("300x100") self.status_label = ttk.Label(self.root, text="请等待,正在计算...", font=("Helvetica", 12)) self.status_label.pack(pady=20) def update_status(self, status): self.status_label.config(text=status) self.root.update() def close(self): self.root.destroy() class SpeedTestApp: def __init__(self, root): self.root = root self.root.title("SpeedTest 测速工具") self.root.geometry("600x500") # 结果变量 self.ping_var = tk.DoubleVar(value=0) self.download_var = tk.DoubleVar(value=0) self.upload_var = tk.DoubleVar(value=0) self.status_var = tk.StringVar() # 创建界面组件 self.create_widgets() def create_widgets(self): # 标题 title_label = ttk.Label(self.root, text="SpeedTest 测速", font=("Helvetica", 16)) title_label.pack(pady=10) # 开始按钮 self.start_button = ttk.Button(self.root, text="开始测速", command=self.start_speedtest) self.start_button.pack(pady=10) # 下载速度转速表 download_frame = ttk.Frame(self.root) download_frame.pack(pady=10) download_label = ttk.Label(download_frame, text="下载速度 (Mbps)") download_label.pack() self.download_speedometer = Speedometer(download_frame, 0, 1000, "Mbps") self.download_speedometer.pack() # 上传速度转速表 upload_frame = ttk.Frame(self.root) upload_frame.pack(pady=10) upload_label = ttk.Label(upload_frame, text="上传速度 (Mbps)") upload_label.pack() self.upload_speedometer = Speedometer(upload_frame, 0, 500, "Mbps") self.upload_speedometer.pack() # Ping转速表 ping_frame = ttk.Frame(self.root) ping_frame.pack(pady=10) ping_label = ttk.Label(ping_frame, text="Ping 值 (ms)") ping_label.pack() self.ping_speedometer = Speedometer(ping_frame, 0, 200, "ms") self.ping_speedometer.pack() # 状态信息 self.status_label = ttk.Label(self.root, textvariable=self.status_var, font=("Helvetica", 12)) self.status_label.pack(pady=10) def start_speedtest(self): self.start_button.config(state='disabled') self.progress_window = ProgressWindow() threading.Thread(target=self.run_speedtest).start() def run_speedtest(self): self.progress_window.update_status("正在选择最优服务器...") st = speedtest.Speedtest() st.get_best_server() self.progress_window.update_status("正在测试下载速度...") download_speed = st.download() / 1_000_000 self.download_speedometer.update_value(download_speed) self.progress_window.update_status("正在测试上传速度...") upload_speed = st.upload() / 1_000_000 self.upload_speedometer.update_value(upload_speed) ping = st.results.ping self.ping_speedometer.update_value(ping) # 保存测试结果 resultados = { 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'ping': ping, 'download_speed': download_speed, 'upload_speed': upload_speed } guardar_resultados(resultados) self.progress_window.update_status("测试完成") self.progress_window.close() self.status_var.set("测试完成") self.start_button.config(state='normal') def guardar_resultados(resultados, filename='测速记录.txt'): with open(filename, 'a', encoding='utf-8') as file: file.write(f"{resultados['timestamp']}, Ping: {resultados['ping']:.2f}ms, 下载: {resultados['download_speed']:.2f}Mbps, 上传: {resultados['upload_speed']:.2f}Mbps\n") print(f"结果已保存至 {filename}") if __name__ == "__main__": root = tk.Tk() app = SpeedTestApp(root) root.mainloop()
方案说明
- 自定义
Speedometer类继承自tk.Canvas,可直接嵌入Tkinter界面,无需额外依赖 - 刻度盘分绿、黄、红三个区间,对应不同速度等级
- 指针角度根据当前数值自动计算,实时更新显示
- 可通过调整
size参数改变转速表大小,修改min_val和max_val适配不同指标范围(比如Ping的范围设为0-200ms)
方案二:结合Matplotlib绘制仪表盘(需安装依赖)
如果需要更丰富的视觉样式,可以用Matplotlib创建仪表盘并嵌入Tkinter窗口。首先安装Matplotlib:
pip install matplotlib
以下是实现代码:
import tkinter as tk from tkinter import ttk import threading from datetime import datetime import speedtest import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import numpy as np class MatplotlibSpeedometer: def __init__(self, parent, min_val, max_val, unit): self.min_val = min_val self.max_val = max_val self.unit = unit self.frame = ttk.Frame(parent) self.frame.pack() # 创建仪表盘图表 self.fig, self.ax = plt.subplots(figsize=(3, 3), dpi=100, subplot_kw={'projection': 'polar'}) self.ax.set_facecolor('white') # 隐藏坐标轴 self.ax.set_xticks([]) self.ax.set_yticks([]) self.ax.set_ylim(0, 1.2) # 绘制背景刻度盘 angles = np.linspace(np.pi*1.25, np.pi*2.75, 100) radii = np.full_like(angles, 1) # 颜色区间划分 colors = np.where(angles < np.pi*1.75, '#2ecc71', np.where(angles < np.pi*2.25, '#f1c40f', '#e74c3c')) self.ax.fill_between(angles, 0, radii, color=colors, alpha=0.7) # 绘制刻度与数值 tick_angles = np.linspace(np.pi*1.25, np.pi*2.75, 11) tick_values = np.linspace(min_val, max_val, 11) for angle, val in zip(tick_angles, tick_values): self.ax.text(angle, 1.1, f'{int(val)}', ha='center', va='center', fontsize=10) # 指针与中心数值显示 self.pointer, = self.ax.plot([np.pi*1.25, np.pi*1.25], [0, 0.9], color='black', linewidth=3) self.value_text = self.ax.text(np.pi*2, 0, f'0 {unit}', ha='center', va='center', fontsize=12, fontweight='bold') # 嵌入Tkinter窗口 self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame) self.canvas.draw() self.canvas.get_tk_widget().pack() def update_value(self, value): val = max(self.min_val, min(value, self.max_val)) # 计算指针角度 angle = np.pi*1.25 + (val - self.min_val)/(self.max_val - self.min_val) * np.pi*1.5 # 更新指针位置 self.pointer.set_data([np.pi*2, angle], [0, 0.9]) # 更新中心数值 self.value_text.set_text(f'{val:.2f} {self.unit}') self.canvas.draw() class ProgressWindow: def __init__(self): self.root = tk.Toplevel() self.root.title("进度") self.root.geometry("300x100") self.status_label = ttk.Label(self.root, text="请等待,正在计算...", font=("Helvetica", 12)) self.status_label.pack(pady=20) def update_status(self, status): self.status_label.config(text=status) self.root.update() def close(self): self.root.destroy() class SpeedTestApp: def __init__(self, root): self.root = root self.root.title("SpeedTest 测速工具") self.root.geometry("700x600") self.status_var = tk.StringVar() self.create_widgets() def create_widgets(self): title_label = ttk.Label(self.root, text="SpeedTest 测速", font=("Helvetica", 16)) title_label.pack(pady=10) self.start_button = ttk.Button(self.root, text="开始测速", command=self.start_speedtest) self.start_button.pack(pady=10) # 下载速度仪表盘 download_frame = ttk.Frame(self.root) download_frame.pack(side=tk.LEFT, padx=20, pady=10) download_label = ttk.Label(download_frame, text="下载速度 (Mbps)") download_label.pack() self.download_meter = MatplotlibSpeedometer(download_frame, 0, 1000, "Mbps") # 上传速度仪表盘 upload_frame = ttk.Frame(self.root) upload_frame.pack(side=tk.LEFT, padx=20, pady=10) upload_label = ttk.Label(upload_frame, text="上传速度 (Mbps)") upload_label.pack() self.upload_meter = MatplotlibSpeedometer(upload_frame, 0, 500, "Mbps") # Ping仪表盘 ping_frame = ttk.Frame(self.root) ping_frame.pack(side=tk.LEFT, padx=20, pady=10) ping_label = ttk.Label(ping_frame, text="Ping 值 (ms)") ping_label.pack() self.ping_meter = MatplotlibSpeedometer(ping_frame, 0, 200, "ms") self.status_label = ttk.Label(self.root, textvariable=self.status_var, font=("Helvetica", 12)) self.status_label.pack(pady=10, side=tk.BOTTOM) def start_speedtest(self): self.start_button.config(state='disabled') self.progress_window = ProgressWindow() threading.Thread(target=self.run_speedtest).start() def run_speedtest(self): self.progress_window.update_status("正在选择最优服务器...") st = speedtest.Speedtest() st.get_best_server() self.progress_window.update_status("正在测试下载速度...") download_speed = st.download() / 1_000_000 self.download_meter.update_value(download_speed) self.progress_window.update_status("正在测试上传速度...") upload_speed = st.upload() / 1_000_000 self.upload_meter.update_value(upload_speed) ping = st.results.ping self.ping_meter.update_value(ping) resultados = { 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'ping': ping, 'download_speed': download_speed, 'upload_speed': upload_speed } guardar_resultados(resultados) self.progress_window.update_status("测试完成") self.progress_window.close() self.status_var.set("测试完成") self.start_button.config
相关产品推荐
相关产品推荐

