You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:33:18