鱼C论坛

 找回密码
 立即注册
查看: 30|回复: 3

[技术交流] 测试workbuddy写硬件监控软件

[复制链接]
发表于 4 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 chinajz 于 2026-3-15 10:35 编辑

workbuddy写的硬件监控软件,测试结果,达到预期要求。
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. CPU/GPU 温度监控 — 图形界面 (gui.py)
  5. 依赖:tkinter(Python 内置)、matplotlib

  6. 启动方式:
  7.   python main.py --gui
  8.   python gui.py        (直接运行)
  9. """

  10. import sys
  11. import time
  12. import threading
  13. from datetime import datetime
  14. from collections import deque

  15. if sys.platform == "win32":
  16.     try:
  17.         sys.stdout.reconfigure(encoding="utf-8")
  18.     except Exception:
  19.         pass

  20. try:
  21.     import tkinter as tk
  22.     from tkinter import ttk, font as tkfont
  23. except ImportError:
  24.     raise ImportError("tkinter 不可用,请确认 Python 安装时包含了 Tk 支持")

  25. try:
  26.     import matplotlib
  27.     matplotlib.use("TkAgg")
  28.     from matplotlib.figure import Figure
  29.     from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
  30.     HAS_MPL = True
  31. except ImportError:
  32.     HAS_MPL = False

  33. # 导入数据采集模块(与 main.py 同目录)
  34. import os
  35. sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
  36. from main import (
  37.     get_cpu_temperature, get_gpu_temperature,
  38.     get_cpu_info, get_gpu_info,
  39.     CPU_WARN_TEMP, CPU_CRIT_TEMP,
  40.     GPU_WARN_TEMP, GPU_CRIT_TEMP,
  41.     HAS_PYNVML, HAS_PSUTIL,
  42. )

  43. # ── 颜色主题 ─────────────────────────────────────────────────────────────────
  44. COLORS = {
  45.     "bg":       "#1e1e2e",
  46.     "panel":    "#2a2a3e",
  47.     "border":   "#44475a",
  48.     "text":     "#cdd6f4",
  49.     "subtext":  "#6c7086",
  50.     "green":    "#a6e3a1",
  51.     "yellow":   "#f9e2af",
  52.     "red":      "#f38ba8",
  53.     "blue":     "#89b4fa",
  54.     "mauve":    "#cba6f7",
  55.     "cpu_line": "#89b4fa",
  56.     "gpu_line": "#f38ba8",
  57. }

  58. REFRESH_MS  = 2000   # 界面刷新间隔(毫秒)
  59. HISTORY_LEN = 60     # 曲线历史长度


  60. # ════════════════════════════════════════════════════════════════════════════
  61. # 数据模型
  62. # ════════════════════════════════════════════════════════════════════════════

  63. class HardwareData:
  64.     def __init__(self):
  65.         self.cpu_temp_history = deque([None] * HISTORY_LEN, maxlen=HISTORY_LEN)
  66.         self.gpu_temp_history = deque([None] * HISTORY_LEN, maxlen=HISTORY_LEN)
  67.         self.lock = threading.Lock()

  68.         # 最新一帧数据
  69.         self.cpu_temps    = {}
  70.         self.gpu_temps    = {}
  71.         self.cpu_info     = {}
  72.         self.gpu_list     = []
  73.         self.timestamp    = ""
  74.         self.fetching     = False

  75.     def fetch(self):
  76.         """在后台线程里采集数据"""
  77.         self.fetching = True
  78.         try:
  79.             cpu_temps = get_cpu_temperature()
  80.             gpu_temps = get_gpu_temperature()
  81.             cpu_info  = get_cpu_info()
  82.             gpu_list  = get_gpu_info()
  83.             ts        = datetime.now().strftime("%H:%M:%S")

  84.             # 取物理核心(过滤 Thread 和 Package/Tdie 聚合传感器)的最高温度作为曲线代表值
  85.             _PKG_KW = ("package", "tdie", "tctl", "average", "max", "distance")
  86.             phys_temps = {k: v for k, v in cpu_temps.items() if "Thread" not in k}
  87.             src = phys_temps if phys_temps else cpu_temps
  88.             core_vals = [v for k, v in src.items()
  89.                          if not any(kw in k.lower() for kw in _PKG_KW)]
  90.             cpu_peak = max(core_vals) if core_vals else (max(src.values()) if src else None)
  91.             # 第一块 GPU 的温度
  92.             gpu_peak = None
  93.             if gpu_list:
  94.                 i    = gpu_list[0]["index"]
  95.                 name = gpu_list[0]["name"]
  96.                 gpu_peak = gpu_temps.get(f"GPU{i} {name}")
  97.             elif gpu_temps:
  98.                 gpu_peak = list(gpu_temps.values())[0]

  99.             with self.lock:
  100.                 self.cpu_temps = cpu_temps
  101.                 self.gpu_temps = gpu_temps
  102.                 self.cpu_info  = cpu_info
  103.                 self.gpu_list  = gpu_list
  104.                 self.timestamp = ts
  105.                 self.cpu_temp_history.append(cpu_peak)
  106.                 self.gpu_temp_history.append(gpu_peak)
  107.         except Exception as e:
  108.             print(f"[数据采集异常] {e}")
  109.         finally:
  110.             self.fetching = False


  111. # ════════════════════════════════════════════════════════════════════════════
  112. # 主窗口
  113. # ════════════════════════════════════════════════════════════════════════════

  114. class MonitorApp(tk.Tk):
  115.     def __init__(self):
  116.         super().__init__()
  117.         self.title("CPU / GPU 硬件监控")
  118.         self.configure(bg=COLORS["bg"])
  119.         self.resizable(True, True)
  120.         self.geometry("900x640")

  121.         self.data = HardwareData()

  122.         self._build_ui()
  123.         self._schedule_refresh()

  124.     # ── UI 构建 ────────────────────────────────────────────────────────────

  125.     def _build_ui(self):
  126.         # 顶部标题栏
  127.         title_frame = tk.Frame(self, bg=COLORS["bg"])
  128.         title_frame.pack(fill="x", padx=16, pady=(12, 4))

  129.         tk.Label(
  130.             title_frame, text="🖥  硬件温度监控",
  131.             bg=COLORS["bg"], fg=COLORS["text"],
  132.             font=("Segoe UI", 16, "bold"),
  133.         ).pack(side="left")

  134.         self.lbl_time = tk.Label(
  135.             title_frame, text="",
  136.             bg=COLORS["bg"], fg=COLORS["subtext"],
  137.             font=("Segoe UI", 10),
  138.         )
  139.         self.lbl_time.pack(side="right")

  140.         # 主体两列布局
  141.         body = tk.Frame(self, bg=COLORS["bg"])
  142.         body.pack(fill="both", expand=True, padx=16, pady=4)

  143.         left  = tk.Frame(body, bg=COLORS["bg"])
  144.         right = tk.Frame(body, bg=COLORS["bg"])
  145.         left.pack(side="left", fill="both", expand=True, padx=(0, 8))
  146.         right.pack(side="right", fill="both", expand=True)

  147.         # 左列:CPU 面板 + GPU 面板
  148.         self._cpu_panel = self._make_panel(left, "📊  CPU", expand=True)
  149.         self._gpu_panel = self._make_panel(left, "🎮  GPU", expand=True)

  150.         # 右列:温度曲线图
  151.         if HAS_MPL:
  152.             chart_frame = self._make_panel(right, "📈  温度趋势(近 60 次)", expand=True)
  153.             self._build_chart(chart_frame)
  154.         else:
  155.             warn = self._make_panel(right, "⚠  提示", expand=True)
  156.             tk.Label(
  157.                 warn, text="安装 matplotlib 以显示温度趋势图\npip install matplotlib",
  158.                 bg=COLORS["panel"], fg=COLORS["yellow"],
  159.                 font=("Segoe UI", 10), justify="left",
  160.             ).pack(padx=12, pady=12, anchor="w")

  161.         # 底部状态栏
  162.         bar = tk.Frame(self, bg=COLORS["border"], height=1)
  163.         bar.pack(fill="x", padx=16)
  164.         self.lbl_status = tk.Label(
  165.             self, text="正在加载…",
  166.             bg=COLORS["bg"], fg=COLORS["subtext"],
  167.             font=("Segoe UI", 9),
  168.         )
  169.         self.lbl_status.pack(anchor="w", padx=16, pady=(2, 8))

  170.     def _make_panel(self, parent, title: str, expand: bool = False) -> tk.Frame:
  171.         """创建带标题的卡片面板,返回内容区 Frame"""
  172.         outer = tk.Frame(parent, bg=COLORS["border"], bd=0)
  173.         outer.pack(fill="both", expand=expand, pady=4)

  174.         inner = tk.Frame(outer, bg=COLORS["panel"], bd=0)
  175.         inner.pack(fill="both", expand=True, padx=1, pady=1)

  176.         tk.Label(
  177.             inner, text=title,
  178.             bg=COLORS["panel"], fg=COLORS["blue"],
  179.             font=("Segoe UI", 11, "bold"),
  180.         ).pack(anchor="w", padx=12, pady=(10, 4))

  181.         sep = tk.Frame(inner, bg=COLORS["border"], height=1)
  182.         sep.pack(fill="x", padx=12)

  183.         content = tk.Frame(inner, bg=COLORS["panel"])
  184.         content.pack(fill="both", expand=True, padx=12, pady=(6, 10))
  185.         return content

  186.     def _build_chart(self, parent: tk.Frame):
  187.         """在 parent 内嵌入 matplotlib 折线图"""
  188.         fig = Figure(figsize=(4.5, 4), dpi=90, facecolor=COLORS["bg"])
  189.         self._ax = fig.add_subplot(111)
  190.         self._ax.set_facecolor(COLORS["panel"])
  191.         fig.tight_layout(pad=1.5)

  192.         self._canvas = FigureCanvasTkAgg(fig, master=parent)
  193.         self._canvas.get_tk_widget().pack(fill="both", expand=True)
  194.         self._fig = fig

  195.         self._line_cpu, = self._ax.plot(
  196.             [], [], color=COLORS["cpu_line"], linewidth=1.8, label="CPU"
  197.         )
  198.         self._line_gpu, = self._ax.plot(
  199.             [], [], color=COLORS["gpu_line"], linewidth=1.8, label="GPU"
  200.         )

  201.         self._ax.axhline(CPU_WARN_TEMP, color=COLORS["yellow"], linewidth=0.8,
  202.                          linestyle="--", alpha=0.6, label=f"警告 {CPU_WARN_TEMP}°C")
  203.         self._ax.axhline(CPU_CRIT_TEMP, color=COLORS["red"], linewidth=0.8,
  204.                          linestyle="--", alpha=0.6, label=f"危险 {CPU_CRIT_TEMP}°C")

  205.         self._ax.set_ylabel("温度 (°C)", color=COLORS["text"], fontsize=9)
  206.         self._ax.set_xlabel("历史记录", color=COLORS["text"], fontsize=9)
  207.         self._ax.tick_params(colors=COLORS["subtext"], labelsize=8)
  208.         for spine in self._ax.spines.values():
  209.             spine.set_edgecolor(COLORS["border"])
  210.         self._ax.legend(
  211.             facecolor=COLORS["panel"], edgecolor=COLORS["border"],
  212.             labelcolor=COLORS["text"], fontsize=8
  213.         )
  214.         self._ax.set_ylim(0, 110)
  215.         self._ax.yaxis.label.set_color(COLORS["text"])
  216.         self._ax.xaxis.label.set_color(COLORS["text"])

  217.     # ── 数据刷新 ──────────────────────────────────────────────────────────

  218.     def _schedule_refresh(self):
  219.         """开始后台采集 + 定时 UI 刷新"""
  220.         self._do_refresh()

  221.     def _do_refresh(self):
  222.         """启动后台采集,采集完毕后更新 UI"""
  223.         def worker():
  224.             self.data.fetch()
  225.             self.after(0, self._update_ui)

  226.         t = threading.Thread(target=worker, daemon=True)
  227.         t.start()

  228.     def _update_ui(self):
  229.         """用最新数据更新所有 UI 控件"""
  230.         with self.data.lock:
  231.             cpu_temps = dict(self.data.cpu_temps)
  232.             gpu_temps = dict(self.data.gpu_temps)
  233.             cpu_info  = dict(self.data.cpu_info)
  234.             gpu_list  = list(self.data.gpu_list)
  235.             ts        = self.data.timestamp
  236.             cpu_hist  = list(self.data.cpu_temp_history)
  237.             gpu_hist  = list(self.data.gpu_temp_history)

  238.         self.lbl_time.config(text=ts)

  239.         self._refresh_cpu_panel(cpu_temps, cpu_info)
  240.         self._refresh_gpu_panel(gpu_temps, gpu_list)
  241.         if HAS_MPL:
  242.             self._refresh_chart(cpu_hist, gpu_hist)

  243.         self.lbl_status.config(text=f"上次更新:{ts}  |  刷新间隔:{REFRESH_MS // 1000}s")

  244.         # 安排下次刷新
  245.         self.after(REFRESH_MS, self._do_refresh)

  246.     def _refresh_cpu_panel(self, cpu_temps: dict, cpu_info: dict):
  247.         """清空并重绘 CPU 面板"""
  248.         for w in self._cpu_panel.winfo_children():
  249.             w.destroy()

  250.         def row(label, value, val_color=COLORS["text"]):
  251.             f = tk.Frame(self._cpu_panel, bg=COLORS["panel"])
  252.             f.pack(fill="x", pady=1)
  253.             tk.Label(f, text=label, bg=COLORS["panel"], fg=COLORS["subtext"],
  254.                      font=("Segoe UI", 9), width=22, anchor="w").pack(side="left")
  255.             tk.Label(f, text=value, bg=COLORS["panel"], fg=val_color,
  256.                      font=("Segoe UI", 9, "bold"), anchor="w").pack(side="left")

  257.         if cpu_info:
  258.             physical = cpu_info.get("cpu_count_physical", "?")
  259.             logical  = cpu_info.get("cpu_count_logical", "?")
  260.             row("核心数", f"{physical} 物理 / {logical} 逻辑")

  261.             if "freq_current" in cpu_info:
  262.                 row("当前频率", f"{cpu_info['freq_current']:.0f} MHz")

  263.             usage = cpu_info.get("usage_total")
  264.             if usage is not None:
  265.                 color = _usage_color(usage)
  266.                 row("CPU 使用率", f"{usage:.1f}%", color)

  267.                 # 各核使用率——按物理核聚合(超线程取两逻辑核中的较大值)
  268.                 per_core  = cpu_info.get("usage_per_core", [])
  269.                 phys_cnt  = cpu_info.get("cpu_count_physical") or 0
  270.                 logi_cnt  = cpu_info.get("cpu_count_logical")  or len(per_core)
  271.                 if per_core:
  272.                     if phys_cnt and logi_cnt == phys_cnt * 2:
  273.                         phys_usage = [
  274.                             max(per_core[i * 2], per_core[i * 2 + 1])
  275.                             for i in range(phys_cnt)
  276.                         ]
  277.                     else:
  278.                         phys_usage = per_core
  279.                     cols = 3
  280.                     for start in range(0, len(phys_usage), cols):
  281.                         chunk = phys_usage[start:start + cols]
  282.                         txt = "  ".join(
  283.                             f"核{start + j + 1}: {v:>5.1f}%" for j, v in enumerate(chunk)
  284.                         )
  285.                         tk.Label(
  286.                             self._cpu_panel, text=txt,
  287.                             bg=COLORS["panel"], fg=COLORS["subtext"],
  288.                             font=("Consolas", 8),
  289.                         ).pack(anchor="w")

  290.             if "mem_used_gb" in cpu_info:
  291.                 mem_color = _usage_color(cpu_info["mem_percent"])
  292.                 row("内存使用",
  293.                     f"{cpu_info['mem_used_gb']:.1f} / {cpu_info['mem_total_gb']:.1f} GB  "
  294.                     f"({cpu_info['mem_percent']:.1f}%)", mem_color)

  295.         # 温度——最高核心温度 + Package 温度
  296.         if cpu_temps:
  297.             tk.Frame(self._cpu_panel, bg=COLORS["border"], height=1).pack(fill="x", pady=4)
  298.             phys = {k: v for k, v in cpu_temps.items() if "Thread" not in k}
  299.             src  = phys if phys else cpu_temps

  300.             # 最高核心温度(排除 Package/Tdie/Tctl 聚合传感器)
  301.             _PKG_KW = ("package", "tdie", "tctl", "average", "max", "distance")
  302.             core_vals = [v for k, v in src.items()
  303.                          if not any(kw in k.lower() for kw in _PKG_KW)]
  304.             peak = max(core_vals) if core_vals else max(src.values())
  305.             row("温度(最高核心)", f"{peak:.1f} °C",
  306.                 _temp_color(peak, CPU_WARN_TEMP, CPU_CRIT_TEMP))

  307.             # Package 温度
  308.             for k, v in src.items():
  309.                 if any(kw in k.lower() for kw in ("package", "tdie", "tctl")):
  310.                     row("温度(Package)", f"{v:.1f} °C",
  311.                         _temp_color(v, CPU_WARN_TEMP, CPU_CRIT_TEMP))
  312.                     break
  313.         else:
  314.             tk.Label(
  315.                 self._cpu_panel,
  316.                 text="温度不可用(需 LibreHardwareMonitor)",
  317.                 bg=COLORS["panel"], fg=COLORS["yellow"],
  318.                 font=("Segoe UI", 9),
  319.             ).pack(anchor="w", pady=4)

  320.     def _refresh_gpu_panel(self, gpu_temps: dict, gpu_list: list):
  321.         """清空并重绘 GPU 面板"""
  322.         for w in self._gpu_panel.winfo_children():
  323.             w.destroy()

  324.         def row(label, value, val_color=COLORS["text"]):
  325.             f = tk.Frame(self._gpu_panel, bg=COLORS["panel"])
  326.             f.pack(fill="x", pady=1)
  327.             tk.Label(f, text=label, bg=COLORS["panel"], fg=COLORS["subtext"],
  328.                      font=("Segoe UI", 9), width=22, anchor="w").pack(side="left")
  329.             tk.Label(f, text=value, bg=COLORS["panel"], fg=val_color,
  330.                      font=("Segoe UI", 9, "bold"), anchor="w").pack(side="left")

  331.         if not gpu_list and not gpu_temps:
  332.             tk.Label(
  333.                 self._gpu_panel,
  334.                 text="未检测到 GPU(NVIDIA 需安装 nvidia-ml-py3)",
  335.                 bg=COLORS["panel"], fg=COLORS["yellow"],
  336.                 font=("Segoe UI", 9),
  337.             ).pack(anchor="w", pady=4)
  338.             return

  339.         for gpu in gpu_list:
  340.             i    = gpu["index"]
  341.             name = gpu["name"]
  342.             tk.Label(
  343.                 self._gpu_panel, text=f"[{i}] {name}",
  344.                 bg=COLORS["panel"], fg=COLORS["mauve"],
  345.                 font=("Segoe UI", 9, "bold"),
  346.             ).pack(anchor="w", pady=(4, 1))

  347.             temp_key = f"GPU{i} {name}"
  348.             temp = gpu_temps.get(temp_key)
  349.             if temp is not None:
  350.                 color = _temp_color(temp, GPU_WARN_TEMP, GPU_CRIT_TEMP)
  351.                 row("  温度", f"{temp:.1f} °C", color)

  352.             if "usage_gpu" in gpu:
  353.                 row("  GPU 使用率", f"{gpu['usage_gpu']:.1f}%",
  354.                     _usage_color(gpu["usage_gpu"]))
  355.             if "mem_used_gb" in gpu:
  356.                 row("  显存使用",
  357.                     f"{gpu['mem_used_gb']:.1f} / {gpu['mem_total_gb']:.1f} GB  "
  358.                     f"({gpu['mem_percent']:.1f}%)",
  359.                     _usage_color(gpu["mem_percent"]))
  360.             if "power_w" in gpu:
  361.                 row("  功耗", f"{gpu['power_w']:.1f} W / {gpu.get('power_limit_w', '?'):.1f} W")
  362.             if "fan_percent" in gpu:
  363.                 row("  风扇转速", f"{gpu['fan_percent']}%")
  364.             if "clock_gpu_mhz" in gpu:
  365.                 row("  核心频率", f"{gpu['clock_gpu_mhz']} MHz")

  366.         # 其他 GPU(WMI)
  367.         known = {f"GPU{g['index']} {g['name']}" for g in gpu_list}
  368.         for name, temp in gpu_temps.items():
  369.             if name not in known:
  370.                 color = _temp_color(temp, GPU_WARN_TEMP, GPU_CRIT_TEMP)
  371.                 row(f"  {_shorten(name, 22)}", f"{temp:.1f} °C", color)

  372.     def _refresh_chart(self, cpu_hist: list, gpu_hist: list):
  373.         """更新 matplotlib 折线图"""
  374.         xs = list(range(HISTORY_LEN))

  375.         cpu_ys = [v if v is not None else float("nan") for v in cpu_hist]
  376.         gpu_ys = [v if v is not None else float("nan") for v in gpu_hist]

  377.         self._line_cpu.set_data(xs, cpu_ys)
  378.         self._line_gpu.set_data(xs, gpu_ys)

  379.         all_vals = [v for v in cpu_ys + gpu_ys if v == v]  # filter nan
  380.         if all_vals:
  381.             y_min = max(0, min(all_vals) - 10)
  382.             y_max = min(120, max(all_vals) + 15)
  383.             self._ax.set_ylim(y_min, y_max)

  384.         self._ax.set_xlim(0, HISTORY_LEN - 1)
  385.         self._canvas.draw_idle()


  386. # ════════════════════════════════════════════════════════════════════════════
  387. # 辅助函数
  388. # ════════════════════════════════════════════════════════════════════════════

  389. def _temp_color(temp: float, warn: float, crit: float) -> str:
  390.     if temp >= crit:
  391.         return COLORS["red"]
  392.     elif temp >= warn:
  393.         return COLORS["yellow"]
  394.     return COLORS["green"]


  395. def _usage_color(pct: float) -> str:
  396.     if pct >= 90:
  397.         return COLORS["red"]
  398.     elif pct >= 70:
  399.         return COLORS["yellow"]
  400.     return COLORS["green"]


  401. def _shorten(text: str, max_len: int) -> str:
  402.     return text if len(text) <= max_len else text[:max_len - 1] + "…"


  403. # ════════════════════════════════════════════════════════════════════════════
  404. # 入口
  405. # ════════════════════════════════════════════════════════════════════════════

  406. def launch_gui():
  407.     app = MonitorApp()
  408.     app.mainloop()


  409. if __name__ == "__main__":
  410.     launch_gui()
复制代码
屏幕截图 2026-03-15 102518.png

评分

参与人数 1荣誉 +6 鱼币 +6 贡献 +6 C币 +6 收起 理由
不二如是 + 6 + 6 + 6 + 6 鱼C有你更精彩^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 4 小时前 | 显示全部楼层
鱼油,您好!很高兴得知您测试的硬件监控软件达到预期要求。硬件监控软件通常用于实时监测计算机硬件的状态,如CPU、内存、磁盘和温度等,这对于系统维护和性能优化非常有用。

如果您想了解如何编写这样的软件,这里是一个简单的Python示例,使用psutil库来监控CPU和内存使用率:


  1. import psutil

  2. # 获取CPU使用率
  3. cpu_usage = psutil.cpu_percent(interval=1)
  4. print(f"当前CPU使用率: {cpu_usage}%")

  5. # 获取内存信息
  6. memory = psutil.virtual_memory()
  7. print(f"内存总量: {memory.total / (1024**3):.2f} GB")
  8. print(f"内存使用率: {memory.percent}%")
复制代码


要运行此代码,您需要先安装psutil库,可以使用pip install psutil命令。

继续加油,如果您在开发或测试中遇到具体问题,欢迎随时提问交流!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 3 小时前 | 显示全部楼层
看不懂但大为震撼
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 3 小时前 | 显示全部楼层
项目超级厉害!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2026-3-15 14:49

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表