图标升级
This commit is contained in:
320
main.py
320
main.py
@@ -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-100,nmcli 格式)
|
||||
"""
|
||||
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} {state} "
|
||||
f"<img src='{icon_fan}'> {speed} {rpm} "
|
||||
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())
|
||||
|
||||
Reference in New Issue
Block a user