CSS3CORE_2.转换、过渡、动画、CSS优化

本文深入解析CSS的转换、过渡及动画技术,包括2D和3D转换、过渡效果控制、关键帧动画制作,以及如何优化CSS以提升网页性能。

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

1 转换

1.1 转换简介

  1. 转换定义
    使元素改变形状、尺寸和位置的效果,可以对元素应用2D或3D转换进行旋转、缩放、移动或倾斜;(改变元素在页面中的位置、大小、角度和形状)
    2D转换:使元素在X轴和Y轴平面上变化,改变其形状、尺寸和位置
    3D转换:增加z轴的效果,网页中不存在3D,模拟效果
  2. 转换属性
    transform属性向元素应用2D或3D转换,取值:
    none:默认值,表示元素不进行转换
    transform function:一个或者多个转换函数,以空格分开;Eg. transform:rotate(90deg) scale(0.8);
  3. 转换原点
    transform-origin指定元素原点位置,默认原点在元素中心点或者x轴和Y轴的50%处;
    transform-origin:数值/百分比/关键字;
    一个值表示所有轴的位置;
    两个值表示X轴和Y轴;
    三个值表示X轴Y轴和Z轴的位置

1.2 2D转换

  1. 2D位移
    translate()方法将元素从其当前位置移动
    translate(x)指定元素在x轴上移动位置;
    translate(x, y)指定元素在x和y的移动位置
    translateX(x)指定X轴位移
    translateY(y)指定Y轴位移
  2. 2D缩放
    scale(value)方法用于等比例改变元素的尺寸,默认为1,绝对值大于1放大,小于1缩小;负数表示反转(以原点为中心旋转180deg);
    scale(x,y)分别代表x轴和Y轴的缩放比例;
    单向缩放函数scaleX()scaleY()
  3. 2D旋转
    rotate()方法用于旋转元素,改变元素在页面中的角度
    根据原点顺时针旋转给定的角度,负值表示逆时针旋转
    旋转原点会影响旋转效果,旋转连同坐标轴一起旋转,会影响之后的位移效果
  4. 2D倾斜
    skew()方法用于倾斜元素,以原点位置围绕X轴和Y轴按照一定角度倾斜,可能会改变元素形状,取值为角度。skew(xdeg)向x轴倾斜,实际改变y轴角度;skew(xdeg, ydeg)
    单向倾斜函数:skewX(xdeg)正数表示逆时针方向倾斜,skewY(ydeg)正数表示顺时针方向倾斜

1.3 3D转换

  1. perspective属性
    属性定义3D元素距视图的距离,单位px;只影响3D转换元素,子元素获得透视效果而不是定义的元素本身
    浏览器兼容性:Chrome和Safari支持-webkit-perspective
  2. 3D位移
    改变元素在Z轴的位置,主要包含translateZ(z),translate(x, y ,z)
  3. 3D旋转
    rotateX(deg)以X轴为中心轴,旋转元素的角度
    rotateY(deg)以Y轴为中心轴,旋转元素的角度
    rotateZ(deg)以Z轴为中心轴,旋转元素的角度
    rotate3d(x, y, z, deg)xyz为0表示不参与旋转
  4. 3D缩放
    scaleZ(z)
    scale3d(x, y, z)

2 过渡

2.1 过渡概述

  1. 过渡定义
    使CSS属性值在一段时间内平滑过渡
    transition: background 3s linear 1s;
  2. 触发过渡
    过渡由用户行为(点击、悬浮等)触发;
    由元素的状态变化触发;
    由JavaScript代码触发

2.2 过渡属性

过渡属性写在元素声明的样式中有去有回;写在触发的操作(:hover等)中有去无回

  1. 过渡属性transition-property
    属性规定应用过渡效果的CSS属性名称;
    transition-property:none/all/property;
    all表示所有可过渡的属性都使用过渡效果
    可以设置过渡的属性:
    1)颜色属性
    2)取值为数值的属性
    3)转换属性
    4)渐变属性
    5)阴影属性
    6)visibility属性
  2. 过渡时间transition-duration
    过渡的持续时长,以s/ms为单位的数字,默认值为0;
  3. 过渡函数transition-timing-function
    过渡时间曲线函数,取值:
    linear匀速
    ease默认值,逐渐慢下来
    ease-in慢 加速
    ease-out快 减速
    ease-in-out加速减速
  4. 过渡延迟transition-delay
    规定过渡效果在元素属性值改变后多久开始执行过渡效果(s/ms)
  5. 简写属性transition
    用于设置四个过渡属性
    transition: property duration timing-function delay;最少也要包含duration属性
  6. 多过渡效果
    设置多个过渡子属性,不同属性值之间用逗号隔开

3 动画

3.1 动画概述

  1. 动画定义
    过渡属性只能模拟动画效果,animation属性可以通过关键帧控制每一步动画,使元素逐渐变化为另一种样式,实现复杂的动画效果(相当于将多个过渡效果放一起使用)
    关键帧:1.动画的执行时间点;2.该时间点上的样式;
    浏览器兼容性:
    Chrome和Safari需要前缀-webkit-;
    Firefox需要前缀-moz-
    Opera需要前缀-o-

  2. 步骤
    1)声明动画:创建一个已命名的动画,使用@keyframes声明动画关键帧

    @keyframes 动画名称{
    	0%{}
    	25%{}
    	...
    	100%{}
    }
    
    1. @keyframes作用
      声明动画,指定关键帧定义每个时间点上的动作
    2. @keyframes语法
      @-webkit-keyframes name
      @-moz-keyframes name
      @-o-keyframes name
      from可表示0%,to可表示100%,中间percent

    2)使用animation属性调用动画,设置动画播放时间、播放次数等

3.2 动画属性

  1. 动画子属性
    1. animation-name:调用动画,规定需要绑定到选择器的keyframe的名称
    2. animation-duration:动画完成一个周期需要的时间,取值为数值,单位s/ms
    3. animation-timing-function:规定动画的速度曲线,取值为预定义函数——linear、ease、ease-in、ease-out、ease-in-out;也可以为贝塞尔曲线
    4. animation-delay:播放之前的延迟时间,取值为数值,单位s/ms
    5. animation-iteration-count:播放次数,取值数值或者infinite无限播放
    6. animation-direction:动画播放方向,默认取值normal,reverse逆向播放,alternate轮流播放,即奇数次正常播放,偶数次向前播放
  2. animation属性
    用于控制动画,调用@keyframes定义的动画并设置属性:
    animation: name duration timing-function delay iteration-count direction
  3. animation-fill-mode属性
    规定动画在播放之前或之后的动画效果是否可见,取值:
    none不改变默认行为
    forwards动画完成后保持最后一个关键帧
    backwards动画播放前在延迟时间内保持第一个关键帧
    both同时应用forwards和backwards
  4. animation-play-state属性
    规定动画正在运行还是暂停,结合JavaScript代码实现暂停播放,可取值paused和running
  5. 动画与过渡
    过渡用于制作简单的动画,动画制作复杂的动画,且控制更精确;
    animation和transition大部分属性相同,都是随时间改变元素的属性值,主要区别在于transition需要触发事件才能改变属性,而animation不需要触发的情况就能随着时间发生属性的变化;transition只有两帧,而animation可以有多个帧

4 CSS优化

  1. CSS优化概述
    减轻服务器压力,缩短服务器响应时间,提高用户的体验
  2. CSS优化原则
    1)尽量减少HTTP请求个数:CSS sprites合并多个背景图像到一个图像,通过background-image和background-position进行调整;Images maps结合多个图像到一个图像,总体规模大致相当,但减少HTTP请求的数量能加快页面显示的速度;
    2)页面顶部引入css文件:样式表放在头部,允许页面逐步呈现,可以提高页面加载速度;
    3)把CSS文件和JS文件放到外部独立的文件中:页面引入外部文件将由浏览器缓存,后续页会使用缓存
  3. CSS代码优化
    1)合并样式:利用CSS继承,提前定义统一的样式,使用群组;
    2)缩小样式文件:使用简写属性
    3)选择更优的样式属性值
    4)减少样式重写
    5)代码压缩:使用yuicompressor工具压缩CSS代码
    6)不在HTML中缩放图像,图像在网络传输中保持原来图像字节数
    7)避免空的src和href=""
    https://www.cnblogs.com/shenxiaolin/p/5390237.html
    https://www.cnblogs.com/sysg/p/6553975.html
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(&#39;SERVER_IP&#39;) self.server_port = int(self.config.get(&#39;SERVER_PORT&#39;)) self.client_ip = self.config.get(&#39;CLIENT_IP&#39;) self.client_port = int(self.config.get(&#39;CLIENT_PORT&#39;)) 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(&#39;config.txt&#39;, &#39;r&#39;) as config_file: for line in config_file: if &#39;=&#39; in line: key, value = line.strip().split(&#39;=&#39;, 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 == &#39;nt&#39;: # Windows 系统 result = os.system(f&#39;where {command} >nul 2>&1&#39;) else: # Linux 系统 result = os.system(f&#39;which {command} >/dev/null 2>&1&#39;) 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( [&#39;cnmon&#39;, &#39;info&#39;, &#39;-e&#39;], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode(&#39;utf-8&#39;) 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( [&#39;cnmon&#39;, &#39;info&#39;, &#39;-p&#39;], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode(&#39;utf-8&#39;) 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( [&#39;cnmon&#39;, &#39;info&#39;, &#39;-m&#39;], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode(&#39;utf-8&#39;) 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( [&#39;cnmon&#39;, &#39;info&#39;, &#39;-b&#39;], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode(&#39;utf-8&#39;) 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( [&#39;cnmon&#39;, &#39;info&#39;, &#39;-u&#39;], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1.0, check=True ).stdout.decode(&#39;utf-8&#39;) 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(&#39;cnmon&#39;): # 模拟数据 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=(1300, 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=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) # 监控控制变量 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(&#39;utf-8&#39;) 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=&#39;utf-8&#39;, line_buffering=True) # 启动GUI应用 SystemMonitorApp() 修改这段程序只保留设计好的ui界面
最新发布
06-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值