import tkinter as tk
from tkinter import ttk
import socket
import threading
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.font_manager import FontProperties
import subprocess
# 指定黑体字体文件路径,需要根据实际情况修改
font_path = '/home/nvidia/test/simhei.ttf'
font_prop = FontProperties(fname=font_path)
plt.rcParams['font.family'] = font_prop.get_name()
# 解决负号显示问题
plt.rcParams['axes.unicode_minus'] = False
class DeviceManager(tk.Tk):
def __init__(self):
super().__init__()
self.title("设备管理器")
self.geometry("1100x900")
self.resizable(False, False)
self.configure(bg="#f9f9f9")
self.devices = {
"设备1": {
"ip": "192.168.31.50",
"status": "离线",
"temperature": 0,
"power": 0,
"memory": 0,
"bandwidth": 0,
"send_count": 0,
"mlu_avg_usage": 0,
"cpu_avg_usage": 0,
"mlu_core_usage": [1, 1, 1, 1],
"cpu_core_usage": [1, 1, 1, 1]
}
}
self.current_device = "设备1"
self.current_page = 1
self.create_sidebar()
self.create_pages()
self.start_udp_receive()
self.has_received_data = False
self.frame_count = 0
# 用于标记页面2、页面3和页面4的折线图是否已初始化
self.page2_plots_initialized = False
self.page3_plots_initialized = False
self.page4_plots_initialized = False
def create_sidebar(self):
# 增大侧边栏宽度
self.sidebar = tk.Frame(self, bg="#e0e0e0", width=300)
self.sidebar.pack(side="left", fill="y")
# 增大按钮尺寸并增加与左侧边界的间距
button_style = ttk.Style()
button_style.configure('TButton', font=('Arial', 16))
self.page1_button = ttk.Button(self.sidebar, text="主页面", command=lambda: self.show_page(1), style='TButton')
self.page1_button.pack(pady=20, padx=30, fill=tk.X)
self.page2_button = ttk.Button(self.sidebar, text="MLU", command=lambda: self.show_page(2),
style='TButton')
self.page2_button.pack(pady=20, padx=30, fill=tk.X)
self.page3_button = ttk.Button(self.sidebar, text="CPU", command=lambda: self.show_page(3),
style='TButton')
self.page3_button.pack(pady=20, padx=30, fill=tk.X)
self.page4_button = ttk.Button(self.sidebar, text="内存", command=lambda: self.show_page(4),
style='TButton')
self.page4_button.pack(pady=20, padx=30, fill=tk.X)
def create_pages(self):
self.pages = []
for i in range(4):
page = tk.Frame(self, bg="#f9f9f9")
page.pack(side="right", fill="both", expand=True, padx=20, pady=20)
self.pages.append(page)
self.create_page1()
self.create_page2()
self.create_page3()
self.create_page4()
self.show_page(1)
def create_page1(self):
self.info_frame = self.pages[0]
self.ip_frame = tk.Frame(self.info_frame, bg="#f9f9f9")
self.ip_frame.pack(pady=20, anchor="w")
self.create_info_widget("IP 地址:", self.ip_frame, "ip")
self.status_frame = tk.Frame(self.info_frame, bg="#f9f9f9")
self.status_frame.pack(pady=20, anchor="w")
self.create_info_widget("状态:", self.status_frame, "status")
self.temp_frame = tk.Frame(self.info_frame, bg="#f9f9f9")
self.temp_frame.pack(pady=20, anchor="w")
self.create_info_widget("温度:", self.temp_frame, "temperature", "°C")
self.power_frame = tk.Frame(self.info_frame, bg="#f9f9f9")
self.power_frame.pack(pady=20, anchor="w")
self.create_info_widget("功率:", self.power_frame, "power", "W")
self.bandwidth_frame = tk.Frame(self.info_frame, bg="#f9f9f9")
self.bandwidth_frame.pack(pady=20, anchor="w")
self.create_info_widget("带宽:", self.bandwidth_frame, "bandwidth", "GB/s")
# self.send_count_frame = tk.Frame(self.info_frame, bg="#f9f9f9")
# self.send_count_frame.pack(pady=20)
# self.create_info_widget("发送次数:", self.send_count_frame, "send_count")
self.mlu_avg_frame = tk.Frame(self.info_frame, bg="#f9f9f9")
self.mlu_avg_frame.pack(pady=20, anchor="w")
self.create_info_widget("MLU 平均占用率:", self.mlu_avg_frame, "mlu_avg_usage", "%")
self.cpu_avg_frame = tk.Frame(self.info_frame, bg="#f9f9f9")
self.cpu_avg_frame.pack(pady=20, anchor="w")
self.create_info_widget("CPU 平均占用率:", self.cpu_avg_frame, "cpu_avg_usage", "%")
def create_info_widget(self, label_text, parent_frame, data_key, unit=""):
label = ttk.Label(parent_frame, text=f"{label_text}",
font=("Arial", 14, "bold"),
background="#f9f9f9")
label.pack(side="left", padx=5)
value = ttk.Label(parent_frame, text="",
font=("Arial", 14),
background="#f9f9f9")
value.pack(side="left", padx=5)
setattr(self, f"{data_key}_value", value)
if unit:
setattr(self, f"{data_key}_unit", unit)
def create_page2(self):
self.mlu_figs = []
self.mlu_axs = []
self.mlu_xs = [[] for _ in range(4)]
self.mlu_ys = [[] for _ in range(4)]
self.mlu_lines = []
for i in range(4):
fig = plt.Figure(figsize=(5, 5), dpi=80)
ax = fig.add_subplot(111)
line, = ax.plot([], [], lw=2)
ax.set_xlabel('')
ax.set_ylabel('占用率 (%)')
ax.set_title(f'MLU 核 {i + 1} 占用率')
ax.set_xlim(0, 20)
ax.set_ylim(0, 100)
# 手动设置横轴刻度
ax.set_xticks(range(0, 21, 5))
canvas = FigureCanvasTkAgg(fig, master=self.pages[1])
if i < 2:
canvas.get_tk_widget().grid(row=0, column=i, padx=10, pady=10)
else:
canvas.get_tk_widget().grid(row=1, column=i - 2, padx=10, pady=10)
self.mlu_figs.append(fig)
self.mlu_axs.append(ax)
self.mlu_lines.append(line)
def create_page3(self):
self.cpu_figs = []
self.cpu_axs = []
self.cpu_xs = [[] for _ in range(4)]
self.cpu_ys = [[] for _ in range(4)]
self.cpu_lines = []
for i in range(4):
fig = plt.Figure(figsize=(5, 5), dpi=80)
ax = fig.add_subplot(111)
line, = ax.plot([], [], lw=2)
ax.set_xlabel('')
ax.set_ylabel('占用率 (%)')
ax.set_title(f'CPU 核 {i + 1} 占用率')
ax.set_xlim(0, 20)
ax.set_ylim(0, 100)
# 手动设置横轴刻度
ax.set_xticks(range(0, 21, 5))
canvas = FigureCanvasTkAgg(fig, master=self.pages[2])
if i < 2:
canvas.get_tk_widget().grid(row=0, column=i, padx=10, pady=10)
else:
canvas.get_tk_widget().grid(row=1, column=i - 2, padx=10, pady=10)
self.cpu_figs.append(fig)
self.cpu_axs.append(ax)
self.cpu_lines.append(line)
def create_page4(self):
self.memory_fig = plt.Figure(figsize=(10, 8), dpi=80)
self.memory_ax = self.memory_fig.add_subplot(111)
self.memory_line, = self.memory_ax.plot([], [], lw=2)
self.memory_ax.set_xlabel('')
self.memory_ax.set_ylabel('占用率 (%)')
self.memory_ax.set_title('内存占用率')
self.memory_ax.set_xlim(0, 20)
self.memory_ax.set_ylim(0, 100)
# 手动设置横轴刻度
self.memory_ax.set_xticks(range(0, 21, 5))
canvas = FigureCanvasTkAgg(self.memory_fig, master=self.pages[3])
canvas.get_tk_widget().pack(padx=10, pady=10)
self.memory_xs = []
self.memory_ys = []
def show_page(self, page_num):
for i, page in enumerate(self.pages):
if i == page_num - 1:
page.pack(side="right", fill="both", expand=True, padx=20, pady=20)
self.current_page = page_num
else:
page.pack_forget()
def update_device_info(self, info_str):
info = self.parse_info(info_str)
device = self.devices[self.current_device]
device.update(info)
self.update_display()
if not self.has_received_data:
self.has_received_data = True
self.initialize_plots()
def parse_info(self, info_str):
info = {}
sections = info_str.split("---------------END---------------")[0].split("\n\n")
for section in sections:
lines = [line.strip() for line in section.splitlines() if line.strip()]
if not lines:
continue
key = lines[0].split(":")[0].strip()
if key == "温度信息":
info.update(self.parse_temperature(lines[1:]))
elif key == "功率信息":
info.update(self.parse_power(lines[1:]))
elif key == "内存信息":
info.update(self.parse_memory(lines))
elif key == "带宽信息":
info.update(self.parse_bandwidth(lines[1:]))
elif key == "发送次数":
info.update(self.parse_send_count(lines))
elif key == "MLU信息":
info.update(self.parse_mlu_avg_usage(lines))
info.update(self.parse_mlu_core_usage(lines))
elif key == "CPU信息":
info.update(self.parse_cpu_avg_usage(lines))
info.update(self.parse_cpu_core_usage(lines))
return info
def parse_temperature(self, lines):
temp = 0
for line in lines:
if "C" in line:
try:
temp = max(temp, float(line.split(":")[1].strip().replace(" C", "")))
except (IndexError, ValueError):
continue
return {"temperature": temp}
def parse_power(self, lines):
power = 0
for line in lines:
if "W" in line:
try:
power = float(line.split(":")[1].strip().replace(" W", ""))
except (IndexError, ValueError):
continue
return {"power": power}
def parse_memory(self, lines):
total = 0
used = 0
physical_memory_section = False
for line in lines:
if "Physical Memory Usage" in line:
physical_memory_section = True
elif physical_memory_section:
if "Total" in line:
total = self.parse_memory_value(line)
elif "Used" in line:
used = self.parse_memory_value(line)
return {"memory": round(used / total * 100, 1) if total != 0 else 0}
def parse_memory_value(self, line):
try:
value = line.split(":")[1].strip()
num = float(value.split()[0])
unit = value.split()[1]
if unit == "MiB":
return num * 1024 * 1024
return num
except (IndexError, ValueError):
return 0
def parse_bandwidth(self, lines):
bandwidth = 0
for line in lines:
if "GB/s" in line:
try:
bandwidth = float(line.split(":")[1].strip().replace(" GB/s", ""))
except (IndexError, ValueError):
continue
return {"bandwidth": bandwidth}
def parse_send_count(self, lines):
send_count = 0
for line in lines:
if "次" in line:
try:
value_part = line.split(":")[-1].strip()
send_count = float(value_part.replace("次", ""))
except (IndexError, ValueError):
continue
return {"send_count": int(send_count)}
def parse_mlu_avg_usage(self, lines):
for line in lines:
if "MLU Average" in line:
try:
parts = line.split(':')
if len(parts) > 1:
mlu_avg_usage = float(parts[1].strip().replace("%", ""))
return {"mlu_avg_usage": mlu_avg_usage}
except (IndexError, ValueError):
continue
return {"mlu_avg_usage": 0}
def parse_cpu_avg_usage(self, lines):
for line in lines:
if "Device CPU Chip" in line:
try:
parts = line.split(':')
if len(parts) > 1:
cpu_avg_usage = float(parts[1].strip().replace("%", ""))
return {"cpu_avg_usage": cpu_avg_usage}
except (IndexError, ValueError):
continue
return {"cpu_avg_usage": 0}
def parse_mlu_core_usage(self, lines):
mlu_core_usage = [1, 1, 1, 1]
for line in lines:
if "MLU " in line and "%" in line:
try:
parts = line.split(':')
core_num = int(parts[0].split('MLU ')[1])
usage = float(parts[1].strip().replace("%", ""))
mlu_core_usage[core_num] = usage
except (IndexError, ValueError) as e:
pass
return {"mlu_core_usage": mlu_core_usage}
def parse_cpu_core_usage(self, lines):
cpu_core_usage = [1, 1, 1, 1]
for line in lines:
if "Device CPU Core" in line and "%" in line:
try:
parts = line.split(':')
core_num = int(parts[0].split('Device CPU Core ')[1])
usage = float(parts[1].strip().replace("%", ""))
cpu_core_usage[core_num] = usage
except (IndexError, ValueError) as e:
pass
return {"cpu_core_usage": cpu_core_usage}
def update_display(self):
device = self.devices[self.current_device]
# 新增ping检测逻辑
ip = device["ip"]
try:
result = subprocess.run(['ping', '-c', '1', '-W', '1', ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode == 0:
device["status"] = "在线"
else:
device["status"] = "离线"
except Exception as e:
print(f"Ping 检测出错: {e}")
device["status"] = "离线"
if self.current_page == 1:
self.ip_value.config(text=device["ip"])
self.status_value.config(text=device["status"],
foreground="green" if device["status"] == "在线" else "red")
self.temperature_value.config(text=f"{device['temperature']:.1f}°C")
self.power_value.config(text=f"{device['power']:.2f} W")
self.bandwidth_value.config(text=f"{device['bandwidth']:.1f} GB/s")
# self.send_count_value.config(text=device["send_count"])
self.mlu_avg_usage_value.config(text=f"{device['mlu_avg_usage']:.1f}%")
self.cpu_avg_usage_value.config(text=f"{device['cpu_avg_usage']:.1f}%")
self.update_page2_plots()
self.update_page3_plots()
self.update_page4_plots()
self.frame_count += 1
def start_udp_receive(self):
def read_config():
config = {}
try:
with open('config.txt', 'r') as config_file:
for line in config_file:
if '=' in line:
key, value = line.strip().split('=', 1)
config[key] = value
print("Read config success.")
except FileNotFoundError:
print("Unable to open config file!")
return None
return config
config = read_config()
if config is None:
return
server_ip = config.get('SERVER_IP')
server_port = int(config.get('SERVER_PORT'))
client_ip = config.get('CLIENT_IP')
client_port = int(config.get('CLIENT_PORT'))
print("ServerIP: {}".format(server_ip))
print("ServerPort: {}".format(server_port))
print("ClientIP: {}".format(client_ip))
print("ClientPort: {}".format(client_port))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("Set socket ok.")
try:
sock.bind((client_ip, client_port))
print("Bind success.")
except OSError:
print("Bind error!")
return
def receive_data():
print("Start receive data.")
try:
while True:
data, addr = sock.recvfrom(4096)
try:
info_str = data.decode('utf-8')
self.update_device_info(info_str)
except UnicodeDecodeError:
print("解码数据时出错,请检查数据编码。")
except OSError:
print("Receive data error!")
except KeyboardInterrupt:
print("Program interrupted by user.")
finally:
sock.close()
receive_thread = threading.Thread(target=receive_data)
receive_thread.daemon = True
receive_thread.start()
def initialize_plots(self):
if not self.page2_plots_initialized:
self.page2_plots_initialized = True
if not self.page3_plots_initialized:
self.page3_plots_initialized = True
if not self.page4_plots_initialized:
self.page4_plots_initialized = True
def update_page2_plots(self):
device = self.devices[self.current_device]
for i in range(4):
try:
usage = device["mlu_core_usage"][i]
if not isinstance(usage, (int, float)):
usage = 1
except (IndexError, KeyError):
usage = 1
# 确保只添加新的时间点
if not self.mlu_xs[i] or self.mlu_xs[i][-1] < self.frame_count:
self.mlu_xs[i].append(self.frame_count)
self.mlu_ys[i].append(usage)
else:
# 如果当前时间点已存在,更新该时间点的值
self.mlu_ys[i][-1] = usage
# 保证数据列表长度不超过20
if len(self.mlu_xs[i]) > 20:
self.mlu_xs[i] = self.mlu_xs[i][-20:]
self.mlu_ys[i] = self.mlu_ys[i][-20:]
# 调整横轴范围
start = max(self.mlu_xs[i][0], self.frame_count - 19)
end = start + 19
self.mlu_axs[i].set_xlim(start, end)
# 确保刻度是 5 的倍数
tick_start = (start // 5) * 5
tick_end = (end // 5) * 5
ticks = [tick for tick in range(tick_start, tick_end + 1, 5) if start <= tick <= end]
self.mlu_axs[i].set_xticks(ticks)
self.mlu_lines[i].set_data(self.mlu_xs[i], self.mlu_ys[i])
self.mlu_figs[i].canvas.draw()
def update_page3_plots(self):
device = self.devices[self.current_device]
for i in range(4):
try:
usage = device["cpu_core_usage"][i]
if not isinstance(usage, (int, float)):
usage = 1
except (IndexError, KeyError):
usage = 1
# 确保只添加新的时间点
if not self.cpu_xs[i] or self.cpu_xs[i][-1] < self.frame_count:
self.cpu_xs[i].append(self.frame_count)
self.cpu_ys[i].append(usage)
else:
# 如果当前时间点已存在,更新该时间点的值
self.cpu_ys[i][-1] = usage
# 保证数据列表长度不超过20
if len(self.cpu_xs[i]) > 20:
self.cpu_xs[i] = self.cpu_xs[i][-20:]
self.cpu_ys[i] = self.cpu_ys[i][-20:]
# 调整横轴范围
start = max(self.cpu_xs[i][0], self.frame_count - 19)
end = start + 19
self.cpu_axs[i].set_xlim(start, end)
# 确保刻度是 5 的倍数
tick_start = (start // 5) * 5
tick_end = (end // 5) * 5
ticks = [tick for tick in range(tick_start, tick_end + 1, 5) if start <= tick <= end]
self.cpu_axs[i].set_xticks(ticks)
self.cpu_lines[i].set_data(self.cpu_xs[i], self.cpu_ys[i])
self.cpu_figs[i].canvas.draw()
def update_page4_plots(self):
device = self.devices[self.current_device]
memory_usage = device["memory"]
# 确保只添加新的时间点
if not self.memory_xs or self.memory_xs[-1] < self.frame_count:
self.memory_xs.append(self.frame_count)
self.memory_ys.append(memory_usage)
else:
# 如果当前时间点已存在,更新该时间点的值
self.memory_ys[-1] = memory_usage
# 保证数据列表长度不超过20
if len(self.memory_xs) > 20:
self.memory_xs = self.memory_xs[-20:]
self.memory_ys = self.memory_ys[-20:]
# 调整横轴范围
start = max(self.memory_xs[0], self.frame_count - 19)
end = start + 19
self.memory_ax.set_xlim(start, end)
# 确保刻度是 5 的倍数
tick_start = (start // 5) * 5
tick_end = (end // 5) * 5
ticks = [tick for tick in range(tick_start, tick_end + 1, 5) if start <= tick <= end]
self.memory_ax.set_xticks(ticks)
self.memory_line.set_data(self.memory_xs, self.memory_ys)
self.memory_fig.canvas.draw()
if __name__ == "__main__":
app = DeviceManager()
app.mainloop()
这是一段基于Socket通讯用于接收服务端MLU220文本数据的客户端程序,用这段程序提取文本数据的方法写入到下面这段程序中以便用相同的方法提取文本数据import tkinter as tk
import ttkbootstrap as tb
import psutil
import random
import threading
import time
from ttkbootstrap.constants import *
from PIL import Image, ImageTk, ImageDraw
class EnhancedCircularProgressBar:
def __init__(self, parent, size=200, thickness=20,
bg_color="#1a1a1a", text_color="#ffffff",
font_size=16, title="", unit="", max_value=100,
glow_effect=True):
self.parent = parent
self.size = size
self.thickness = thickness
self.bg_color = bg_color
self.text_color = text_color
self.font_size = font_size
self.title = title
self.unit = unit
self.max_value = max_value
self.glow_effect = glow_effect
self.current_value = 0
self.target_value = 0
self.animation_speed = 1.0 # 动画速度因子
# 创建画布
self.canvas = tk.Canvas(
parent,
width=size,
height=size,
bg="black",
highlightthickness=0,
bd=0
)
# 计算圆心和半径
self.center_x = size / 2
self.center_y = size / 2
self.radius = (size - thickness) / 2 - 5
# 绘制背景圆环
self.draw_background()
# 绘制文本
self.text_id = self.canvas.create_text(
self.center_x,
self.center_y,
text="0%",
fill=text_color,
font=("Arial", font_size, "bold")
)
# 绘制标题
self.title_id = self.canvas.create_text(
self.center_x,
self.center_y + 40,
text=title,
fill=text_color,
font=("Arial", int(font_size*0.8))
)
# 初始绘制
self.set_value(0)
def draw_background(self):
"""绘制背景圆环"""
# 计算圆环的外接矩形坐标
x0 = self.center_x - self.radius
y0 = self.center_y - self.radius
x1 = self.center_x + self.radius
y1 = self.center_y + self.radius
# 绘制背景圆环
self.bg_arc = self.canvas.create_arc(
x0, y0, x1, y1,
start=0, # 起始角度
extent=360, # 360度完整圆环
width=self.thickness,
outline=self.bg_color,
style=tk.ARC
)
def interpolate_color(self, progress):
"""根据进度值插值计算颜色(绿-黄-橙-红过渡)"""
# 定义颜色过渡点
color_points = [
(0.00, (0, 255, 0)), # 绿色
(0.25, (170, 255, 0)), # 黄绿色
(0.50, (255, 255, 0)), # 黄色
(0.75, (255, 170, 0)), # 橙色
(1.00, (255, 0, 0)) # 红色
]
# 找到当前进度所在的区间
for i in range(1, len(color_points)):
if progress <= color_points[i][0]:
# 计算区间内的比例
t = (progress - color_points[i-1][0]) / (color_points[i][0] - color_points[i-1][0])
# 线性插值计算RGB值(不使用numpy)
r1, g1, b1 = color_points[i-1][1]
r2, g2, b2 = color_points[i][1]
r = int(r1 + t * (r2 - r1))
g = int(g1 + t * (g2 - g1))
b = int(b1 + t * (b2 - b1))
return f"#{r:02x}{g:02x}{b:02x}"
# 默认返回红色
return "#ff0000"
def set_value(self, value, animate=True):
"""设置当前值并更新进度条"""
# 确保值在合理范围内
value = max(0, min(value, self.max_value))
if animate:
self.target_value = value
# 启动动画(如果尚未运行)
if abs(self.current_value - self.target_value) > 0.1:
self.animate_step()
else:
self.target_value = value
self.current_value = value
self.update_display()
def update_display(self):
"""更新进度条显示"""
# 计算进度比例
progress = self.current_value / self.max_value
# 计算角度(从-90度开始,即12点钟方向)
angle = progress * 360
# 清除旧的前景圆环
if hasattr(self, 'fg_arc'):
self.canvas.delete(self.fg_arc)
# 计算圆环的外接矩形坐标
x0 = self.center_x - self.radius
y0 = self.center_y - self.radius
x1 = self.center_x + self.radius
y1 = self.center_y + self.radius
# 获取插值颜色
color = self.interpolate_color(progress)
# 绘制前景圆环(进度指示)
self.fg_arc = self.canvas.create_arc(
x0, y0, x1, y1,
start=90, # 从12点钟方向开始
extent=angle, # 根据值计算角度
width=self.thickness,
outline=color,
style=tk.ARC
)
# 提升前景圆环的显示层级
self.canvas.tag_raise(self.fg_arc)
# 更新文本显示
display_text = f"{self.current_value:.1f}"
if self.unit == "%":
display_text = f"{progress*100:.1f}%"
self.canvas.itemconfig(self.text_id, text=display_text)
def animate_step(self):
"""执行一步动画(不使用numpy)"""
if abs(self.current_value - self.target_value) < 0.1:
self.current_value = self.target_value
else:
# 使用缓动函数实现平滑动画
diff = self.target_value - self.current_value
self.current_value += diff * 0.2 * self.animation_speed
self.update_display()
# 如果未达到目标值,继续动画
if abs(self.current_value - self.target_value) > 0.1:
self.parent.after(16, self.animate_step) # 约60fps
class SystemMonitorApp:
def __init__(self):
# 创建主窗口
self.root = tb.Window(themename="darkly", title="系统资源监控", size=(1300, 800))
self.root.minsize(1000, 700)
# 设置样式
style = tb.Style()
style.configure("TFrame", background="#121212")
style.configure("Title.TLabel", background="#121212", foreground="#e0e0e0",
font=("Arial", 16, "bold"))
style.configure("Section.TLabelframe", font=("Arial", 10, "bold"))
# 创建主框架 - 使用网格布局
main_frame = tb.Frame(self.root, padding=10)
main_frame.pack(fill=tk.BOTH, expand=True)
# ========== 顶部控制栏 ==========
control_bar = tb.Frame(main_frame, padding=(10, 5))
control_bar.grid(row=0, column=0, columnspan=3, sticky="ew", pady=(0, 10))
# 设备选择下拉框
tb.Label(control_bar, text="监控设备:", bootstyle=PRIMARY).pack(side=tk.LEFT, padx=(0, 5))
self.device_var = tk.StringVar()
device_options = ["设备A", "设备B", "设备C", "设备D", "设备E"]
device_combo = tb.Combobox(
control_bar,
textvariable=self.device_var,
values=device_options,
width=15,
state="readonly",
bootstyle=PRIMARY
)
device_combo.pack(side=tk.LEFT, padx=5)
device_combo.current(0) # 设置默认选项
# 采样频率滑块
tb.Label(control_bar, text="采样频率:", bootstyle=PRIMARY).pack(side=tk.LEFT, padx=(20, 5))
self.sampling_rate = tk.IntVar(value=1)
tb.Scale(
control_bar,
from_=0.5,
to=5,
length=120,
orient=tk.HORIZONTAL,
variable=self.sampling_rate,
bootstyle=PRIMARY
).pack(side=tk.LEFT, padx=5)
# 动画速度滑块 (修复resolution问题)
tb.Label(control_bar, text="动画速度:", bootstyle=PRIMARY).pack(side=tk.LEFT, padx=(20, 5))
self.animation_speed = tk.DoubleVar(value=1.0)
tb.Scale(
control_bar,
from_=0.5,
to=3.0,
length=120,
orient=tk.HORIZONTAL,
variable=self.animation_speed,
bootstyle=PRIMARY
).pack(side=tk.LEFT, padx=5)
# 控制按钮
tb.Button(
control_bar,
text="启动",
bootstyle=SUCCESS,
width=8,
command=self.start_monitoring
).pack(side=tk.LEFT, padx=5)
tb.Button(
control_bar,
text="暂停",
bootstyle=DANGER,
width=8,
command=self.stop_monitoring
).pack(side=tk.LEFT, padx=5)
tb.Button(
control_bar,
text="重置",
bootstyle=WARNING,
width=8,
command=self.reset_all
).pack(side=tk.LEFT, padx=5)
# 状态指示器
self.status_indicator = tb.Label(
control_bar,
text="● 已连接",
bootstyle=(SUCCESS, INVERSE),
font=("Arial", 10),
padding=(10, 0)
)
self.status_indicator.pack(side=tk.RIGHT, padx=(10, 0))
# ========== 左侧设置面板 ==========
settings_frame = tb.Labelframe(
main_frame,
text="监控设置",
bootstyle="info",
padding=10,
style="Section.TLabelframe"
)
settings_frame.grid(row=1, column=0, sticky="nsew", padx=(0, 10), pady=(0, 10))
# 温度报警设置
temp_frame = tb.Frame(settings_frame)
temp_frame.pack(fill=tk.X, pady=5)
tb.Label(temp_frame, text="温度报警阈值(°C):").pack(side=tk.LEFT)
self.temp_threshold = tk.IntVar(value=80)
tb.Entry(
temp_frame,
textvariable=self.temp_threshold,
width=8,
bootstyle=PRIMARY
).pack(side=tk.RIGHT, padx=(5, 0))
# 内存报警设置
mem_frame = tb.Frame(settings_frame)
mem_frame.pack(fill=tk.X, pady=5)
tb.Label(mem_frame, text="内存报警阈值(%):").pack(side=tk.LEFT)
self.mem_threshold = tk.IntVar(value=90)
tb.Entry(
mem_frame,
textvariable=self.mem_threshold,
width=8,
bootstyle=PRIMARY
).pack(side=tk.RIGHT, padx=(5, 0))
# 网络报警设置
net_frame = tb.Frame(settings_frame)
net_frame.pack(fill=tk.X, pady=5)
tb.Label(net_frame, text="网络报警阈值(Mbps):").pack(side=tk.LEFT)
self.net_threshold = tk.IntVar(value=800)
tb.Entry(
net_frame,
textvariable=self.net_threshold,
width=8,
bootstyle=PRIMARY
).pack(side=tk.RIGHT, padx=(5, 0))
# 分隔线
tb.Separator(settings_frame, bootstyle=SECONDARY).pack(fill=tk.X, pady=10)
# 主题选择
tb.Label(settings_frame, text="界面主题:", bootstyle=PRIMARY).pack(anchor=tk.W, pady=(0, 5))
self.theme_var = tk.StringVar(value="darkly")
themes = ["darkly", "solar", "superhero", "cyborg", "vapor"]
tb.Combobox(
settings_frame,
textvariable=self.theme_var,
values=themes,
state="readonly",
bootstyle=PRIMARY
).pack(fill=tk.X, pady=(0, 10))
# 历史数据按钮
tb.Button(
settings_frame,
text="查看历史数据",
bootstyle=(OUTLINE, INFO),
command=self.show_history
).pack(fill=tk.X, pady=5)
# ========== 中间监控面板 ==========
monitor_frame = tb.Frame(main_frame)
monitor_frame.grid(row=1, column=1, sticky="nsew", pady=(0, 10))
# 标题
tb.Label(
monitor_frame,
text="系统资源实时监控",
style="Title.TLabel"
).pack(pady=(0, 15))
# 创建进度条容器框架
progress_container = tb.Frame(monitor_frame)
progress_container.pack(fill=tk.BOTH, expand=True, pady=5)
# 创建监控指标配置
monitor_config = [
{"title": "CPU温度", "unit": "°C", "max_value": 100, "thickness": 18},
{"title": "功耗", "unit": "W", "max_value": 200, "thickness": 18},
{"title": "内存使用", "unit": "%", "max_value": 100, "thickness": 18},
{"title": "网络带宽", "unit": "Mbps", "max_value": 1000, "thickness": 18},
{"title": "MLU利用率", "unit": "%", "max_value": 100, "thickness": 18},
{"title": "CPU利用率", "unit": "%", "max_value": 100, "thickness": 18}
]
# 使用网格布局排列进度条
self.progress_bars = []
for i, config in enumerate(monitor_config):
frame = tb.Frame(progress_container)
frame.grid(row=i//3, column=i%3, padx=15, pady=15, sticky="nsew")
# 创建增强型进度条
progress_bar = EnhancedCircularProgressBar(
frame,
size=220,
thickness=config["thickness"],
title=config["title"],
unit=config["unit"],
max_value=config["max_value"],
glow_effect=True
)
self.progress_bars.append(progress_bar)
progress_bar.canvas.pack(fill=tk.BOTH, expand=True)
# 设置网格列权重
for i in range(3):
progress_container.columnconfigure(i, weight=1)
for i in range(2):
progress_container.rowconfigure(i, weight=1)
# ========== 右侧信息面板 ==========
info_frame = tb.Labelframe(
main_frame,
text="系统信息",
bootstyle="info",
padding=10,
style="Section.TLabelframe"
)
info_frame.grid(row=1, column=2, sticky="nsew", padx=(10, 0), pady=(0, 10))
# 系统信息标签
info_labels = [
("设备型号:", "MLU"),
("操作系统:", "Ubuntu 20.04 LTS"),
("处理器:", "16核 ARMv8"),
("内存总量:", "32 GB"),
("MLU数量:", "4"),
("网络接口:", "eth0 (10Gbps)")
]
for label, value in info_labels:
frame = tb.Frame(info_frame)
frame.pack(fill=tk.X, pady=3)
tb.Label(frame, text=label, width=10, anchor=tk.W).pack(side=tk.LEFT)
tb.Label(frame, text=value, bootstyle=INFO).pack(side=tk.LEFT)
# 分隔线
tb.Separator(info_frame, bootstyle=SECONDARY).pack(fill=tk.X, pady=10)
# 实时状态
tb.Label(info_frame, text="实时状态", bootstyle=PRIMARY).pack(anchor=tk.W, pady=(0, 5))
self.realtime_labels = {}
status_items = [
("CPU温度", "cpu_temp", "°C"),
("功耗", "power", "W"),
("内存使用", "memory", "%"),
("网络带宽", "bandwidth", "Mbps"),
("MLU利用率", "mlu_usage", "%"),
("CPU利用率", "cpu_usage", "%")
]
for name, key, unit in status_items:
frame = tb.Frame(info_frame)
frame.pack(fill=tk.X, pady=2)
tb.Label(frame, text=name, width=12, anchor=tk.W).pack(side=tk.LEFT)
value_label = tb.Label(frame, text="0.0", width=8, anchor=tk.W)
value_label.pack(side=tk.LEFT)
tb.Label(frame, text=unit).pack(side=tk.LEFT)
self.realtime_labels[key] = value_label
# ========== 状态栏 ==========
self.status = tb.Label(
self.root,
text="系统准备就绪 | 当前设备: 设备A",
bootstyle=(SECONDARY, INVERSE),
anchor=tk.CENTER
)
self.status.pack(side=tk.BOTTOM, fill=tk.X)
# 配置网格权重
main_frame.columnconfigure(0, weight=1) # 左侧设置面板
main_frame.columnconfigure(1, weight=3) # 中间监控面板主要区域
main_frame.columnconfigure(2, weight=1) # 右侧信息面板
main_frame.rowconfigure(1, weight=1)
# 监控控制变量
self.monitoring_active = False
self.monitoring_thread = None
# 启动初始监控
self.start_monitoring()
# 绑定主题切换事件
self.theme_var.trace_add("write", lambda *args: self.change_theme())
self.root.mainloop()
def start_monitoring(self):
"""启动资源监控"""
if self.monitoring_active:
return
self.status.config(text="启动系统资源监控...")
self.monitoring_active = True
# 使用线程运行监控,避免阻塞UI
self.monitoring_thread = threading.Thread(target=self.monitor_resources, daemon=True)
self.monitoring_thread.start()
def stop_monitoring(self):
"""停止资源监控"""
self.monitoring_active = False
self.status.config(text="监控已暂停")
def reset_all(self):
"""重置所有监控指标"""
for bar in self.progress_bars:
bar.set_value(0)
self.status.config(text="所有监控指标已重置")
def change_theme(self):
"""更改应用主题"""
theme = self.theme_var.get()
# 正确切换主题方法
self.root.style.theme_use(theme)
self.status.config(text=f"主题已切换为: {theme.capitalize()}")
def show_history(self):
"""显示历史数据"""
self.status.config(text="历史数据查看功能开发中...")
def monitor_resources(self):
"""监控系统资源(线程安全实现)"""
while self.monitoring_active:
# 获取系统资源数据
data = {
'cpu_temp': self.get_cpu_temperature(),
'power': self.get_power_usage(),
'mem_usage': self.get_memory_usage(),
'network': self.get_network_usage(),
'mlu_usage': self.get_mlu_usage(),
'cpu_usage': psutil.cpu_percent()
}
# 使用after安全更新UI
self.root.after(0, self.update_ui, data)
time.sleep(max(0.5, self.sampling_rate.get()))
def update_ui(self, data):
"""安全更新UI组件(在主线程执行)"""
# 更新进度条的动画速度
speed = self.animation_speed.get()
for bar in self.progress_bars:
bar.animation_speed = speed
# 更新进度条
self.progress_bars[0].set_value(data['cpu_temp'])
self.progress_bars[1].set_value(data['power'])
self.progress_bars[2].set_value(data['mem_usage'])
self.progress_bars[3].set_value(data['network'])
self.progress_bars[4].set_value(data['mlu_usage'])
self.progress_bars[5].set_value(data['cpu_usage'])
# 更新实时状态标签
self.realtime_labels["cpu_temp"].config(text=f"{data['cpu_temp']:.1f}")
self.realtime_labels["power"].config(text=f"{data['power']:.1f}")
self.realtime_labels["memory"].config(text=f"{data['mem_usage']:.1f}")
self.realtime_labels["bandwidth"].config(text=f"{data['network']:.1f}")
self.realtime_labels["mlu_usage"].config(text=f"{data['mlu_usage']:.1f}")
self.realtime_labels["cpu_usage"].config(text=f"{data['cpu_usage']:.1f}")
# 更新状态栏
status_text = (
f"当前设备: {self.device_var.get()} | "
f"CPU: {data['cpu_usage']:.1f}% | "
f"温度: {data['cpu_temp']:.1f}°C | "
f"内存: {data['mem_usage']:.1f}% | "
f"MLU: {data['mlu_usage']:.1f}%"
)
self.status.config(text=status_text)
# 资源获取方法
def get_cpu_temperature(self):
"""获取CPU温度(模拟数据)"""
return random.uniform(40, 90)
def get_power_usage(self):
"""获取功耗(模拟数据)"""
return random.uniform(50, 180)
def get_memory_usage(self):
"""获取内存使用率"""
return psutil.virtual_memory().percent
def get_network_usage(self):
"""获取网络带宽(模拟数据)"""
return random.uniform(100, 900)
def get_mlu_usage(self):
"""获取MLU利用率(模拟数据)"""
return random.uniform(10, 95)
if __name__ == "__main__":
SystemMonitorApp()