import sys
import os
import json
import time
from datetime import datetime
from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLabel, QStackedWidget)
from PyQt6.QtCore import Qt, QSize, QTimer
from PyQt6.QtGui import QIcon, QFont
from pages.status_page import StatusPage
from pages.control_page import ControlPage
from pages.setting_page import SettingPage
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
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# 初始化 API 客户端
self.config_parser = ConfigParse()
self.config_parser.config_changed.connect(self._on_config_changed)
self.api_client = AIOPrrintSystemAPI(
api_url=self.config_parser.api_url,
api_key=self.config_parser.api_key
)
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._fan_timer = QTimer(self)
self._fan_timer.timeout.connect(self._update_top_bar)
self._fan_timer.start(1000)
def _check_network(self):
"""检查网络连接状态(每30秒检测一次,避免频繁调用)"""
now = time.time()
if now - self._last_network_check < 30:
return self._is_network_connected
self._last_network_check = now
try:
status = self.wifi_manager.get_current_status()
self._is_network_connected = status.get("wpa_state") == "COMPLETED"
except Exception:
self._is_network_connected = False
return self._is_network_connected
def _on_config_changed(self, config_instance):
"""配置变化时更新 API 客户端等相关设置"""
self.api_client = AIOPrrintSystemAPI(
api_url=self.config_parser.api_url,
api_key=self.config_parser.api_key
)
def _signal_to_bars(self, signal_val):
"""将信号强度转换为条形图标字符串
支持 dBm(负值,如 -45)和百分比(0-100,nmcli 格式)
"""
if signal_val is None:
return "⬜⬜⬜"
if signal_val < 0:
# dBm 格式
if signal_val >= -50:
return "■■■"
elif signal_val >= -60:
return "■■□"
elif signal_val >= -70:
return "■□□"
else:
return "□□□"
else:
# 百分比格式 (0-100)
if signal_val >= 75:
return "■■■"
elif signal_val >= 50:
return "■■□"
elif signal_val >= 25:
return "■□□"
else:
return "□□□"
def _update_top_bar(self):
"""更新风扇/网络状态横条显示"""
s = self.auto_fan_status
temp = f"{s.cpu_temp:.1f}°C" if s.is_auto_fan_service_running else "--.-°C"
speed_pct = min(s.fan_speed / 255 * 100, 100)
speed = f"{speed_pct:.1f}%" if s.is_auto_fan_service_running else "--.-%"
state = s.fan_state if s.is_auto_fan_service_running else "Auto Fan service is not running"
rpm = f"{s.fan_rpm:.1f} r/min" if s.is_auto_fan_service_running else "--.-- r/min"
# 根据运行状态改变颜色
if s.is_auto_fan_service_running:
if s.fan_state == "Stalled":
color = "#e86c60" # 风扇异常-红色
else:
color = "#a0d8a0" # 正常-浅绿色
else:
color = "#e8a060" # 异常-橙色
self._top_bar.setStyleSheet(
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")
cpu_load_str = f"{s.cpu_load:.2f}/4.0"
self._fan_label.setText(
f"🌡 {temp} {state} 𖣘 {speed} {rpm} 🖥 {cpu_load_str}"
)
self._fan_label.setTextFormat(Qt.TextFormat.RichText)
# --- WiFi 状态指示 ---
is_connected = self._check_network()
if is_connected:
try:
status = self.wifi_manager.get_current_status()
raw_signal = status.get("signal_level")
signal_dbm = int(raw_signal) if raw_signal else None
except Exception:
signal_dbm = None
bars = self._signal_to_bars(signal_dbm)
self._wifi_label.setText(f"Signal: {bars}")
self._wifi_label.setStyleSheet("color: #a0d8a0; font-size: 18px; font-weight: 600;")
else:
self._wifi_label.setText("No Signal")
self._wifi_label.setStyleSheet("color: #e86c60; font-size: 18px; font-weight: 600;")
# --- 时钟(有网络时更新;断网后保留最后一次的时间) ---
if is_connected:
now = datetime.now()
self._clock_label.setText(now.strftime("%H:%M:%S"))
if not self._clock_has_synced:
self._clock_has_synced = True
self._clock_label.show()
else:
# 从未同步过则隐藏,否则保留上次时间
if self._clock_has_synced:
self._clock_label.show()
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)
# 风扇状态横条
self._top_bar = QWidget()
self._top_bar.setFixedHeight(36)
self._top_bar.setStyleSheet(
"background-color: #2a2a2a; color: #a0d8a0; "
"font-size: 18px; font-weight: 600; padding: 4px 16px;"
)
top_layout = QHBoxLayout(self._top_bar)
top_layout.setContentsMargins(16, 0, 16, 0)
self._fan_label = QLabel("🌡 --°C -- 𖣘 --% -- r/min")
self._fan_label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
top_layout.addWidget(self._fan_label)
top_layout.addStretch()
# WiFi 状态指示
self._wifi_label = QLabel("📶 --")
self._wifi_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self._wifi_label.setStyleSheet("color: #a0d8a0; font-size: 18px; font-weight: 600;")
top_layout.addWidget(self._wifi_label)
# 时钟标签(有网络时显示,获取过一次后断网也不隐藏)
self._clock_label = QLabel("--:--:--")
self._clock_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self._clock_label.setStyleSheet("color: #a0d8a0; font-size: 18px; font-weight: 600;")
self._clock_label.hide()
top_layout.addWidget(self._clock_label)
# 底部按钮区
bottom_layout = QHBoxLayout()
bottom_layout.setContentsMargins(10, 10, 10, 10)
bottom_layout.setSpacing(20)
# 定义底部区域背景
bottom_widget = QWidget()
bottom_widget.setStyleSheet("background-color: #333333;")
bottom_widget.setLayout(bottom_layout)
bottom_widget.setFixedHeight(120) # 为触摸优化的按钮高度
# 创建触摸友好的按钮
self.btn_status = self.create_nav_button("状态", self.switchToStatus)
self.btn_control = self.create_nav_button("控制", self.switchToControl)
self.btn_settings = self.create_nav_button("设置", self.switchToSettings)
bottom_layout.addWidget(self.btn_status)
bottom_layout.addWidget(self.btn_control)
bottom_layout.addWidget(self.btn_settings)
# 将主显示区、风扇横条、底部按钮加入整体布局
main_layout.addWidget(self._top_bar)
main_layout.addWidget(self.stacked_widget)
main_layout.addWidget(bottom_widget)
self.setLayout(main_layout)
self.setStyleSheet("background-color: #666666;") # 整体灰色背景
def create_nav_button(self, text, callback):
# 如果有图标,可以使用 btn.setIcon(QIcon("path/to/icon.png"))
btn = QPushButton(text)
# 为触摸屏优化:大字体,增加内边距
btn.setStyleSheet("""
QPushButton {
background-color: #444444;
color: white;
border: 2px solid #555555;
border-radius: 10px;
font-size: 32px;
padding: 10px;
}
QPushButton:pressed {
background-color: #222222;
border: 2px solid #4CAF50;
}
""")
btn.setSizePolicy(btn.sizePolicy().Policy.Expanding, btn.sizePolicy().Policy.Expanding)
btn.clicked.connect(callback)
return btn
def switchToStatus(self):
self.stacked_widget.setCurrentIndex(0)
def switchToControl(self):
self.stacked_widget.setCurrentIndex(1)
def switchToSettings(self):
self.stacked_widget.setCurrentIndex(2)
def main():
app = QApplication(sys.argv)
# 隐藏鼠标光标,针对触摸屏优化
app.setOverrideCursor(Qt.CursorShape.BlankCursor)
window = MainWindow()
# 全屏无边框显示
window.showFullScreen()
sys.exit(app.exec())
if __name__ == "__main__":
main()