优化wifi设置页面,添加悬浮键盘

This commit is contained in:
2026-05-10 01:39:16 +08:00
parent 65c342219d
commit 649677f564
5 changed files with 1380 additions and 18 deletions

895
pages/setting_page.py Normal file
View 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)