图标升级

This commit is contained in:
2026-05-16 00:44:37 +08:00
parent 837996c436
commit d80e8dd05d
2113 changed files with 14850 additions and 244328 deletions

320
main.py
View File

@@ -1,13 +1,15 @@
import sys
import base64
import os
import json
import time
import re
from datetime import datetime
from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLabel, QStackedWidget)
QPushButton, QLabel, QStackedWidget, QProgressBar)
from PyQt6.QtCore import Qt, QSize, QTimer
from PyQt6.QtGui import QIcon, QFont
from PyQt6.QtGui import QIcon, QFont, QPixmap
from pages.status_page import StatusPage
from pages.control_page import ControlPage
from pages.setting_page import SettingPage
@@ -15,29 +17,243 @@ from utils.aio_print_api import AIOPrrintSystemAPI
from utils.auto_fan_status import AutoFanStatus
from utils.config_parse import ConfigParse
from utils.wifi_manager import WifiManager
from utils.gcode_viewer import GCodeViewerWidget
from utils.get_bootstrap_icon import get_colored_svg_uri
class SplashWidget(QWidget):
"""开屏启动动画,显示 Logo 与初始化进度"""
def __init__(self, fix_width=None, fix_height=None, parent=None):
self.gcode_viewer = parent.gcode_viewer
super().__init__(parent)
self.setStyleSheet("background-color: #222222;")
self.fix_width = fix_width
self.fix_height = fix_height
if self.fix_width is not None and self.fix_height is not None:
self.setFixedSize(self.fix_width,self.fix_height)
# 主布局
layout = QVBoxLayout(self)
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.setContentsMargins(40,40,40,40)
layout.setSpacing(20)
# ---- Logo ----
self.logo_label = QLabel()
script_dir = os.path.dirname(os.path.abspath(__file__))
logo_path = os.path.join(script_dir, "assets", "img", "logo.jpg")
if os.path.isfile(logo_path):
pixmap = QPixmap(logo_path)
scaled = pixmap.scaled(400, 400,
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation)
self.logo_label.setPixmap(scaled)
else:
self.logo_label.setText("🖨️")
self.logo_label.setStyleSheet("font-size: 120px; color: #4CAF50;")
self.logo_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.logo_label)
# ---- 标题 ----
self.title_label = QLabel("Printer Screen Menu")
self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.title_label.setStyleSheet(
"color: #e0e0e0; font-size: 36px; font-weight: 700;"
"padding: 10px 0 5px 0;"
)
layout.addWidget(self.title_label)
# ---- 副标题 / 版本 ----
self.subtitle_label = QLabel("正在启动 ...")
self.subtitle_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.subtitle_label.setStyleSheet(
"color: #888888; font-size: 18px; font-weight: 400;"
)
layout.addWidget(self.subtitle_label)
# ---- 进度条 ----
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 0) # 不确定模式(脉冲动画)
self.progress_bar.setFixedHeight(6)
self.progress_bar.setFixedWidth(500)
self.progress_bar.setTextVisible(False)
self.progress_bar.setStyleSheet("""
QProgressBar {
background-color: #333355;
border: none;
border-radius: 3px;
}
QProgressBar::chunk {
background-color: #4CAF50;
border-radius: 3px;
}
""")
bar_container = QHBoxLayout()
bar_container.addStretch()
bar_container.addWidget(self.progress_bar)
bar_container.addStretch()
layout.addLayout(bar_container)
# ---- 进度文字 ----
self.progress_label = QLabel("正在加载配置...")
self.progress_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.progress_label.setStyleSheet(
"color: #a0d8a0; font-size: 22px; font-weight: 500; padding: 10px;"
)
layout.addWidget(self.progress_label)
layout.addWidget(self.gcode_viewer)
def update_progress(self, text: str):
"""更新进度文字并立即刷新界面"""
self.progress_label.setText(text)
if text.find("界面") != -1:
self.subtitle_label.setText("请稍候 (界面加载时间较长) ...")
else:
self.subtitle_label.setText("请稍候 ...")
QApplication.processEvents()
class TopStacked(QWidget):
def __init__(self, fix_width=None, fix_height=None):
super().__init__()
self.root_layout = QVBoxLayout(self)
self.root_layout.setContentsMargins(0, 0, 0, 0)
self.root_layout.setSpacing(0)
self.fix_width = fix_width
self.fix_height = fix_height
if self.fix_width is not None and self.fix_height is not None:
self.setFixedSize(self.fix_width,self.fix_height)
self.root_stack = QStackedWidget()
self.root_stack.setStyleSheet("background-color: #222222;") # 深灰色主显示区
# !Important: 先初始化带OpenGL的东西防止 Could not queue DRM page flip on screen HDMI1 (Device or resource busy)
self.gcode_viewer = GCodeViewerWidget()
self.gcode_viewer.setUpdatesEnabled(False)
self.gcode_viewer.hide()
# 1. 模拟的启动画面
self.splash_widget = SplashWidget(fix_width=self.fix_width, fix_height=self.fix_height, parent=self)
self.root_stack.addWidget(self.splash_widget)
# 2. 真实主页面容器
self.main_content = MainWindow(fix_width=self.fix_width, fix_height=self.fix_height, parent=self)
self.root_stack.addWidget(self.main_content)
self.root_layout.addWidget(self.root_stack)
# self.root_layout.addWidget(self.root_container)
# 初始显示启动界面
# self.root_stack.setCurrentWidget(self.splash_widget)
self.root_stack.setCurrentIndex(0)
QTimer.singleShot(100, self.main_content.start_init)
# self.main_content.start_init()
# self.setLayout(self.root_layout)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# 初始化 API 客户端
def __init__(self, fix_width=None, fix_height=None, parent=None):
super().__init__(parent)
self.fix_width = fix_width
self.fix_height = fix_height
if self.fix_width is not None and self.fix_height is not None:
self.setFixedSize(self.fix_width,self.fix_height)
self.splash_widget = parent.splash_widget
self.root_stack = parent.root_stack
self.gcode_viewer = parent.gcode_viewer
self._last_network_check = 0.0
self._is_network_connected = False
self._clock_has_synced = False
def start_init(self):
"""真正的初始化逻辑,使用定时器链式调用,避免卡死主线程"""
self.splash_widget.update_progress("正在加载配置...")
QTimer.singleShot(100, self._init_step1)
def _init_step1(self):
self.config_parser = ConfigParse()
self.config_parser.config_changed.connect(self._on_config_changed)
self.splash_widget.update_progress("正在初始化 API 客户端...")
QTimer.singleShot(600, self._init_step2)
def _init_step2(self):
self.api_client = AIOPrrintSystemAPI(
api_url=self.config_parser.api_url,
api_key=self.config_parser.api_key
)
self.splash_widget.update_progress("正在检测散热风扇...")
QTimer.singleShot(600, self._init_step3)
def _init_step3(self):
self.auto_fan_status = AutoFanStatus()
self.wifi_manager = WifiManager()
self._last_network_check = 0.0
self._is_network_connected = False
self._clock_has_synced = False # 是否曾成功获取到时间(断网后继续显示)
self.init_ui()
self.splash_widget.update_progress("正在检查网络连接...")
QTimer.singleShot(600, self._init_step4)
def _init_step4(self):
self.wifi_manager = WifiManager()
self.splash_widget.update_progress("正在初始化主框架...")
QTimer.singleShot(100, self._init_step5)
def _init_step5(self):
# 整体布局附加到 main_content 占位符
self.main_layout = QVBoxLayout(self)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.setSpacing(0)
# 顶部主显示区 (使用 QStackedWidget 切换不同页面)
self.stacked_widget = QStackedWidget()
self.stacked_widget.setStyleSheet("background-color: #555555;") # 深灰色主显示区
self.splash_widget.update_progress("正在加载状态界面...")
QTimer.singleShot(100, self._init_step5_status)
def _init_step5_status(self):
self.page_status = StatusPage(self.api_client, GcodeViewer=self.gcode_viewer)
self.stacked_widget.addWidget(self.page_status)
self.splash_widget.update_progress("正在加载控制界面...")
QTimer.singleShot(100, self._init_step5_control)
def _init_step5_control(self):
self.page_control = ControlPage(self.api_client)
self.stacked_widget.addWidget(self.page_control)
self.splash_widget.update_progress("正在加载设置界面...")
QTimer.singleShot(100, self._init_step5_settings)
def _init_step5_settings(self):
self.page_settings = SettingPage(self.api_client)
self.stacked_widget.addWidget(self.page_settings)
self.splash_widget.update_progress("正在组合界面...")
QTimer.singleShot(100, self._init_step5_finish)
def _init_step5_finish(self):
self.build_ui_bars()
# 定时刷新风扇状态显示
self._fan_timer = QTimer(self)
self._fan_timer.timeout.connect(self._update_top_bar)
self._fan_timer.start(1000)
self._update_top_bar() # 立即刷新横条数据
self.splash_widget.update_progress("启动完成!")
QTimer.singleShot(600, self._init_step6)
def _init_step6(self):
# 切换到真实主界面
self.root_stack.setCurrentIndex(1)
self.gcode_viewer.setUpdatesEnabled(True)
self.gcode_viewer.show()
def _check_network(self):
"""检查网络连接状态每30秒检测一次避免频繁调用"""
@@ -63,28 +279,33 @@ class MainWindow(QWidget):
"""将信号强度转换为条形图标字符串
支持 dBm负值如 -45和百分比0-100nmcli 格式)
"""
icon_name = "reception-0.svg"
if signal_val is None:
return "⬜⬜⬜"
if signal_val < 0:
icon_name = "reception-0.svg"
elif signal_val < 0:
# dBm 格式
if signal_val >= -50:
return "■■■"
icon_name = "reception-4.svg"
elif signal_val >= -60:
return "■■□"
icon_name = "reception-2.svg"
elif signal_val >= -70:
return "■□□"
icon_name = "reception-1.svg"
else:
return "□□□"
icon_name = "reception-0.svg"
else:
# 百分比格式 (0-100)
if signal_val >= 75:
return "■■■"
icon_name = "reception-4.svg"
elif signal_val >= 50:
return "■■□"
icon_name = "reception-2.svg"
elif signal_val >= 25:
return "■□□"
icon_name = "reception-1.svg"
else:
return "□□□"
icon_name = "reception-0.svg"
# 为了能够在深色主题正确显示颜色,通过图片标签引入
colored_uri = get_colored_svg_uri(icon_name, "#a0d8a0", 40, 20, "0 -4 18 18")
return f"<img src='{colored_uri}'>"
def _update_top_bar(self):
"""更新风扇/网络状态横条显示"""
@@ -107,10 +328,17 @@ class MainWindow(QWidget):
f"background-color: #2a2a2a; color: {color}; "
f"font-size: 18px; font-weight: 600; padding: 4px 16px;"
)
load_color = "#a0d8a0" if s.cpu_load < 1.0 else ("#e8a060" if s.cpu_load < 2.0 else "#e86c60")
load_color = "#a0d8a0" if s.cpu_load < 1.0 else ("#e8a060" if s.cpu_load < 3.0 else "#e86c60")
cpu_load_str = f"<span style='color:{load_color}'>{s.cpu_load:.2f}</span><span>/4.0</span>"
icon_temp = get_colored_svg_uri("thermometer-half.svg", color, 20, 20, "-4 -2 18 18")
icon_fan = get_colored_svg_uri("fan.svg", color, 20, 20, "-2 -2 18 18")
icon_cpu = get_colored_svg_uri("cpu.svg", load_color, 20, 20, "-2 -2 18 18")
self._fan_label.setText(
f"🌡 {temp} {state} 𖣘 {speed} {rpm} 🖥 {cpu_load_str}"
f"<img src='{icon_temp}'> {temp} &nbsp; {state} &nbsp; "
f"<img src='{icon_fan}'> {speed} &nbsp; {rpm} &nbsp; "
f"<img src='{icon_cpu}'> {cpu_load_str}"
)
self._fan_label.setTextFormat(Qt.TextFormat.RichText)
@@ -125,6 +353,7 @@ class MainWindow(QWidget):
signal_dbm = None
bars = self._signal_to_bars(signal_dbm)
self._wifi_label.setText(f"Signal: {bars}")
self._wifi_label.setTextFormat(Qt.TextFormat.RichText)
self._wifi_label.setStyleSheet("color: #a0d8a0; font-size: 18px; font-weight: 600;")
else:
self._wifi_label.setText("No Signal")
@@ -144,25 +373,7 @@ class MainWindow(QWidget):
else:
self._clock_label.hide()
def init_ui(self):
# 整体布局
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.setSpacing(0)
# 顶部主显示区 (使用 QStackedWidget 切换不同页面)
self.stacked_widget = QStackedWidget()
self.stacked_widget.setStyleSheet("background-color: #555555;") # 深灰色主显示区
# 添加测试页面
self.page_status = StatusPage(self.api_client)
self.page_control = ControlPage(self.api_client)
self.page_settings = SettingPage(self.api_client)
self.stacked_widget.addWidget(self.page_status)
self.stacked_widget.addWidget(self.page_control)
self.stacked_widget.addWidget(self.page_settings)
def build_ui_bars(self):
# 风扇状态横条
self._top_bar = QWidget()
self._top_bar.setFixedHeight(36)
@@ -178,8 +389,10 @@ class MainWindow(QWidget):
top_layout.addStretch()
# WiFi 状态指示
self._wifi_label = QLabel("📶 --")
init_wifi_uri = get_colored_svg_uri('reception-0.svg', '#a0d8a0', 40, 20, "0 -4 18 18")
self._wifi_label = QLabel(f"<img src='{init_wifi_uri}'> --")
self._wifi_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self._wifi_label.setTextFormat(Qt.TextFormat.RichText)
self._wifi_label.setStyleSheet("color: #a0d8a0; font-size: 18px; font-weight: 600;")
top_layout.addWidget(self._wifi_label)
@@ -211,11 +424,10 @@ class MainWindow(QWidget):
bottom_layout.addWidget(self.btn_settings)
# 将主显示区、风扇横条、底部按钮加入整体布局
main_layout.addWidget(self._top_bar)
main_layout.addWidget(self.stacked_widget)
main_layout.addWidget(bottom_widget)
self.main_layout.addWidget(self._top_bar)
self.main_layout.addWidget(self.stacked_widget)
self.main_layout.addWidget(bottom_widget)
self.setLayout(main_layout)
self.setStyleSheet("background-color: #666666;") # 整体灰色背景
def create_nav_button(self, text, callback):
@@ -255,9 +467,19 @@ def main():
# 隐藏鼠标光标,针对触摸屏优化
app.setOverrideCursor(Qt.CursorShape.BlankCursor)
window = MainWindow()
# # 唯一且纯粹的顶级窗口
# window = MainWindow()
# window.showFullScreen()
# 全屏无边框显示
# # 待窗口在底层完全拿到 Plane 并开始渲染后,触发后续耗时初始化
# QTimer.singleShot(100, window.start_init)
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
screen = QApplication.primaryScreen()
fix_width = screen.size().width()
fix_height = screen.size().height()
window = TopStacked(fix_width,fix_height)
window.showFullScreen()
sys.exit(app.exec())