优化wifi设置页面,添加悬浮键盘
This commit is contained in:
14
main.py
14
main.py
@@ -1,12 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# Fix QtWebEngine initialization by importing it before QApplication is created
|
|
||||||
import os
|
|
||||||
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--no-sandbox --disable-gpu --disable-gpu-compositing --disable-dev-shm-usage"
|
|
||||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
|
||||||
|
|
||||||
from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
QPushButton, QLabel, QStackedWidget)
|
QPushButton, QLabel, QStackedWidget)
|
||||||
from PyQt6.QtCore import Qt, QSize
|
from PyQt6.QtCore import Qt, QSize
|
||||||
@@ -14,6 +8,7 @@ from PyQt6.QtGui import QIcon, QFont
|
|||||||
|
|
||||||
from pages.status_page import StatusPage
|
from pages.status_page import StatusPage
|
||||||
from pages.control_page import ControlPage
|
from pages.control_page import ControlPage
|
||||||
|
from pages.setting_page import SettingPage
|
||||||
from utils.aio_print_api import AIOPrrintSystemAPI
|
from utils.aio_print_api import AIOPrrintSystemAPI
|
||||||
|
|
||||||
def load_config():
|
def load_config():
|
||||||
@@ -49,14 +44,11 @@ class MainWindow(QWidget):
|
|||||||
# 添加测试页面
|
# 添加测试页面
|
||||||
self.page_status = StatusPage(self.api_client)
|
self.page_status = StatusPage(self.api_client)
|
||||||
self.page_control = ControlPage(self.api_client)
|
self.page_control = ControlPage(self.api_client)
|
||||||
self.page_settings = QLabel("系统设置")
|
self.page_settings = SettingPage(self.api_client)
|
||||||
|
|
||||||
self.stacked_widget.addWidget(self.page_status)
|
self.stacked_widget.addWidget(self.page_status)
|
||||||
self.stacked_widget.addWidget(self.page_control)
|
self.stacked_widget.addWidget(self.page_control)
|
||||||
for page in [self.page_settings]:
|
self.stacked_widget.addWidget(self.page_settings)
|
||||||
page.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
||||||
page.setStyleSheet("color: white; font-size: 48px; font-weight: bold;")
|
|
||||||
self.stacked_widget.addWidget(page)
|
|
||||||
|
|
||||||
# 底部按钮区
|
# 底部按钮区
|
||||||
bottom_layout = QHBoxLayout()
|
bottom_layout = QHBoxLayout()
|
||||||
|
|||||||
895
pages/setting_page.py
Normal file
895
pages/setting_page.py
Normal file
@@ -0,0 +1,895 @@
|
|||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget,
|
||||||
|
QVBoxLayout,
|
||||||
|
QLabel,
|
||||||
|
QHBoxLayout,
|
||||||
|
QListWidget,
|
||||||
|
QStackedWidget,
|
||||||
|
QPushButton,
|
||||||
|
QLineEdit,
|
||||||
|
QMessageBox,
|
||||||
|
QFormLayout,
|
||||||
|
QComboBox,
|
||||||
|
QListWidgetItem,
|
||||||
|
QFrame,
|
||||||
|
QScrollArea,
|
||||||
|
QAbstractItemView,
|
||||||
|
)
|
||||||
|
from PyQt6.QtCore import Qt, QEvent, QObject, QThread, QTimer, pyqtSignal
|
||||||
|
from PyQt6.QtGui import QMouseEvent
|
||||||
|
import codecs
|
||||||
|
from utils.wifi_manager import WifiManager
|
||||||
|
from utils.floating_keyboard import FloatingKeyboard
|
||||||
|
|
||||||
|
|
||||||
|
class DragScrollArea(QScrollArea):
|
||||||
|
"""支持鼠标/触屏点击拖拽滑动的滚动区域"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._press_pos = None
|
||||||
|
self._start_value = 0
|
||||||
|
self._dragging = False
|
||||||
|
|
||||||
|
def viewportEvent(self, event):
|
||||||
|
etype = event.type()
|
||||||
|
|
||||||
|
if etype == QEvent.Type.MouseButtonPress:
|
||||||
|
if event.button() == Qt.MouseButton.LeftButton:
|
||||||
|
self._press_pos = event.position().toPoint()
|
||||||
|
self._start_value = self.verticalScrollBar().value()
|
||||||
|
self._dragging = False
|
||||||
|
|
||||||
|
elif etype == QEvent.Type.MouseMove:
|
||||||
|
if self._press_pos and event.buttons() & Qt.MouseButton.LeftButton:
|
||||||
|
pos = event.position().toPoint()
|
||||||
|
delta = pos - self._press_pos
|
||||||
|
# 移动超过15px阈值才触发拖拽,避免误触
|
||||||
|
if self._dragging or delta.manhattanLength() > 15:
|
||||||
|
self._dragging = True
|
||||||
|
new_val = self._start_value - delta.y()
|
||||||
|
# 限制在有效范围内
|
||||||
|
self.verticalScrollBar().setValue(
|
||||||
|
max(0, min(new_val, self.verticalScrollBar().maximum()))
|
||||||
|
)
|
||||||
|
return True # 拖拽中拦截事件,不传递给子控件
|
||||||
|
|
||||||
|
elif etype == QEvent.Type.MouseButtonRelease:
|
||||||
|
if event.button() == Qt.MouseButton.LeftButton and self._press_pos is not None:
|
||||||
|
was_dragging = self._dragging
|
||||||
|
self._press_pos = None
|
||||||
|
self._dragging = False
|
||||||
|
if was_dragging:
|
||||||
|
return True # 拖拽结束,拦截释放事件,避免触发子控件点击
|
||||||
|
|
||||||
|
return super().viewportEvent(event)
|
||||||
|
|
||||||
|
|
||||||
|
class DraggableListWidget(QListWidget):
|
||||||
|
"""支持鼠标/触屏点击拖拽滑动的列表控件"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||||
|
self._press_pos = None
|
||||||
|
self._start_value = 0
|
||||||
|
self._dragging = False
|
||||||
|
|
||||||
|
def viewportEvent(self, event):
|
||||||
|
etype = event.type()
|
||||||
|
|
||||||
|
if etype == QEvent.Type.MouseButtonPress:
|
||||||
|
if event.button() == Qt.MouseButton.LeftButton:
|
||||||
|
self._press_pos = event.position().toPoint()
|
||||||
|
self._start_value = self.verticalScrollBar().value()
|
||||||
|
self._dragging = False
|
||||||
|
|
||||||
|
elif etype == QEvent.Type.MouseMove:
|
||||||
|
if self._press_pos and event.buttons() & Qt.MouseButton.LeftButton:
|
||||||
|
pos = event.position().toPoint()
|
||||||
|
delta = pos - self._press_pos
|
||||||
|
# 移动超过15px阈值才触发拖拽,避免误触
|
||||||
|
if self._dragging or delta.manhattanLength() > 15:
|
||||||
|
self._dragging = True
|
||||||
|
new_val = self._start_value - int(delta.y())
|
||||||
|
# 限制在有效范围内
|
||||||
|
self.verticalScrollBar().setValue(
|
||||||
|
max(0, min(new_val, self.verticalScrollBar().maximum()))
|
||||||
|
)
|
||||||
|
return True # 拖拽中拦截事件,不传递给子控件
|
||||||
|
|
||||||
|
elif etype == QEvent.Type.MouseButtonRelease:
|
||||||
|
if event.button() == Qt.MouseButton.LeftButton and self._press_pos is not None:
|
||||||
|
was_dragging = self._dragging
|
||||||
|
self._press_pos = None
|
||||||
|
self._dragging = False
|
||||||
|
if was_dragging:
|
||||||
|
return True # 拖拽结束,拦截释放事件,避免触发子控件点击
|
||||||
|
|
||||||
|
return super().viewportEvent(event)
|
||||||
|
|
||||||
|
|
||||||
|
class WifiScanWorker(QObject):
|
||||||
|
"""在后台线程中执行WiFi扫描,避免阻塞UI"""
|
||||||
|
scan_finished = pyqtSignal(list)
|
||||||
|
scan_error = pyqtSignal(str)
|
||||||
|
|
||||||
|
def __init__(self, wifi_manager):
|
||||||
|
super().__init__()
|
||||||
|
self.wifi_manager = wifi_manager
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
networks = self.wifi_manager.scan_networks()
|
||||||
|
self.scan_finished.emit(networks)
|
||||||
|
except Exception as e:
|
||||||
|
self.scan_error.emit(str(e))
|
||||||
|
|
||||||
|
|
||||||
|
class SettingPage(QWidget):
|
||||||
|
def __init__(self, api_client, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.api_client = api_client
|
||||||
|
self.wifi_manager = WifiManager()
|
||||||
|
self._saved_networks_cache = [] # 缓存上次的已保存网络列表
|
||||||
|
# 让页面自身能接收焦点,用于点击扫描按钮后转移焦点防止跳动
|
||||||
|
self.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
|
||||||
|
|
||||||
|
# 创建悬浮键盘
|
||||||
|
self._keyboard = FloatingKeyboard()
|
||||||
|
self._keyboard_attached = False
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
self._start_status_timer()
|
||||||
|
|
||||||
|
def _attach_keyboard(self, widget):
|
||||||
|
"""将悬浮键盘绑定到指定输入框并显示"""
|
||||||
|
self._keyboard.attach(widget)
|
||||||
|
self._keyboard.show_below(widget)
|
||||||
|
self._keyboard_attached = True
|
||||||
|
|
||||||
|
def _dismiss_keyboard(self):
|
||||||
|
"""关闭悬浮键盘"""
|
||||||
|
self._keyboard.hide()
|
||||||
|
self._keyboard.detach()
|
||||||
|
self._keyboard_attached = False
|
||||||
|
|
||||||
|
def _on_input_focus_in(self, widget):
|
||||||
|
"""输入框获得焦点时的处理"""
|
||||||
|
self._attach_keyboard(widget)
|
||||||
|
|
||||||
|
def eventFilter(self, obj, event):
|
||||||
|
if event.type() == QEvent.Type.FocusIn:
|
||||||
|
if obj in (self.ssid_input, self.identity_input, self.password_input):
|
||||||
|
self._attach_keyboard(obj)
|
||||||
|
# 如果不是输入框获得焦点且键盘正显示,则关闭键盘
|
||||||
|
elif self._keyboard_attached and not isinstance(obj, (QLineEdit, QComboBox, QPushButton)):
|
||||||
|
self._dismiss_keyboard()
|
||||||
|
elif event.type() == QEvent.Type.FocusOut:
|
||||||
|
# 延迟检查:如果焦点真的离开了所有输入框,就关闭键盘
|
||||||
|
if obj in (self.ssid_input, self.identity_input, self.password_input):
|
||||||
|
QTimer.singleShot(100, self._check_dismiss_keyboard)
|
||||||
|
return super().eventFilter(obj, event)
|
||||||
|
|
||||||
|
def _check_dismiss_keyboard(self):
|
||||||
|
"""检查当前焦点是否还在任何输入控件上,不在则关闭键盘"""
|
||||||
|
w = self.focusWidget()
|
||||||
|
if w not in (self.ssid_input, self.identity_input, self.password_input, self.auth_combo):
|
||||||
|
self._dismiss_keyboard()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _styled_message(icon, parent, title, text, buttons=QMessageBox.StandardButton.Ok):
|
||||||
|
"""显示与整体风格一致的暗色主题消息框"""
|
||||||
|
msg = QMessageBox(parent)
|
||||||
|
msg.setIcon(icon)
|
||||||
|
msg.setWindowTitle(title)
|
||||||
|
msg.setText(text)
|
||||||
|
msg.setStandardButtons(buttons)
|
||||||
|
msg.setStyleSheet("""
|
||||||
|
QMessageBox {
|
||||||
|
background-color: #3f3f3f;
|
||||||
|
color: #f2f2f2;
|
||||||
|
border: 2px solid #646464;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
QMessageBox QLabel {
|
||||||
|
color: #f2f2f2;
|
||||||
|
font-size: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
QMessageBox QPushButton {
|
||||||
|
min-width: 130px;
|
||||||
|
min-height: 50px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f8f8f8;
|
||||||
|
background-color: #555555;
|
||||||
|
border: 2px solid #888888;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 24px;
|
||||||
|
}
|
||||||
|
QMessageBox QPushButton:hover {
|
||||||
|
background-color: #636363;
|
||||||
|
border-color: #aaaaaa;
|
||||||
|
}
|
||||||
|
QMessageBox QPushButton:pressed {
|
||||||
|
background-color: #3d3d3d;
|
||||||
|
border-color: #5a9fcf;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
return msg.exec()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
layout = QHBoxLayout(self)
|
||||||
|
layout.setContentsMargins(14, 14, 14, 14)
|
||||||
|
layout.setSpacing(14)
|
||||||
|
|
||||||
|
left_panel = QFrame()
|
||||||
|
left_panel.setStyleSheet("background-color: #3f3f3f; border-radius: 10px;")
|
||||||
|
left_layout = QVBoxLayout(left_panel)
|
||||||
|
left_layout.setContentsMargins(10, 10, 10, 10)
|
||||||
|
|
||||||
|
title = QLabel("系统设置")
|
||||||
|
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
title.setStyleSheet("color: #efefef; font-size: 24px; font-weight: 600;")
|
||||||
|
left_layout.addWidget(title)
|
||||||
|
|
||||||
|
# 左侧设置项列表(启用平滑/按像素滚动,增大触摸目标)
|
||||||
|
self.item_list = QListWidget()
|
||||||
|
self.item_list.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||||
|
self.item_list.setSpacing(6)
|
||||||
|
self.item_list.setUniformItemSizes(False)
|
||||||
|
self.item_list.setStyleSheet(
|
||||||
|
"""
|
||||||
|
QListWidget {
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
color: #f2f2f2;
|
||||||
|
border: 1px solid #646464;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 22px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QListWidget::item {
|
||||||
|
height: 56px;
|
||||||
|
padding-left: 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
QListWidget::item:selected {
|
||||||
|
background-color: #2f6f91;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.item_list.addItem("WiFi设置")
|
||||||
|
self.item_list.addItem("其它设置(待定)")
|
||||||
|
self.item_list.currentRowChanged.connect(self.display_setting)
|
||||||
|
left_layout.addWidget(self.item_list)
|
||||||
|
layout.addWidget(left_panel, 1)
|
||||||
|
|
||||||
|
# 右侧设置参数区域(放入可滚动区域,便于触屏上下滑动)
|
||||||
|
self.settings_stack = QStackedWidget()
|
||||||
|
self.settings_stack.setStyleSheet("background-color: #444444; border-radius: 10px;")
|
||||||
|
self.init_wifi_settings()
|
||||||
|
self.init_todo_settings()
|
||||||
|
|
||||||
|
right_scroll = DragScrollArea()
|
||||||
|
right_scroll.setWidgetResizable(True)
|
||||||
|
right_scroll.setFrameShape(QFrame.Shape.NoFrame)
|
||||||
|
# 将堆叠面板放入 scroll area
|
||||||
|
right_scroll.setWidget(self.settings_stack)
|
||||||
|
right_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||||
|
right_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||||
|
layout.addWidget(right_scroll, 3)
|
||||||
|
|
||||||
|
self.item_list.setCurrentRow(0)
|
||||||
|
|
||||||
|
def init_wifi_settings(self):
|
||||||
|
wifi_widget = QWidget()
|
||||||
|
wifi_widget.setStyleSheet(
|
||||||
|
"""
|
||||||
|
/* ------ 通用标签 ------ */
|
||||||
|
QLabel {
|
||||||
|
color: #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 大按钮:清晰边框、足够高度 ------ */
|
||||||
|
QPushButton {
|
||||||
|
min-height: 50px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f8f8f8;
|
||||||
|
background-color: #555555;
|
||||||
|
border: 2px solid #888888;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 24px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #636363;
|
||||||
|
border-color: #aaaaaa;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #3d3d3d;
|
||||||
|
border-color: #5a9fcf;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
color: #808080;
|
||||||
|
background-color: #404040;
|
||||||
|
border-color: #555555;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 输入框 ------ */
|
||||||
|
QLineEdit {
|
||||||
|
min-height: 44px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #f2f2f2;
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
border: 2px solid #707070;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
}
|
||||||
|
QLineEdit:focus {
|
||||||
|
border-color: #5a9fcf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 下拉框 ------ */
|
||||||
|
QComboBox {
|
||||||
|
min-height: 44px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #f2f2f2;
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
border: 2px solid #707070;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
}
|
||||||
|
QComboBox:hover {
|
||||||
|
border-color: #aaaaaa;
|
||||||
|
}
|
||||||
|
QComboBox QAbstractItemView {
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
color: #f2f2f2;
|
||||||
|
selection-background-color: #2f6f91;
|
||||||
|
border: 1px solid #707070;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 列表控件 ------ */
|
||||||
|
QListWidget {
|
||||||
|
min-height: 300px;
|
||||||
|
max-width: 500%;
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
color: #f2f2f2;
|
||||||
|
border: 2px solid #707070;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
QListWidget::item {
|
||||||
|
min-height: 40px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QListWidget::item:selected {
|
||||||
|
background-color: #2f6f91;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
QListWidget::item:selected QLabel {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
QListWidget::item:hover {
|
||||||
|
background-color: #505050;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
wifi_layout = QVBoxLayout(wifi_widget)
|
||||||
|
wifi_layout.setContentsMargins(16, 16, 16, 16)
|
||||||
|
wifi_layout.setSpacing(14)
|
||||||
|
|
||||||
|
wifi_title = QLabel("WiFi设置")
|
||||||
|
wifi_title.setStyleSheet("color: #f2f2f2; font-size: 26px; font-weight: 700;")
|
||||||
|
wifi_layout.addWidget(wifi_title)
|
||||||
|
|
||||||
|
self.current_status_label = QLabel("当前连接:未知")
|
||||||
|
self.current_status_label.setStyleSheet("color: #cccccc; font-size: 19px; font-weight: 500;")
|
||||||
|
wifi_layout.addWidget(self.current_status_label)
|
||||||
|
|
||||||
|
# 显示保存的WiFi列表
|
||||||
|
saved_title = QLabel("已保存网络")
|
||||||
|
saved_title.setStyleSheet("color: #dcdcdc; font-size: 22px; font-weight: 600;")
|
||||||
|
wifi_layout.addWidget(saved_title)
|
||||||
|
|
||||||
|
self.saved_wifi_list = DraggableListWidget()
|
||||||
|
wifi_layout.addWidget(self.saved_wifi_list)
|
||||||
|
|
||||||
|
saved_buttons_layout = QHBoxLayout()
|
||||||
|
saved_buttons_layout.setSpacing(12)
|
||||||
|
connect_saved_button = QPushButton("连接到此网络")
|
||||||
|
connect_saved_button.clicked.connect(self.connect_to_saved_wifi)
|
||||||
|
saved_buttons_layout.addWidget(connect_saved_button)
|
||||||
|
|
||||||
|
remove_saved_button = QPushButton("删除选中")
|
||||||
|
remove_saved_button.clicked.connect(self.remove_selected_saved_wifi)
|
||||||
|
saved_buttons_layout.addWidget(remove_saved_button)
|
||||||
|
wifi_layout.addLayout(saved_buttons_layout)
|
||||||
|
|
||||||
|
nearby_title = QLabel("附近网络")
|
||||||
|
nearby_title.setStyleSheet("color: #dcdcdc; font-size: 22px; font-weight: 600;")
|
||||||
|
wifi_layout.addWidget(nearby_title)
|
||||||
|
|
||||||
|
self.nearby_wifi_list = DraggableListWidget()
|
||||||
|
self.nearby_wifi_list.itemClicked.connect(self.fill_ssid_from_scan)
|
||||||
|
wifi_layout.addWidget(self.nearby_wifi_list)
|
||||||
|
|
||||||
|
self.scan_button = QPushButton("扫描网络")
|
||||||
|
self.scan_button.clicked.connect(self.scan_nearby_wifi)
|
||||||
|
wifi_layout.addWidget(self.scan_button)
|
||||||
|
|
||||||
|
# 连接新WiFi
|
||||||
|
connect_title = QLabel("连接新网络")
|
||||||
|
connect_title.setStyleSheet("color: #dcdcdc; font-size: 22px; font-weight: 600;")
|
||||||
|
wifi_layout.addWidget(connect_title)
|
||||||
|
|
||||||
|
form = QFormLayout()
|
||||||
|
form.setLabelAlignment(Qt.AlignmentFlag.AlignRight)
|
||||||
|
form.setFormAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||||
|
form.setHorizontalSpacing(16)
|
||||||
|
form.setVerticalSpacing(12)
|
||||||
|
|
||||||
|
# 表单标签使用浅色
|
||||||
|
ssid_label = QLabel("SSID")
|
||||||
|
ssid_label.setStyleSheet("color: #d0d0d0; font-size: 19px; font-weight: 500;")
|
||||||
|
self.ssid_input = QLineEdit()
|
||||||
|
self.ssid_input.setStyleSheet("max-width: 400%;")
|
||||||
|
self.ssid_input.setPlaceholderText("输入WiFi名称")
|
||||||
|
self.ssid_input.installEventFilter(self)
|
||||||
|
self.ssid_input.textChanged.connect(self._on_ssid_text_changed)
|
||||||
|
form.addRow(ssid_label, self.ssid_input)
|
||||||
|
|
||||||
|
auth_label = QLabel("认证方式")
|
||||||
|
auth_label.setStyleSheet("color: #d0d0d0; font-size: 19px; font-weight: 500;")
|
||||||
|
self.auth_combo = QComboBox()
|
||||||
|
self.auth_combo.setStyleSheet(
|
||||||
|
"""
|
||||||
|
QComboBox {
|
||||||
|
max-width: 400%;
|
||||||
|
font-size: 22px;
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# 直接设置下拉列表视图的样式,避免被父级样式覆盖
|
||||||
|
self.auth_combo.view().setStyleSheet(
|
||||||
|
"""
|
||||||
|
QListView {
|
||||||
|
font-size: 24px;
|
||||||
|
min-height: 50px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
QListView::item {
|
||||||
|
min-height: 50px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.auth_combo.addItem("开放网络(无密码)", "open")
|
||||||
|
self.auth_combo.addItem("WPA/WPA2-PSK", "psk")
|
||||||
|
self.auth_combo.addItem("WPA-EAP (PEAP/MSCHAPv2)", "eap")
|
||||||
|
self.auth_combo.currentIndexChanged.connect(self.update_auth_fields)
|
||||||
|
form.addRow(auth_label, self.auth_combo)
|
||||||
|
|
||||||
|
identity_label = QLabel("身份")
|
||||||
|
identity_label.setStyleSheet("color: #d0d0d0; font-size: 19px; font-weight: 500;")
|
||||||
|
self.identity_label = identity_label
|
||||||
|
self.identity_input = QLineEdit()
|
||||||
|
self.identity_input.setStyleSheet("max-width: 400%;")
|
||||||
|
self.identity_input.setPlaceholderText("企业网络用户名/身份")
|
||||||
|
self.identity_input.installEventFilter(self)
|
||||||
|
form.addRow(identity_label, self.identity_input)
|
||||||
|
|
||||||
|
password_label = QLabel("密码")
|
||||||
|
password_label.setStyleSheet("color: #d0d0d0; font-size: 19px; font-weight: 500;")
|
||||||
|
self.password_label = password_label
|
||||||
|
self.password_input = QLineEdit()
|
||||||
|
self.password_input.setStyleSheet("max-width: 400%;")
|
||||||
|
self.password_input.setPlaceholderText("输入WiFi密码")
|
||||||
|
self.password_input.installEventFilter(self)
|
||||||
|
self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
|
||||||
|
form.addRow(password_label, self.password_input)
|
||||||
|
|
||||||
|
wifi_layout.addLayout(form)
|
||||||
|
|
||||||
|
# 连接按钮使用醒目的强调色
|
||||||
|
connect_button = QPushButton("连接")
|
||||||
|
connect_button.setStyleSheet(
|
||||||
|
"""
|
||||||
|
QPushButton {
|
||||||
|
min-height: 52px;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #2f6f91;
|
||||||
|
border: 2px solid #4a9fc8;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 24px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #3a85b3;
|
||||||
|
border-color: #6fb8dd;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #1e4d66;
|
||||||
|
border-color: #2f6f91;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
connect_button.clicked.connect(self.connect_to_wifi)
|
||||||
|
wifi_layout.addWidget(connect_button)
|
||||||
|
|
||||||
|
wifi_layout.addStretch()
|
||||||
|
self.settings_stack.addWidget(wifi_widget)
|
||||||
|
self.update_auth_fields()
|
||||||
|
self.refresh_saved_wifi()
|
||||||
|
self.refresh_current_status()
|
||||||
|
|
||||||
|
def init_todo_settings(self):
|
||||||
|
todo_widget = QWidget()
|
||||||
|
todo_widget.setStyleSheet(
|
||||||
|
"""
|
||||||
|
QLabel {
|
||||||
|
color: #e4e4e4;
|
||||||
|
}
|
||||||
|
QPushButton {
|
||||||
|
min-height: 50px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f8f8f8;
|
||||||
|
background-color: #555555;
|
||||||
|
border: 2px solid #888888;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 24px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #636363;
|
||||||
|
border-color: #aaaaaa;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
todo_layout = QVBoxLayout(todo_widget)
|
||||||
|
todo_layout.setContentsMargins(20, 20, 20, 20)
|
||||||
|
|
||||||
|
label = QLabel("其它配置项待定")
|
||||||
|
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
label.setStyleSheet("color: #cecece; font-size: 26px; font-weight: 600;")
|
||||||
|
todo_layout.addWidget(label)
|
||||||
|
|
||||||
|
hint = QLabel("更多设置项将在后续版本中添加")
|
||||||
|
hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
hint.setStyleSheet("color: #909090; font-size: 18px; margin-top: 12px;")
|
||||||
|
todo_layout.addWidget(hint)
|
||||||
|
|
||||||
|
todo_layout.addStretch()
|
||||||
|
|
||||||
|
self.settings_stack.addWidget(todo_widget)
|
||||||
|
|
||||||
|
def refresh_saved_wifi(self):
|
||||||
|
try:
|
||||||
|
saved_networks = self.wifi_manager.list_saved_networks()
|
||||||
|
except Exception as e:
|
||||||
|
self._styled_message(QMessageBox.Icon.Critical, self, "错误", f"无法加载保存的WiFi: {str(e)}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 比较与上次缓存的网络列表是否有变化
|
||||||
|
cache_key = [(n.get("network_id"), n.get("ssid"), n.get("flags")) for n in saved_networks]
|
||||||
|
if cache_key == self._saved_networks_cache:
|
||||||
|
return # 无变化,不清空列表
|
||||||
|
self._saved_networks_cache = cache_key
|
||||||
|
|
||||||
|
# 列表有变化时才真正刷新
|
||||||
|
current_item = self.saved_wifi_list.currentItem()
|
||||||
|
current_net_id = current_item.data(Qt.ItemDataRole.UserRole).get("network_id") if current_item else None
|
||||||
|
|
||||||
|
self.saved_wifi_list.clear()
|
||||||
|
for network in saved_networks:
|
||||||
|
item_text = f"[{network.get('network_id', '-')}] {network.get('ssid', '<hidden>')} {network.get('flags', '')}"
|
||||||
|
item = QListWidgetItem(item_text)
|
||||||
|
item.setData(Qt.ItemDataRole.UserRole, network)
|
||||||
|
self.saved_wifi_list.addItem(item)
|
||||||
|
# 恢复选中
|
||||||
|
if current_net_id is not None and network.get("network_id") == current_net_id:
|
||||||
|
self.saved_wifi_list.setCurrentItem(item)
|
||||||
|
|
||||||
|
def scan_nearby_wifi(self):
|
||||||
|
"""在后台线程中扫描WiFi,防止界面卡死"""
|
||||||
|
self.nearby_wifi_list.clear()
|
||||||
|
# 把焦点交给页面自身,防止跳到输入框导致画面跳动
|
||||||
|
self.setFocus()
|
||||||
|
self.scan_button.setEnabled(False)
|
||||||
|
self.scan_button.setText("扫描中……")
|
||||||
|
|
||||||
|
self._scan_thread = QThread()
|
||||||
|
self._scan_worker = WifiScanWorker(self.wifi_manager)
|
||||||
|
self._scan_worker.moveToThread(self._scan_thread)
|
||||||
|
self._scan_thread.started.connect(self._scan_worker.run)
|
||||||
|
self._scan_worker.scan_finished.connect(self._on_scan_finished)
|
||||||
|
self._scan_worker.scan_error.connect(self._on_scan_error)
|
||||||
|
# 清理线程资源
|
||||||
|
self._scan_worker.scan_finished.connect(self._scan_thread.quit)
|
||||||
|
self._scan_worker.scan_error.connect(self._scan_thread.quit)
|
||||||
|
self._scan_worker.scan_finished.connect(self._scan_worker.deleteLater)
|
||||||
|
self._scan_worker.scan_error.connect(self._scan_worker.deleteLater)
|
||||||
|
self._scan_thread.finished.connect(self._scan_thread.deleteLater)
|
||||||
|
self._scan_thread.start()
|
||||||
|
|
||||||
|
def _on_scan_finished(self, networks):
|
||||||
|
"""扫描完成后的UI更新(主线程中执行)"""
|
||||||
|
self.scan_button.setEnabled(True)
|
||||||
|
self.scan_button.setText("扫描网络")
|
||||||
|
|
||||||
|
processed = self._deduplicate_networks(networks)
|
||||||
|
if not processed:
|
||||||
|
self._styled_message(QMessageBox.Icon.Information, self, "提示", "未扫描到可用网络")
|
||||||
|
return
|
||||||
|
|
||||||
|
for network in processed:
|
||||||
|
ssid = network.get("ssid", "")
|
||||||
|
if not ssid:
|
||||||
|
continue
|
||||||
|
decoded_ssid = self._decode_ssid(ssid)
|
||||||
|
signal = network.get("signal_level", "")
|
||||||
|
|
||||||
|
# 自定义列表项:SSID靠左,信号强度靠右
|
||||||
|
item_widget = QWidget()
|
||||||
|
item_widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
|
# 为了比较好看的列表项目,下面的不要删
|
||||||
|
item_widget.setStyleSheet("""
|
||||||
|
QWidget {
|
||||||
|
min-height: 40px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QWidget:selected {
|
||||||
|
background-color: #2f6f91;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
QWidget:selected QLabel {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
QWidget:hover {
|
||||||
|
background-color: #505050;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
item_layout = QHBoxLayout(item_widget)
|
||||||
|
item_layout.setContentsMargins(8, 2, 12, 2)
|
||||||
|
|
||||||
|
ssid_label = QLabel(decoded_ssid)
|
||||||
|
ssid_label.setStyleSheet("background: transparent; color: #f2f2f2; font-size: 18px;")
|
||||||
|
signal_label = QLabel(f"{signal} dBm" if signal else "")
|
||||||
|
signal_label.setStyleSheet("background: transparent; color: #aaaaaa; font-size: 16px;")
|
||||||
|
|
||||||
|
item_layout.addWidget(ssid_label)
|
||||||
|
item_layout.addStretch()
|
||||||
|
item_layout.addWidget(signal_label)
|
||||||
|
|
||||||
|
item = QListWidgetItem()
|
||||||
|
item.setData(Qt.ItemDataRole.UserRole, network)
|
||||||
|
item.setSizeHint(item_widget.sizeHint())
|
||||||
|
self.nearby_wifi_list.addItem(item)
|
||||||
|
self.nearby_wifi_list.setItemWidget(item, item_widget)
|
||||||
|
|
||||||
|
def _on_scan_error(self, error_msg):
|
||||||
|
"""扫描出错后的UI恢复(主线程中执行)"""
|
||||||
|
self.scan_button.setEnabled(True)
|
||||||
|
self.scan_button.setText("扫描网络")
|
||||||
|
self._styled_message(QMessageBox.Icon.Critical, self, "错误", f"扫描网络失败: {error_msg}")
|
||||||
|
|
||||||
|
def _on_ssid_text_changed(self, text):
|
||||||
|
"""SSID输入框文本变化时,如果清空则重置为开放网络"""
|
||||||
|
if not text.strip():
|
||||||
|
self.auth_combo.blockSignals(True)
|
||||||
|
self.auth_combo.setCurrentIndex(0)
|
||||||
|
self.auth_combo.blockSignals(False)
|
||||||
|
self.update_auth_fields()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _decode_ssid(ssid):
|
||||||
|
"""尝试解码非英文WiFi名称(处理 \\xe9\\x83\\xbd 等转义序列)"""
|
||||||
|
if not ssid:
|
||||||
|
return ssid
|
||||||
|
# 尝试 unicode_escape 解码(处理 \x 转义序列,返回 bytes)
|
||||||
|
try:
|
||||||
|
decoded = ssid.encode('latin-1').decode('unicode-escape').encode('latin-1').decode()
|
||||||
|
if decoded != ssid:
|
||||||
|
return decoded
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# 尝试 latin-1 → UTF-8 双重转换
|
||||||
|
try:
|
||||||
|
decoded = bytes(ssid, encoding='utf-8').decode('utf-8')
|
||||||
|
if decoded != ssid:
|
||||||
|
return decoded
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return ssid
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _deduplicate_networks(networks):
|
||||||
|
"""去重同名网络,每个SSID只保留信号最强的一个"""
|
||||||
|
best = {}
|
||||||
|
for net in networks:
|
||||||
|
ssid = net.get("ssid", "")
|
||||||
|
if not ssid:
|
||||||
|
continue
|
||||||
|
raw = net.get("signal_level", -100)
|
||||||
|
try:
|
||||||
|
signal = int(raw)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
signal = -100
|
||||||
|
if ssid not in best or signal > best[ssid].get("_signal_int", -100):
|
||||||
|
net["_signal_int"] = signal
|
||||||
|
best[ssid] = net
|
||||||
|
return list(best.values())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _detect_auth_mode(network):
|
||||||
|
"""根据 wpa_supplicant 返回的标准 flags 判断认证方式"""
|
||||||
|
flags = network.get("flags", "").strip()
|
||||||
|
|
||||||
|
# 无加密标记 → 开放网络
|
||||||
|
if not flags or flags in ("", "[ESS]", "[NONE]", "NONE"):
|
||||||
|
return "open"
|
||||||
|
|
||||||
|
# 检查是否含企业级认证标记
|
||||||
|
eap_keywords = (
|
||||||
|
"WPA2-EAP", "WPA-EAP", "WPA3-EAP",
|
||||||
|
"EAP", "SUITE-B",
|
||||||
|
"802.1X", "IEEE8021X", "ENTERPRISE",
|
||||||
|
"FT/EAP",
|
||||||
|
)
|
||||||
|
if any(kw in flags.upper() for kw in eap_keywords):
|
||||||
|
return "eap"
|
||||||
|
|
||||||
|
# 检查是否含 PSK 类标记(个人级加密)
|
||||||
|
psk_keywords = (
|
||||||
|
"PSK", "SAE", "WPA2", "WPA3", "WPA",
|
||||||
|
"CCMP", "TKIP",
|
||||||
|
)
|
||||||
|
if any(kw in flags.upper() for kw in psk_keywords):
|
||||||
|
return "psk"
|
||||||
|
|
||||||
|
# 兜底:有标记但无法识别,默认 psk
|
||||||
|
return "psk"
|
||||||
|
|
||||||
|
def fill_ssid_from_scan(self, item):
|
||||||
|
network = item.data(Qt.ItemDataRole.UserRole) or {}
|
||||||
|
ssid = network.get("ssid", "")
|
||||||
|
if ssid:
|
||||||
|
decoded = self._decode_ssid(ssid)
|
||||||
|
self.ssid_input.setText(decoded)
|
||||||
|
# 根据网络标志自动选择认证方式
|
||||||
|
auth_mode = self._detect_auth_mode(network)
|
||||||
|
index = self.auth_combo.findData(auth_mode)
|
||||||
|
if index >= 0:
|
||||||
|
self.auth_combo.blockSignals(True)
|
||||||
|
self.auth_combo.setCurrentIndex(index)
|
||||||
|
self.auth_combo.blockSignals(False)
|
||||||
|
self.update_auth_fields()
|
||||||
|
|
||||||
|
def update_auth_fields(self):
|
||||||
|
auth_mode = self.auth_combo.currentData()
|
||||||
|
if auth_mode == "open":
|
||||||
|
self.identity_label.setVisible(False)
|
||||||
|
self.identity_input.setVisible(False)
|
||||||
|
self.password_label.setVisible(False)
|
||||||
|
self.password_input.setVisible(False)
|
||||||
|
elif auth_mode == "psk":
|
||||||
|
self.identity_label.setVisible(False)
|
||||||
|
self.identity_input.setVisible(False)
|
||||||
|
self.password_label.setVisible(True)
|
||||||
|
self.password_input.setVisible(True)
|
||||||
|
else:
|
||||||
|
self.identity_label.setVisible(True)
|
||||||
|
self.identity_input.setVisible(True)
|
||||||
|
self.password_label.setVisible(True)
|
||||||
|
self.password_input.setVisible(True)
|
||||||
|
|
||||||
|
def refresh_current_status(self):
|
||||||
|
try:
|
||||||
|
status = self.wifi_manager.get_current_status()
|
||||||
|
ssid = status.get("ssid", "未连接")
|
||||||
|
ip_addr = status.get("ip_address", "-")
|
||||||
|
state = status.get("wpa_state", "UNKNOWN")
|
||||||
|
self.current_status_label.setText(f"当前连接:{ssid} | IP: {ip_addr} | 状态: {state}")
|
||||||
|
except Exception:
|
||||||
|
self.current_status_label.setText("当前连接:未知")
|
||||||
|
|
||||||
|
def _start_status_timer(self):
|
||||||
|
"""启动定时器,每秒刷新已保存网络列表和当前连接状态"""
|
||||||
|
self._status_timer = QTimer(self)
|
||||||
|
self._status_timer.timeout.connect(self._on_status_timer_tick)
|
||||||
|
self._status_timer.start(1000)
|
||||||
|
|
||||||
|
def _on_status_timer_tick(self):
|
||||||
|
self.refresh_saved_wifi()
|
||||||
|
self.refresh_current_status()
|
||||||
|
|
||||||
|
def connect_to_saved_wifi(self):
|
||||||
|
"""连接已保存列表中选中的网络"""
|
||||||
|
item = self.saved_wifi_list.currentItem()
|
||||||
|
if item is None:
|
||||||
|
self._styled_message(QMessageBox.Icon.Warning, self, "提示", "请先选择一个已保存网络")
|
||||||
|
return
|
||||||
|
network = item.data(Qt.ItemDataRole.UserRole) or {}
|
||||||
|
network_id = network.get("network_id")
|
||||||
|
ssid = network.get("ssid", "")
|
||||||
|
if network_id is None:
|
||||||
|
self._styled_message(QMessageBox.Icon.Warning, self, "提示", "选中网络无效")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
ok = self.wifi_manager.connect_network_id(network_id)
|
||||||
|
if not ok:
|
||||||
|
self._styled_message(QMessageBox.Icon.Critical, self, "错误", "连接请求下发失败")
|
||||||
|
return
|
||||||
|
self._styled_message(QMessageBox.Icon.Information, self, "成功", f"已发起连接: {ssid}")
|
||||||
|
except Exception as e:
|
||||||
|
self._styled_message(QMessageBox.Icon.Critical, self, "错误", f"连接失败: {str(e)}")
|
||||||
|
|
||||||
|
def remove_selected_saved_wifi(self):
|
||||||
|
item = self.saved_wifi_list.currentItem()
|
||||||
|
if item is None:
|
||||||
|
self._styled_message(QMessageBox.Icon.Warning, self, "提示", "请先选择一个已保存网络")
|
||||||
|
return
|
||||||
|
network = item.data(Qt.ItemDataRole.UserRole) or {}
|
||||||
|
network_id = network.get("network_id")
|
||||||
|
ssid = network.get("ssid", "")
|
||||||
|
if network_id is None:
|
||||||
|
self._styled_message(QMessageBox.Icon.Warning, self, "提示", "选中网络无效,无法删除")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.wifi_manager.remove_network(network_id)
|
||||||
|
self._styled_message(QMessageBox.Icon.Information, self, "成功", f"已删除网络: {ssid}")
|
||||||
|
self.refresh_saved_wifi()
|
||||||
|
self.refresh_current_status()
|
||||||
|
except Exception as e:
|
||||||
|
self._styled_message(QMessageBox.Icon.Critical, self, "错误", f"删除失败: {str(e)}")
|
||||||
|
|
||||||
|
def connect_to_wifi(self):
|
||||||
|
ssid = self.ssid_input.text().strip()
|
||||||
|
password = self.password_input.text()
|
||||||
|
identity = self.identity_input.text().strip()
|
||||||
|
auth_mode = self.auth_combo.currentData()
|
||||||
|
|
||||||
|
if not ssid:
|
||||||
|
self._styled_message(QMessageBox.Icon.Warning, self, "警告", "WiFi名称不能为空!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if auth_mode == "psk" and not password:
|
||||||
|
self._styled_message(QMessageBox.Icon.Warning, self, "警告", "WPA/WPA2 认证需要密码")
|
||||||
|
return
|
||||||
|
|
||||||
|
if auth_mode == "eap" and (not identity or not password):
|
||||||
|
self._styled_message(QMessageBox.Icon.Warning, self, "警告", "WPA-EAP 认证需要身份和密码")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if auth_mode == "open":
|
||||||
|
ok = self.wifi_manager.connect_wifi(ssid, None)
|
||||||
|
elif auth_mode == "psk":
|
||||||
|
ok = self.wifi_manager.connect_wifi(ssid, password)
|
||||||
|
else:
|
||||||
|
ok = self.wifi_manager.connect_eap(ssid, identity, password)
|
||||||
|
|
||||||
|
if not ok:
|
||||||
|
self._styled_message(QMessageBox.Icon.Critical, self, "错误", "连接请求下发失败,请检查系统日志")
|
||||||
|
return
|
||||||
|
|
||||||
|
self._styled_message(QMessageBox.Icon.Information, self, "成功", f"已发起连接: {ssid}")
|
||||||
|
self.refresh_saved_wifi()
|
||||||
|
self.refresh_current_status()
|
||||||
|
except Exception as e:
|
||||||
|
self._styled_message(QMessageBox.Icon.Critical, self, "错误", f"连接WiFi失败: {str(e)}")
|
||||||
|
|
||||||
|
def display_setting(self, index):
|
||||||
|
if index < 0:
|
||||||
|
return
|
||||||
|
self.settings_stack.setCurrentIndex(index)
|
||||||
@@ -19,13 +19,59 @@ class AIOPrrintSystemAPI:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
url = f"{self.api_url}/status"
|
|
||||||
try:
|
test_data = {
|
||||||
r = requests.get(url, headers=self.headers, timeout=5)
|
'job': {
|
||||||
r.raise_for_status()
|
'job': {
|
||||||
return r.json()
|
'estimatedPrintTime': 1234,
|
||||||
except:
|
'filament': {'length': 765, 'volume': 24356},
|
||||||
return {"status": {}, "job": {}}
|
'file': {'display_name': 'Test File','date': None, 'name': '20260414135441_42bff5215c6148b8b5f4d8c4f15d5ddc.gcode', 'origin': 'local', 'path': None, 'size': 1468987},
|
||||||
|
'lastPrintTime': None,
|
||||||
|
'user': None
|
||||||
|
},
|
||||||
|
'progress': {
|
||||||
|
'completion': 74.8,
|
||||||
|
'filepos': 1234,
|
||||||
|
'printTime': 1235,
|
||||||
|
'printTimeLeft': 6353,
|
||||||
|
'printTimeLeftOrigin': 5366
|
||||||
|
},
|
||||||
|
'state': 'Operational'
|
||||||
|
},
|
||||||
|
'status': {
|
||||||
|
'sd': {'ready': False},
|
||||||
|
'state': {
|
||||||
|
'error': '',
|
||||||
|
'flags': {
|
||||||
|
'cancelling': False,
|
||||||
|
'closedOrError': False,
|
||||||
|
'error': False,
|
||||||
|
'finishing': False,
|
||||||
|
'operational': True,
|
||||||
|
'paused': False,
|
||||||
|
'pausing': False,
|
||||||
|
'printing': False,
|
||||||
|
'ready': True,
|
||||||
|
'resuming': False,
|
||||||
|
'sdReady': False
|
||||||
|
},
|
||||||
|
'text': 'Operational test'
|
||||||
|
},
|
||||||
|
'temperature': {
|
||||||
|
'bed': {'actual': 85, 'offset': 0, 'target': 56},
|
||||||
|
'tool0': {'actual': 0.0, 'offset': 0, 'target': 340}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return test_data
|
||||||
|
|
||||||
|
# url = f"{self.api_url}/status"
|
||||||
|
# try:
|
||||||
|
# r = requests.get(url, headers=self.headers, timeout=5)
|
||||||
|
# r.raise_for_status()
|
||||||
|
# return r.json()
|
||||||
|
# except:
|
||||||
|
# return {"status": {}, "job": {}}
|
||||||
|
|
||||||
def pause_print(self):
|
def pause_print(self):
|
||||||
return self._post_action("pause_print", action="pause")
|
return self._post_action("pause_print", action="pause")
|
||||||
|
|||||||
423
utils/floating_keyboard.py
Normal file
423
utils/floating_keyboard.py
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
"""
|
||||||
|
悬浮虚拟键盘
|
||||||
|
- 美式 QWERTY 键盘排列(字母 + 数字 + 常用符号)
|
||||||
|
- Shift / Caps Lock / 退格 / 空格 / 回车 / 关闭
|
||||||
|
- 可拖拽悬浮,自动跟随焦点输入框
|
||||||
|
- 暗色主题,大按钮适合触屏
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
|
QApplication, QLineEdit, QTextEdit,
|
||||||
|
)
|
||||||
|
from PyQt6.QtCore import Qt, QEvent, QPoint, pyqtSignal
|
||||||
|
from PyQt6.QtGui import QMouseEvent, QTextCursor
|
||||||
|
|
||||||
|
|
||||||
|
# ─── 键盘布局定义 ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
KEY_ROWS = {
|
||||||
|
"normal": [
|
||||||
|
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
|
||||||
|
["a", "s", "d", "f", "g", "h", "j", "k", "l"],
|
||||||
|
["z", "x", "c", "v", "b", "n", "m"],
|
||||||
|
],
|
||||||
|
"shift": [
|
||||||
|
["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
|
||||||
|
["A", "S", "D", "F", "G", "H", "J", "K", "L"],
|
||||||
|
["Z", "X", "C", "V", "B", "N", "M"],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
NUMBER_ROW = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
|
||||||
|
|
||||||
|
# 符号键:按 Shift 或符号切换时与数字行互换
|
||||||
|
SYMBOL_ROW = ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"]
|
||||||
|
|
||||||
|
# 右侧额外符号(放在字母区行末或单独行)
|
||||||
|
EXTRA_SYMBOLS = [
|
||||||
|
["-", "=", "[", "]", "\\", ";", "'", ",", ".", "/"],
|
||||||
|
["_", "+", "{", "}", "|", ":", "\"", "<", ">", "?"],
|
||||||
|
]
|
||||||
|
|
||||||
|
KEY_STYLE = """
|
||||||
|
QPushButton {
|
||||||
|
min-width: 52px;
|
||||||
|
min-height: 52px;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f2f2f2;
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
border: 2px solid #646464;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 4px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #5a5a5a;
|
||||||
|
border-color: #888888;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #2f6f91;
|
||||||
|
border-color: #5a9fcf;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
CTRL_KEY_STYLE = """
|
||||||
|
QPushButton {
|
||||||
|
min-width: 72px;
|
||||||
|
min-height: 52px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f2f2f2;
|
||||||
|
background-color: #555555;
|
||||||
|
border: 2px solid #707070;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #666666;
|
||||||
|
border-color: #909090;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #2f6f91;
|
||||||
|
border-color: #5a9fcf;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
SPACE_STYLE = """
|
||||||
|
QPushButton {
|
||||||
|
min-width: 280px;
|
||||||
|
min-height: 52px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: transparent;
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
border: 2px solid #646464;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #5a5a5a;
|
||||||
|
border-color: #888888;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #2f6f91;
|
||||||
|
border-color: #5a9fcf;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ACTIVE_CTRL_STYLE = """
|
||||||
|
QPushButton {
|
||||||
|
min-width: 72px;
|
||||||
|
min-height: 52px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #2f6f91;
|
||||||
|
border: 2px solid #5a9fcf;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #3a85b3;
|
||||||
|
border-color: #6fb8dd;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class FloatingKeyboard(QWidget):
|
||||||
|
"""可悬浮拖拽的虚拟键盘,自动跟随焦点输入框"""
|
||||||
|
|
||||||
|
key_pressed = pyqtSignal(str)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowFlags(
|
||||||
|
Qt.WindowType.FramelessWindowHint
|
||||||
|
| Qt.WindowType.WindowStaysOnTopHint
|
||||||
|
| Qt.WindowType.Tool
|
||||||
|
)
|
||||||
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
|
self.setStyleSheet("background: transparent;")
|
||||||
|
|
||||||
|
self._drag_pos = None
|
||||||
|
self._shift_on = False
|
||||||
|
self._caps_on = False
|
||||||
|
self._symbol_mode = False
|
||||||
|
|
||||||
|
self._target_widget = None
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
# ── UI 构建 ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
outer = QWidget()
|
||||||
|
outer.setStyleSheet(
|
||||||
|
"background-color: #333333; border: 2px solid #555555; border-radius: 12px;"
|
||||||
|
)
|
||||||
|
layout = QVBoxLayout(outer)
|
||||||
|
layout.setContentsMargins(8, 8, 8, 8)
|
||||||
|
layout.setSpacing(4)
|
||||||
|
|
||||||
|
# ── 顶部拖拽条 ──
|
||||||
|
title_bar = QWidget()
|
||||||
|
title_bar.setFixedHeight(28)
|
||||||
|
title_bar.setStyleSheet("background: transparent;")
|
||||||
|
title_bar.mousePressEvent = self._title_mouse_press
|
||||||
|
title_bar.mouseMoveEvent = self._title_mouse_move
|
||||||
|
title_layout = QHBoxLayout(title_bar)
|
||||||
|
title_layout.setContentsMargins(8, 0, 8, 0)
|
||||||
|
|
||||||
|
drag_label = QPushButton("≡ 键盘")
|
||||||
|
drag_label.setStyleSheet(
|
||||||
|
"""
|
||||||
|
QPushButton {
|
||||||
|
background: transparent;
|
||||||
|
color: #aaaaaa;
|
||||||
|
font-size: 16px;
|
||||||
|
border: none;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
title_layout.addWidget(drag_label)
|
||||||
|
title_layout.addStretch()
|
||||||
|
|
||||||
|
close_btn = QPushButton("✕")
|
||||||
|
close_btn.setFixedSize(28, 28)
|
||||||
|
close_btn.setStyleSheet(
|
||||||
|
"""
|
||||||
|
QPushButton {
|
||||||
|
background: transparent;
|
||||||
|
color: #aaaaaa;
|
||||||
|
font-size: 18px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #555555;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
close_btn.clicked.connect(self.hide)
|
||||||
|
title_layout.addWidget(close_btn)
|
||||||
|
layout.addWidget(title_bar)
|
||||||
|
|
||||||
|
# ── 数字/符号行 ──
|
||||||
|
self.num_layout = QHBoxLayout()
|
||||||
|
self.num_layout.setSpacing(4)
|
||||||
|
self.num_buttons = []
|
||||||
|
for ch in NUMBER_ROW:
|
||||||
|
btn = self._make_key(ch)
|
||||||
|
self.num_buttons.append(btn)
|
||||||
|
self.num_layout.addWidget(btn)
|
||||||
|
layout.addLayout(self.num_layout)
|
||||||
|
|
||||||
|
# ── 字母行 ──
|
||||||
|
self._letter_buttons = [] # flat list for shift toggle
|
||||||
|
for row_keys in KEY_ROWS["normal"]:
|
||||||
|
row_layout = QHBoxLayout()
|
||||||
|
row_layout.setSpacing(4)
|
||||||
|
row_layout.addStretch()
|
||||||
|
for ch in row_keys:
|
||||||
|
btn = self._make_key(ch)
|
||||||
|
self._letter_buttons.append(btn)
|
||||||
|
row_layout.addWidget(btn)
|
||||||
|
row_layout.addStretch()
|
||||||
|
layout.addLayout(row_layout)
|
||||||
|
|
||||||
|
# ── 额外符号行(- = [ ] 等) ──
|
||||||
|
self.extra_layout = QHBoxLayout()
|
||||||
|
self.extra_layout.setSpacing(4)
|
||||||
|
self.extra_layout.addStretch()
|
||||||
|
self.extra_buttons = []
|
||||||
|
for ch in EXTRA_SYMBOLS[0]:
|
||||||
|
btn = self._make_key(ch)
|
||||||
|
self.extra_buttons.append(btn)
|
||||||
|
self.extra_layout.addWidget(btn)
|
||||||
|
self.extra_layout.addStretch()
|
||||||
|
layout.addLayout(self.extra_layout)
|
||||||
|
|
||||||
|
# ── 功能键行 ──
|
||||||
|
ctrl_layout = QHBoxLayout()
|
||||||
|
ctrl_layout.setSpacing(4)
|
||||||
|
|
||||||
|
self.shift_btn = QPushButton("⇧")
|
||||||
|
self.shift_btn.setStyleSheet(CTRL_KEY_STYLE)
|
||||||
|
self.shift_btn.clicked.connect(self._toggle_shift)
|
||||||
|
ctrl_layout.addWidget(self.shift_btn)
|
||||||
|
|
||||||
|
self.caps_btn = QPushButton("A/a")
|
||||||
|
self.caps_btn.setStyleSheet(CTRL_KEY_STYLE)
|
||||||
|
self.caps_btn.clicked.connect(self._toggle_caps)
|
||||||
|
ctrl_layout.addWidget(self.caps_btn)
|
||||||
|
|
||||||
|
self.sym_btn = QPushButton("?123")
|
||||||
|
self.sym_btn.setStyleSheet(CTRL_KEY_STYLE)
|
||||||
|
self.sym_btn.clicked.connect(self._toggle_symbol)
|
||||||
|
ctrl_layout.addWidget(self.sym_btn)
|
||||||
|
|
||||||
|
backspace_btn = QPushButton("⌫")
|
||||||
|
backspace_btn.setStyleSheet(CTRL_KEY_STYLE)
|
||||||
|
backspace_btn.clicked.connect(lambda: self._send_key("\b"))
|
||||||
|
ctrl_layout.addWidget(backspace_btn)
|
||||||
|
|
||||||
|
ctrl_layout.addStretch()
|
||||||
|
|
||||||
|
enter_btn = QPushButton("↵ 回车")
|
||||||
|
enter_btn.setStyleSheet(CTRL_KEY_STYLE)
|
||||||
|
enter_btn.clicked.connect(lambda: self._send_key("\n"))
|
||||||
|
ctrl_layout.addWidget(enter_btn)
|
||||||
|
|
||||||
|
layout.addLayout(ctrl_layout)
|
||||||
|
|
||||||
|
# ── 空格行 ──
|
||||||
|
space_layout = QHBoxLayout()
|
||||||
|
space_layout.setSpacing(4)
|
||||||
|
space_layout.addStretch()
|
||||||
|
|
||||||
|
self.space_btn = QPushButton(" ") # full-width space as placeholder
|
||||||
|
self.space_btn.setStyleSheet(SPACE_STYLE)
|
||||||
|
self.space_btn.clicked.connect(lambda: self._send_key(" "))
|
||||||
|
space_layout.addWidget(self.space_btn)
|
||||||
|
|
||||||
|
space_layout.addStretch()
|
||||||
|
layout.addLayout(space_layout)
|
||||||
|
|
||||||
|
outer_layout = QVBoxLayout(self)
|
||||||
|
outer_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
outer_layout.addWidget(outer)
|
||||||
|
|
||||||
|
self.setFixedWidth(680)
|
||||||
|
|
||||||
|
# ── 按键工厂 ──
|
||||||
|
|
||||||
|
def _make_key(self, text):
|
||||||
|
btn = QPushButton(text)
|
||||||
|
btn.setStyleSheet(KEY_STYLE)
|
||||||
|
btn.clicked.connect(lambda checked, t=text: self._send_key(t))
|
||||||
|
return btn
|
||||||
|
|
||||||
|
# ── 按键发送 ──
|
||||||
|
|
||||||
|
def _send_key(self, text):
|
||||||
|
"""发送按键到目标控件"""
|
||||||
|
if text == "\b":
|
||||||
|
self._backspace()
|
||||||
|
return
|
||||||
|
if self._symbol_mode:
|
||||||
|
self._toggle_symbol()
|
||||||
|
|
||||||
|
widget = self._get_target()
|
||||||
|
if widget is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(widget, QLineEdit):
|
||||||
|
cursor = widget.cursorPosition()
|
||||||
|
current = widget.text()
|
||||||
|
new_text = current[:cursor] + text + current[cursor:]
|
||||||
|
widget.setText(new_text)
|
||||||
|
widget.setCursorPosition(cursor + len(text))
|
||||||
|
elif isinstance(widget, QTextEdit):
|
||||||
|
tc = widget.textCursor()
|
||||||
|
tc.insertText(text)
|
||||||
|
widget.setTextCursor(tc)
|
||||||
|
|
||||||
|
def _backspace(self):
|
||||||
|
widget = self._get_target()
|
||||||
|
if widget is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(widget, QLineEdit):
|
||||||
|
cursor = widget.cursorPosition()
|
||||||
|
current = widget.text()
|
||||||
|
if cursor > 0:
|
||||||
|
new_text = current[:cursor - 1] + current[cursor:]
|
||||||
|
widget.setText(new_text)
|
||||||
|
widget.setCursorPosition(cursor - 1)
|
||||||
|
elif isinstance(widget, QTextEdit):
|
||||||
|
tc = widget.textCursor()
|
||||||
|
if not tc.hasSelection():
|
||||||
|
pos = tc.position()
|
||||||
|
if pos > 0:
|
||||||
|
tc.setPosition(pos - 1)
|
||||||
|
tc.movePosition(QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor)
|
||||||
|
tc.removeSelectedText()
|
||||||
|
|
||||||
|
def _get_target(self):
|
||||||
|
"""获取当前有效的目标输入控件"""
|
||||||
|
if self._target_widget and self._target_widget.hasFocus():
|
||||||
|
return self._target_widget
|
||||||
|
w = QApplication.focusWidget()
|
||||||
|
if isinstance(w, (QLineEdit, QTextEdit)):
|
||||||
|
return w
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ── Shift / Caps / Symbol 切换 ──
|
||||||
|
|
||||||
|
def _toggle_shift(self):
|
||||||
|
self._shift_on = not self._shift_on
|
||||||
|
self._apply_shift_caps()
|
||||||
|
self.shift_btn.setStyleSheet(ACTIVE_CTRL_STYLE if self._shift_on else CTRL_KEY_STYLE)
|
||||||
|
|
||||||
|
def _toggle_caps(self):
|
||||||
|
self._caps_on = not self._caps_on
|
||||||
|
self._apply_shift_caps()
|
||||||
|
self.caps_btn.setStyleSheet(ACTIVE_CTRL_STYLE if self._caps_on else CTRL_KEY_STYLE)
|
||||||
|
|
||||||
|
def _toggle_symbol(self):
|
||||||
|
self._symbol_mode = not self._symbol_mode
|
||||||
|
if self._symbol_mode:
|
||||||
|
for btn, ch in zip(self.num_buttons, SYMBOL_ROW):
|
||||||
|
btn.setText(ch)
|
||||||
|
for btn, ch in zip(self.extra_buttons, EXTRA_SYMBOLS[1]):
|
||||||
|
btn.setText(ch)
|
||||||
|
self.sym_btn.setStyleSheet(ACTIVE_CTRL_STYLE)
|
||||||
|
self.sym_btn.setText("ABC")
|
||||||
|
else:
|
||||||
|
for btn, ch in zip(self.num_buttons, NUMBER_ROW):
|
||||||
|
btn.setText(ch)
|
||||||
|
for btn, ch in zip(self.extra_buttons, EXTRA_SYMBOLS[0]):
|
||||||
|
btn.setText(ch)
|
||||||
|
self.sym_btn.setStyleSheet(CTRL_KEY_STYLE)
|
||||||
|
self.sym_btn.setText("?123")
|
||||||
|
|
||||||
|
def _apply_shift_caps(self):
|
||||||
|
use_shift = self._shift_on != self._caps_on # XOR
|
||||||
|
rows = KEY_ROWS["shift"] if use_shift else KEY_ROWS["normal"]
|
||||||
|
flat = [ch for row in rows for ch in row]
|
||||||
|
for btn, ch in zip(self._letter_buttons, flat):
|
||||||
|
btn.setText(ch)
|
||||||
|
|
||||||
|
# ── 窗口拖拽 ──
|
||||||
|
|
||||||
|
def _title_mouse_press(self, event: QMouseEvent):
|
||||||
|
if event.button() == Qt.MouseButton.LeftButton:
|
||||||
|
self._drag_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
|
||||||
|
|
||||||
|
def _title_mouse_move(self, event: QMouseEvent):
|
||||||
|
if event.buttons() & Qt.MouseButton.LeftButton and self._drag_pos is not None:
|
||||||
|
self.move(event.globalPosition().toPoint() - self._drag_pos)
|
||||||
|
|
||||||
|
# ── 目标绑定 ──
|
||||||
|
|
||||||
|
def attach(self, widget):
|
||||||
|
"""绑定到一个输入控件,键盘输入将发送到此控件"""
|
||||||
|
self._target_widget = widget
|
||||||
|
|
||||||
|
def detach(self):
|
||||||
|
self._target_widget = None
|
||||||
|
|
||||||
|
def show_at(self, x, y):
|
||||||
|
"""在屏幕坐标 (x, y) 处显示"""
|
||||||
|
self.move(x, y)
|
||||||
|
self.show()
|
||||||
|
self.raise_()
|
||||||
|
|
||||||
|
def show_below(self, widget):
|
||||||
|
"""显示在指定控件下方"""
|
||||||
|
pos = widget.mapToGlobal(QPoint(0, widget.height() + 4))
|
||||||
|
screen = QApplication.primaryScreen().availableGeometry()
|
||||||
|
if pos.x() + self.width() > screen.width():
|
||||||
|
pos.setX(screen.width() - self.width() - 8)
|
||||||
|
self.show_at(pos.x(), pos.y())
|
||||||
@@ -89,6 +89,12 @@ class WifiManager:
|
|||||||
self._run_wpa_cli("save_config")
|
self._run_wpa_cli("save_config")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def connect_network_id(self, network_id):
|
||||||
|
"""通过 network_id 连接已保存的网络"""
|
||||||
|
ret1 = self._run_wpa_cli("select_network", str(network_id))
|
||||||
|
self._run_wpa_cli("save_config")
|
||||||
|
return bool(ret1)
|
||||||
|
|
||||||
def remove_network(self, network_id):
|
def remove_network(self, network_id):
|
||||||
"""删除某个已保存的网络"""
|
"""删除某个已保存的网络"""
|
||||||
self._run_wpa_cli("remove_network", str(network_id))
|
self._run_wpa_cli("remove_network", str(network_id))
|
||||||
|
|||||||
Reference in New Issue
Block a user