使用python解析mib文件,生成json文件提高可读性

本文介绍如何利用Python解析SNMP的MIB文件,并将其转换为JSON格式,以提高数据可读性。通过替换指定的文件路径,你可以轻松完成这个过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用python解析mib文件

只需要替换文件地址和生成文件路径,

#coding=utf-8

import re
import json

def getMib():
    trap_types=['OBJECT IDENTIFIER','OBJECT-TYPE'
import os import sys import io import socket import time import re import threading import tkinter as tk import ttkbootstrap as tb import psutil import math import random import subprocess import json import queue from ttkbootstrap.constants import * from PIL import Image, ImageTk, ImageDraw # ====================== 数据采集服务 ====================== class HardwareMonitorService: def __init__(self): self.config = self.read_config() if self.config is None: raise Exception("配置读取失败,无法启动服务") self.server_ip = self.config.get('SERVER_IP') self.server_port = int(self.config.get('SERVER_PORT')) self.client_ip = self.config.get('CLIENT_IP') self.client_port = int(self.config.get('CLIENT_PORT')) self.running = True self.sock = None self.send_count = 1 self.setup_socket() # 启动服务线程 self.service_thread = threading.Thread(target=self.run_service, daemon=True) self.service_thread.start() def read_config(self): 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("读取配置成功", flush=True) return config except FileNotFoundError: print("无法打开配置文件!", flush=True) return None def is_command_available(self, command): """检查命令是否可用""" if os.name == 'nt': # Windows 系统 result = os.system(f'where {command} >nul 2>&1') else: # Linux 系统 result = os.system(f'which {command} >/dev/null 2>&1') return result == 0 def get_hardware_info(self, send_count): # 初始化硬件信息字典 hardware_data = { "cpu_temp": 0.0, "power": 0.0, "memory_used": 0, "memory_total": 0, "bandwidth": 0.0, "mlu_usage": 0.0, "cpu_usage": 0.0 } try: # 获取温度信息 result_temp = subprocess.run( ['cnmon', 'info', '-e'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode('utf-8') temp_match = re.search(r"Board\s*[::]?\s*([-+]?\d*\.?\d+)\s*[cC°]", result_temp) if temp_match: hardware_data["cpu_temp"] = float(temp_match.group(1)) # 获取功耗信息 result_power = subprocess.run( ['cnmon', 'info', '-p'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode('utf-8') power_match = re.search(r"Usage\s+:\s+(\d+\.?\d*)\s*W", result_power) if power_match: hardware_data["power"] = float(power_match.group(1)) # 获取内存信息 result_memory = subprocess.run( ['cnmon', 'info', '-m'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode('utf-8') mem_match = re.search(r"Used\s+:\s+(\d+)\s*MiB.*?Total\s+:\s+(\d+)\s*MiB", result_memory, re.DOTALL) if mem_match: hardware_data["memory_used"] = int(mem_match.group(1)) hardware_data["memory_total"] = int(mem_match.group(2)) # 获取带宽信息 result_bandwidth = subprocess.run( ['cnmon', 'info', '-b'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode('utf-8') bw_match = re.search(r"Bandwidth\s+:\s+([\d.]+)\s*GB/s", result_bandwidth) if bw_match: hardware_data["bandwidth"] = float(bw_match.group(1)) # 获取利用率信息 result_usage = subprocess.run( ['cnmon', 'info', '-u'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode('utf-8') mlu_match = re.search(r"MLU Average\s+:\s+(\d+) %", result_usage) if mlu_match: hardware_data["mlu_usage"] = float(mlu_match.group(1)) cpu_match = re.search(r"Device CPU Chip\s+:\s+(\d+) %", result_usage) if cpu_match: hardware_data["cpu_usage"] = float(cpu_match.group(1)) except Exception as e: print(f"硬件信息获取错误: {e}") # 返回模拟数据 return self.get_fallback_data(send_count) # 添加发送次数 hardware_data["send_count"] = send_count return json.dumps(hardware_data) def get_fallback_data(self, send_count): """返回全0数据""" return json.dumps({ "cpu_temp":0.0, "power": 0.0, "memory_used": 0, "memory_total": 0, "bandwidth": 0.0, "mlu_usage": 0.0, "cpu_usage": 0.0, "send_count": send_count }) def setup_socket(self): """设置UDP套接字""" try: # 创建 UDP 套接字 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 端口号快速重用 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定服务器 IP 和端口 self.sock.bind((self.server_ip, self.server_port)) print(f"绑定到 {self.server_ip}:{self.server_port}", flush=True) except OSError as e: print(f"绑定错误: {e}", flush=True) self.running = False def run_service(self): print("硬件监控服务已启动", flush=True) while self.running: try: if not self.is_command_available('cnmon'): # 模拟数据 info = self.get_fallback_data(self.send_count) else: # 获取真实数据 info = self.get_hardware_info(self.send_count) # 发送JSON数据到客户端 self.sock.sendto(info.encode(), (self.client_ip, self.client_port)) self.send_count += 1 time.sleep(0.5) except Exception as e: print(f"服务错误: {e}", flush=True) time.sleep(1) def stop_service(self): """停止数据采集服务""" self.running = False if self.sock: self.sock.close() print("硬件监控服务已停止", flush=True) # ====================== GUI 监控界面 ====================== class EnhancedCircularProgressBar: def __init__(self, parent, size=200, thickness=20, bg_color="#1a1a1a", fg_color="#4caf50", 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.fg_color = fg_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 # 创建Canvas 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.create_gradient() # 绘制背景圆环 self.draw_background() # 创建进度弧 self.arc_id = self.canvas.create_arc( self.center_x - self.radius, self.center_y - self.radius, self.center_x + self.radius, self.center_y + self.radius, start=90, extent=0, style=tk.ARC, outline="", width=thickness, tags="progress" ) # 创建发光效果 if self.glow_effect: self.glow_id = self.canvas.create_oval( self.center_x - self.radius - 5, self.center_y - self.radius - 5, self.center_x + self.radius + 5, self.center_y + self.radius + 5, outline="", fill="", tags="glow" ) # 创建文本元素 self.create_text_elements() # 动画控制变量 self.current_value = 0 self.target_value = 0 self.animation_running = False self.animation_id = None self.last_update_time = time.time() # 性能优化 self.canvas.tag_raise("progress") self.canvas.tag_raise("text") def create_gradient(self): """创建渐变背景效果""" self.gradient_img = Image.new("RGBA", (self.size, self.size), (0, 0, 0, 0)) draw = ImageDraw.Draw(self.gradient_img) for r in range(int(self.radius), 0, -1): alpha = int(150 * (1 - r/self.radius)) draw.ellipse([ self.center_x - r, self.center_y - r, self.center_x + r, self.center_y + r ], outline=(40, 40, 40, alpha)) self.gradient_photo = ImageTk.PhotoImage(self.gradient_img) self.canvas.create_image( self.center_x, self.center_y, image=self.gradient_photo, tags="background" ) def draw_background(self): """绘制背景圆环""" self.bg_arc_id = self.canvas.create_arc( self.center_x - self.radius, self.center_y - self.radius, self.center_x + self.radius, self.center_y + self.radius, start=0, extent=359.9, style=tk.ARC, outline=self.bg_color, width=self.thickness, tags="background" ) def create_text_elements(self): """创建所有文本元素""" # 标题文本 self.title_id = self.canvas.create_text( self.center_x, self.center_y - self.radius * 0.5, text=self.title, fill=self.text_color, font=("Arial", self.font_size, "bold"), tags="text" ) # 数值文本 self.value_id = self.canvas.create_text( self.center_x, self.center_y, text="0", fill=self.text_color, font=("Arial", int(self.font_size * 1.8), "bold"), tags="text" ) # 单位文本 self.unit_id = self.canvas.create_text( self.center_x, self.center_y + self.radius * 0.3, text=self.unit, fill=self.text_color, font=("Arial", self.font_size - 2), tags="text" ) def calculate_color(self, value): """实现绿→黄→橙→红颜色过渡""" ratio = value / self.max_value if ratio <= 0.5: # 绿(0,255,0) → 黄(255,255,0) r = int(510 * ratio) g = 255 b = 0 elif ratio <= 0.75: # 黄(255,255,0) → 橙(255,165,0) r = 255 g = int(255 - 360 * (ratio - 0.5)) # 255 → 165 b = 0 else: # 橙(255,165,0) → 红(255,0,0) r = 255 g = int(165 - 660 * (ratio - 0.75)) # 165 → 0 b = 0 return f"#{r:02x}{g:02x}{b:02x}" def set_value(self, value): """设置目标值""" self.target_value = max(0, min(self.max_value, value)) # 更新数值显示 self.canvas.itemconfig(self.value_id, text=f"{self.target_value:.1f}") # 启动动画 if not self.animation_running: self.animate() def animate(self): """平滑动画更新进度""" self.animation_running = True # 计算插值(使用缓动函数) delta = self.target_value - self.current_value speed_factor = 0.2 # 控制动画速度 if abs(delta) > 0.1: self.current_value += delta * speed_factor else: self.current_value = self.target_value # 计算弧的角度 angle = 360 * (self.current_value / self.max_value) # 更新弧 self.canvas.itemconfig(self.arc_id, extent=-angle) # 更新颜色 color = self.calculate_color(self.current_value) self.canvas.itemconfig(self.arc_id, outline=color) # 更新发光效果 if self.glow_effect and time.time() - self.last_update_time > 0.1: self.update_glow_effect(color) self.last_update_time = time.time() # 继续动画或停止 if abs(self.current_value - self.target_value) > 0.5: self.animation_id = self.canvas.after(16, self.animate) else: self.current_value = self.target_value self.animation_running = False self.animation_id = None def update_glow_effect(self, color): """更新发光效果""" if not self.glow_effect: return # 创建新的发光图像 glow_img = Image.new("RGBA", (self.size, self.size), (0, 0, 0, 0)) draw = ImageDraw.Draw(glow_img) # 解析颜色 r = int(color[1:3], 16) g = int(color[3:5], 16) b = int(color[5:7], 16) # 绘制发光效果 for i in range(1, 6): alpha = int(50 * (1 - i/6)) radius = self.radius + i draw.ellipse([ self.center_x - radius, self.center_y - radius, self.center_x + radius, self.center_y + radius ], outline=(r, g, b, alpha), width=1) self.glow_photo = ImageTk.PhotoImage(glow_img) self.canvas.itemconfig(self.glow_id, image=self.glow_photo) def reset(self): """重置进度条""" if self.animation_id: self.canvas.after_cancel(self.animation_id) self.current_value = 0 self.target_value = 0 self.canvas.itemconfig(self.arc_id, extent=0) self.canvas.itemconfig(self.value_id, text="0") color = self.calculate_color(0) self.canvas.itemconfig(self.arc_id, outline=color) class SystemMonitorApp: def __init__(self): # 启动数据采集服务 try: self.monitor_service = HardwareMonitorService() client_ip = self.monitor_service.client_ip client_port = self.monitor_service.client_port except Exception as e: print(f"无法启动数据采集服务: {e}") self.monitor_service = None client_ip = "127.0.0.1" client_port = 9999 # 创建主窗口 self.root = tb.Window(themename="darkly", title="系统资源监控", size=(1200, 800)) self.root.iconbitmap("") 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")) # 创建UDP接收套接字 self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: self.udp_socket.bind((client_ip, client_port)) print(f"GUI绑定到 {client_ip}:{client_port} 接收数据") except Exception as e: print(f"GUI绑定错误: {e}") self.udp_socket.settimeout(0.1) # 设置超时避免阻塞 # 存储真实数据的变量 self.real_data = { "cpu_temp": 0.0, "power": 0.0, "memory": 0.0, # 内存使用百分比 "bandwidth": 0.0, # 带宽,单位GB/s "mlu_usage": 0.0, "cpu_usage": 0.0 } # 数据更新队列(用于线程安全) self.data_queue = queue.Queue() # 创建主框架 main_frame = tb.Frame(self.root, padding=20) main_frame.pack(fill=tk.BOTH, expand=True) # 标题 tb.Label( main_frame, text="系统资源实时监控", style="Title.TLabel" ).pack(pady=(0, 20)) # 创建进度条容器框架 progress_container = tb.Frame(main_frame) progress_container.pack(fill=tk.BOTH, expand=True, pady=10) # 创建监控指标配置 monitor_config = [ {"title": "CPU温度", "unit": "°C", "max_value": 100, "thickness": 18, "fg_color": "#ff5555"}, {"title": "功耗", "unit": "W", "max_value": 200, "thickness": 18, "fg_color": "#ffaa00"}, {"title": "内存使用", "unit": "%", "max_value": 100, "thickness": 18, "fg_color": "#55aaff"}, {"title": "网络带宽", "unit": "Mbps", "max_value": 1000, "thickness": 18, "fg_color": "#aa55ff"}, {"title": "MLU利用率", "unit": "%", "max_value": 100, "thickness": 18, "fg_color": "#00cc99"}, {"title": "CPU利用率", "unit": "%", "max_value": 100, "thickness": 18, "fg_color": "#ff55ff"} ] # 使用网格布局排列进度条 self.progress_bars = [] for i, config in enumerate(monitor_config): frame = tb.Frame(progress_container) frame.grid(row=i//3, column=i%3, padx=20, pady=20, sticky="nsew") # 创建增强型进度条 progress_bar = EnhancedCircularProgressBar( frame, size=220, thickness=config["thickness"], title=config["title"], unit=config["unit"], max_value=config["max_value"], fg_color=config["fg_color"], 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) # 控制面板 control_frame = tb.Frame(main_frame) control_frame.pack(pady=20, fill=tk.X) # 按钮框架 btn_frame = tb.Frame(control_frame) btn_frame.pack(pady=10) tb.Button( btn_frame, text="启动", bootstyle=SUCCESS, command=self.start_monitoring ).pack(side=tk.LEFT, padx=5) tb.Button( btn_frame, text="暂停", bootstyle=DANGER, command=self.stop_monitoring ).pack(side=tk.LEFT, padx=5) tb.Button( btn_frame, text="重置", bootstyle=WARNING, command=self.reset_all ).pack(side=tk.LEFT, padx=5) # 主题选择器 theme_frame = tb.Frame(control_frame) theme_frame.pack(fill=tk.X, pady=10) tb.Label(theme_frame, text="选择主题:", bootstyle=PRIMARY).pack(anchor=tk.W) self.theme_var = tk.StringVar(value="darkly") themes = ["darkly", "cyborg", "solar"] for theme in themes: tb.Radiobutton( theme_frame, text=theme.capitalize(), variable=self.theme_var, value=theme, bootstyle=PRIMARY, command=self.change_theme ).pack(side=tk.LEFT, padx=5) # 状态栏 self.status = tb.Label( self.root, text="系统准备就绪", bootstyle=(SECONDARY, INVERSE), anchor=tk.CENTER ) self.status.pack(side=tk.BOTTOM, fill=tk.X) # 监控控制变量 self.monitoring_active = False self.monitoring_thread = None # 启动初始监控 self.start_monitoring() # 设置定时器处理数据队列 self.root.after(100, self.process_updates) # 窗口关闭事件处理 self.root.protocol("WM_DELETE_WINDOW", self.on_close) 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.reset() self.status.config(text="所有监控指标已重置") def receive_real_data(self): """接收并解析真实硬件数据""" try: data, _ = self.udp_socket.recvfrom(4096) data_str = data.decode('utf-8') try: # 解析JSON数据 data_dict = json.loads(data_str) # 更新real_data self.real_data["cpu_temp"] = data_dict.get("cpu_temp", 0.0) self.real_data["power"] = data_dict.get("power", 0.0) # 计算内存使用百分比 mem_used = data_dict.get("memory_used", 0) mem_total = data_dict.get("memory_total", 1) # 避免除零错误 if mem_total > 0: self.real_data["memory"] = (mem_used / mem_total) * 100 self.real_data["bandwidth"] = data_dict.get("bandwidth", 0.0) self.real_data["mlu_usage"] = data_dict.get("mlu_usage", 0.0) self.real_data["cpu_usage"] = data_dict.get("cpu_usage", 0.0) except json.JSONDecodeError: # 兼容旧版文本格式 self._legacy_parse(data_str) except socket.timeout: pass # 没有数据是正常的 except Exception as e: print(f"接收数据错误: {e}") def _legacy_parse(self, info_str): """兼容旧版文本格式解析""" try: # 解析CPU温度 temp_match = re.search(r"Board\s+:\s+(\d+\.?\d*)\s*C", info_str) if temp_match: self.real_data["cpu_temp"] = float(temp_match.group(1)) # 解析功耗 power_match = re.search(r"Usage\s+:\s+(\d+\.?\d*)\s*W", info_str) if power_match: self.real_data["power"] = float(power_match.group(1)) # 解析内存使用率 mem_match = re.search(r"Used\s+:\s+(\d+)\s*MiB.*?Total\s+:\s+(\d+)\s*MiB", info_str, re.DOTALL) if mem_match: used = float(mem_match.group(1)) total = float(mem_match.group(2)) if total > 0: self.real_data["memory"] = (used / total) * 100 # 解析带宽 bw_match = re.search(r"Bandwidth\s+:\s+([\d.]+)\s*GB/s", info_str) if bw_match: self.real_data["bandwidth"] = float(bw_match.group(1)) # 解析MLU利用率 mlu_match = re.search(r"MLU Average:\s*(\d+\.?\d*)%", info_str) if mlu_match: self.real_data["mlu_usage"] = float(mlu_match.group(1)) # 解析CPU利用率 cpu_match = re.search(r"Device CPU Chip:\s*(\d+\.?\d*)%", info_str) if cpu_match: self.real_data["cpu_usage"] = float(cpu_match.group(1)) except Exception as e: print(f"旧版解析错误: {e}") def monitor_resources(self): """监控系统资源""" while self.monitoring_active: # 接收并解析真实数据 self.receive_real_data() # 使用真实数据或模拟数据 cpu_temp = self.real_data["cpu_temp"] or self.get_cpu_temperature() power = self.real_data["power"] or self.get_power_usage() mem_usage = self.real_data["memory"] or self.get_memory_usage() # 带宽单位转换:GB/s → Mbps (1 GB/s = 8000 Mbps) bandwidth_gb = self.real_data["bandwidth"] network = bandwidth_gb * 8000 # 转换为Mbps if network <= 0: # 如果真实数据无效,使用模拟数据 network = self.get_network_usage() mlu_usage = self.real_data["mlu_usage"] or self.get_mlu_usage() cpu_usage = self.real_data["cpu_usage"] or psutil.cpu_percent() # 将数据放入队列,由主线程更新UI self.data_queue.put((cpu_temp, power, mem_usage, network, mlu_usage, cpu_usage)) time.sleep(1) def process_updates(self): """从队列中取出数据并更新UI(在主线程中调用)""" try: while not self.data_queue.empty(): data = self.data_queue.get_nowait() # 更新进度条 self.progress_bars[0].set_value(data[0]) self.progress_bars[1].set_value(data[1]) self.progress_bars[2].set_value(data[2]) self.progress_bars[3].set_value(data[3]) self.progress_bars[4].set_value(data[4]) self.progress_bars[5].set_value(data[5]) # 更新状态栏 status_text = ( f"CPU: {data[5]:.1f}% | " f"温度: {data[0]:.1f}°C | " f"内存: {data[2]:.1f}% | " f"MLU: {data[4]:.1f}%" ) self.status.config(text=status_text) except queue.Empty: pass # 每100毫秒检查一次 self.root.after(100, self.process_updates) def get_cpu_temperature(self): """获取CPU温度(模拟)""" base_temp = 40.0 fluctuation = random.uniform(-2, 8) load_factor = self.progress_bars[5].current_value / 100 * 10 return min(100, max(30, base_temp + fluctuation + load_factor)) def get_power_usage(self): """获取功耗使用(模拟)""" base_power = 80.0 fluctuation = random.uniform(-10, 15) load_factor = (self.progress_bars[5].current_value + self.progress_bars[4].current_value) / 200 * 50 return min(200, max(50, base_power + fluctuation + load_factor)) def get_memory_usage(self): """获取内存使用率""" return psutil.virtual_memory().percent def get_network_usage(self): """获取网络带宽使用(模拟)""" base_usage = 300.0 fluctuation = random.uniform(-50, 100) return min(1000, max(0, base_usage + fluctuation)) def get_mlu_usage(self): """获取MLU利用率(模拟)""" base_usage = 30.0 fluctuation = random.uniform(-5, 15) load_factor = random.uniform(0, 40) return min(100, max(0, base_usage + fluctuation + load_factor)) def change_theme(self): """更改应用主题""" theme = self.theme_var.get() tb.Style(theme=theme) self.status.config(text=f"主题已切换为: {theme.capitalize()}") def on_close(self): """窗口关闭时清理资源""" self.monitoring_active = False if self.monitoring_thread and self.monitoring_thread.is_alive(): self.monitoring_thread.join(timeout=2.0) try: if self.monitor_service: self.monitor_service.stop_service() self.udp_socket.close() except Exception as e: print(f"关闭资源时出错: {e}") self.root.destroy() if __name__ == "__main__": # 设置无缓冲输出 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', line_buffering=True) # 启动GUI应用 SystemMonitorApp() 在改代码左侧增加下拉选项按钮内容待定,合理布局,写出完整代码
最新发布
06-22
<think>我们将在现有GUI的左侧添加一个下拉选项按钮(Combobox)。考虑到当前布局是3x2的环形进度条网格,我们可以将左侧区域独立出来作为控制面板,包含下拉框和其他可能的控制元素。修改步骤:1.将主框架(main_frame)拆分为两个部分:左侧控制面板(left_frame)和右侧监控面板(right_frame)2.在左侧控制面板中添加下拉框(Combobox)和其他控件3.调整布局,使左侧控制面板宽度固定,右侧监控面板可扩展具体实现:-使用PanedWindow或Frame+grid布局来实现左右分割-左侧控制面板放置在下拉框,并可以添加其他控制元素-右侧监控面板保持原来的3x2网格布局注意:原代码中,监控面板(progress_container)是直接放在main_frame中的。现在我们将progress_container放入right_frame。我们将进行以下调整:main_frame├──left_frame(控制面板,固定宽度,靠左)└──right_frame(监控面板,填充剩余空间)在左侧控制面板中,我们添加一个标签和一个下拉框,垂直排列。另外,我们还需要调整网格布局的列权重,使得右侧面板能够扩展。由于原代码使用的是pack布局,我们将改为grid布局来实现左右分栏。具体修改:1.修改main_frame的布局方式为grid2.创建两个子框架:left_frame和right_frame3.将控制面板的控件放入left_frame4.将原来的progress_container放入right_frame代码修改部分:原代码:main_frame=tb.Frame(self.root,padding=20)main_frame.pack(fill=tk.BOTH,expand=True)修改为使用grid布局:main_frame=tb.Frame(self.root,padding=20)main_frame.pack(fill=tk.BOTH,expand=True)#仍然使用pack,内部用grid但是,为了左右分栏,我们在main_frame内部使用grid布局:left_frame=tb.Frame(main_frame,width=200)#固定宽度left_frame.grid(row=0,column=0,padx=10,pady=10,sticky="nswe")right_frame=tb.Frame(main_frame)right_frame.grid(row=0,column=1,padx=10,pady=10,sticky="nswe")然后设置main_frame的列权重,使得右侧可以扩展:main_frame.columnconfigure(1,weight=1)main_frame.rowconfigure(0,weight=1)在left_frame中,我们添加下拉框:#下拉框的标签label=tb.Label(left_frame,text="选择设备:",bootstyle=PRIMARY)label.pack(pady=(10,5),anchor='w')#创建下拉框self.device_combobox=tb.Combobox(left_frame,values=["设备1","设备2","设备3"],state="readonly")self.device_combobox.pack(pady=5,fill=tk.X,padx=5)self.device_combobox.current(0)#默认选择第一个注意:为了保持左侧控制面板的控件排列,我们使用pack布局,从上到下排列。同时,我们需要调整原来放在main_frame中的标题的位置。原来标题是直接pack在main_frame中,现在我们将标题放在right_frame的顶部。原代码:tb.Label(main_frame,...).pack(...)修改后,将标题放入right_frame:tb.Label(right_frame,...).pack(...)#注意,这里right_frame内部使用pack,所以标题会放在顶部同样,将progress_container放入right_frame,并使用pack。由于布局调整,我们还需要确保left_frame的高度跟随窗口扩展,所以设置sticky="nswe",并且设置left_frame的行列权重(如果使用grid布局的话,但这里left_frame内部是pack,所以只需要设置left_frame的sticky为ns)。另外,为了保持左侧控制面板的宽度固定,我们可以设置left_frame的width,并且设置grid_propagate(False)来固定宽度。但这里我们使用pack,所以不需要。但是,为了确保左侧面板不会随窗口拉伸而改变宽度,我们设置left_frame的宽度,并设置grid的sticky为ns(上下拉伸,左右固定)。修改后的代码步骤:1.创建main_frame,使用pack填充2.在main_frame内创建两个子框架:left_frame(左侧控制面板)和right_frame(右侧监控面板),使用grid布局3.配置main_frame的列权重:第0列(left_frame)固定宽度,第1列(right_frame)可扩展4.在left_frame中添加控件(标签、下拉框等)5.将标题和progress_container移动到right_frame中注意:原控制面板(control_frame)和状态栏(status)的位置也需要调整。控制面板我们之前是放在main_frame底部,现在我们可以选择放在left_frame底部或者保持原位置。这里为了保持原布局风格,我们将控制面板放在right_frame底部(即原位置),状态栏仍然在窗口底部。因此,控制面板(control_frame)和状态栏的父容器不变,但控制面板原来是在main_frame中,现在main_frame已经分成了左右两部分,那么控制面板应该放在right_frame的底部?但是原代码中控制面板是放在所有进度条的下方,所以我们将其放入right_frame,并pack在progress_container之后。修改后的代码结构:main_frame(pack填充)grid布局:一行两列列0:left_frame(固定宽度,放置下拉框)列1:right_frame(填充剩余空间)right_frame内部:标题(pack在顶部)progress_container(pack,填充)control_frame(pack在底部)状态栏仍然pack在窗口底部(self.root)。由于修改较大,下面将详细代码列出,但只关注布局部分。注意:由于原代码较长,我们只修改布局部分,其他功能保持不变。修改后的SystemMonitorApp的__init__方法(仅布局部分):</think># 添加左侧下拉选项按钮的Python GUI实现 我将指导您在现有代码基础上添加左侧下拉选项按钮,并实现合理的布局设计。主要修改集中在`SystemMonitorApp`类的`__init__`方法中。 ## 实现思路 1. **拆分主布局**:将主窗口分为左右两部分 2. **添加左侧面板**:包含下拉选项按钮和其他控件 3. **保持右侧监控面板**:原有的进度条显示区域 4. **优化布局管理**:使用`grid`和`pack`组合实现响应式布局 ## 修改后的代码实现 ```python class SystemMonitorApp: def __init__(self): # ... 前面的代码保持不变 ... # 创建主框架 - 改为水平分割布局 main_frame = tb.Frame(self.root, padding=10) main_frame.pack(fill=tk.BOTH, expand=True) # ========== 左侧控制面板 ========== left_panel = tb.Frame(main_frame, width=220, padding=10) left_panel.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10)) # 下拉选项按钮 tb.Label(left_panel, text="选择监控设备", bootstyle=PRIMARY).pack(anchor=tk.W, pady=(0, 5)) self.device_var = tk.StringVar() device_options = ["设备A", "设备B", "设备C", "设备D"] device_combo = tb.Combobox( left_panel, textvariable=self.device_var, values=device_options, state="readonly", bootstyle=PRIMARY ) device_combo.pack(fill=tk.X, pady=(0, 20)) device_combo.current(0) # 设置默认选项 # 其他控制选项 tb.Label(left_panel, text="监控设置", bootstyle=PRIMARY).pack(anchor=tk.W, pady=(10, 5)) # 采样频率滑块 self.sampling_rate = tk.IntVar(value=1) tb.Label(left_panel, text="采样频率(秒):").pack(anchor=tk.W) tb.Scale( left_panel, from_=0.5, to=5, length=180, orient=tk.HORIZONTAL, variable=self.sampling_rate, bootstyle=PRIMARY ).pack(fill=tk.X, pady=(0, 15)) # 报警阈值设置 tb.Label(left_panel, text="温度报警阈值(°C):").pack(anchor=tk.W) self.temp_threshold = tk.IntVar(value=80) tb.Entry( left_panel, textvariable=self.temp_threshold, width=10, bootstyle=PRIMARY ).pack(fill=tk.X, pady=(0, 15)) # 控制按钮 control_frame = tb.Frame(left_panel) control_frame.pack(fill=tk.X, pady=(10, 0)) tb.Button( control_frame, text="启动", bootstyle=SUCCESS, command=self.start_monitoring ).pack(side=tk.LEFT, padx=2) tb.Button( control_frame, text="暂停", bootstyle=DANGER, command=self.stop_monitoring ).pack(side=tk.LEFT, padx=2) tb.Button( control_frame, text="重置", bootstyle=WARNING, command=self.reset_all ).pack(side=tk.LEFT, padx=2) # 状态指示器 tb.Label(left_panel, text="连接状态", bootstyle=PRIMARY).pack(anchor=tk.W, pady=(15, 5)) self.status_indicator = tb.Label( left_panel, text="● 已连接", bootstyle=(SUCCESS, INVERSE), font=("Arial", 10) ) self.status_indicator.pack(fill=tk.X, pady=(0, 5)) # ========== 右侧监控面板 ========== right_panel = tb.Frame(main_frame) right_panel.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # 标题 tb.Label( right_panel, text="系统资源实时监控", style="Title.TLabel" ).pack(pady=(0, 15)) # 创建进度条容器框架 progress_container = tb.Frame(right_panel) progress_container.pack(fill=tk.BOTH, expand=True, pady=5) # 创建监控指标配置 (保持不变) monitor_config = [ {"title": "CPU温度", "unit": "°C", "max_value": 100, "thickness": 18, "fg_color": "#ff5555"}, {"title": "功耗", "unit": "W", "max_value": 200, "thickness": 18, "fg_color": "#ffaa00"}, {"title": "内存使用", "unit": "%", "max_value": 100, "thickness": 18, "fg_color": "#55aaff"}, {"title": "网络带宽", "unit": "Mbps", "max_value": 1000, "thickness": 18, "fg_color": "#aa55ff"}, {"title": "MLU利用率", "unit": "%", "max_value": 100, "thickness": 18, "fg_color": "#00cc99"}, {"title": "CPU利用率", "unit": "%", "max_value": 100, "thickness": 18, "fg_color": "#ff55ff"} ] # 使用网格布局排列进度条 (保持不变) 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"], fg_color=config["fg_color"], 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) # ========== 状态栏 ========== self.status = tb.Label( self.root, text="系统准备就绪 | 当前设备: 设备A", bootstyle=(SECONDARY, INVERSE), anchor=tk.CENTER ) self.status.pack(side=tk.BOTTOM, fill=tk.X) # ... 后面的代码保持不变 ... ``` ## 布局设计说明 ### 左侧面板设计 1. **设备选择下拉框**:用户可以选择不同的监控设备 2. **采样频率滑块**:控制数据采集频率 (0.5-5秒) 3. **报警阈值设置**:设置CPU温度报警阈值 4. **控制按钮**:启动/暂停/重置监控 5. **状态指示器**:显示当前连接状态 ### 右侧面板设计 1. **标题区域**:显示"系统资源实时监控" 2. **监控指标区域**:保持原有的6个环形进度条,3×2网格布局 ### 状态栏 1. 显示系统状态信息 2. 显示当前选中的设备 ### 响应式布局特性 1. 左侧面板宽度固定(220px),高度随窗口调整 2. 右侧面板填充剩余空间 3. 进度条网格自动调整大小 4. 状态栏始终位于窗口底部 ## 功能扩展建议 1. **添加设备管理功能**: ```python def add_new_device(self): """添加新设备到下拉选项""" new_device = simpledialog.askstring("添加设备", "输入设备名称:") if new_device: current_values = list(self.device_combo['values']) current_values.append(new_device) self.device_combo['values'] = current_values self.device_combo.set(new_device) self.status.config(text=f"已添加设备: {new_device}") ``` 2. **实现报警功能**: ```python def check_temperature_alert(self, temperature): """检查温度是否超过阈值""" if temperature > self.temp_threshold.get(): messagebox.showwarning( "温度报警", f"CPU温度过高: {temperature}°C (阈值: {self.temp_threshold.get()}°C)" ) # 更新状态指示器 self.status_indicator.config( text="● 高温报警!", bootstyle=(DANGER, INVERSE) ) ``` 3. **动态调整采样频率**: ```python def monitor_resources(self): """监控系统资源 - 只使用真实数据""" while self.monitoring_active: # 获取当前采样频率 sampling_rate = self.sampling_rate.get() # 接收并解析真实数据 self.receive_real_data() # ... 数据处理保持不变 ... # 根据采样频率休眠 time.sleep(sampling_rate) ``` ## 布局优化技巧 1. **使用填充(padx/pady)**:在控件之间添加适当间距,提高可读性 2. **对齐方式(anchor)**:确保文本和控件对齐一致 3. **权重分配(weight)**:使用`columnconfigure`和`rowconfigure`分配空间比例 4. **固定宽度控件**:左侧面板设置固定宽度,防止内容变形 5. **响应式设计**:使用`fill`和`expand`参数确保内容随窗口调整 ## 最终效果 ![GUI布局示意图](https://via.placeholder.com/800x500?text=左侧控制面板+右侧监控区域) 这个布局实现了: - 清晰的功能分区(左侧控制,右侧监控) - 响应式设计,适应不同窗口大小 - 专业美观的控件排列 - 直观的用户操作流程
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liangblog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值