feat: 实现设置页设备管理界面 (#5)
This commit is contained in:
@@ -1,25 +1,92 @@
|
||||
"""设置页的事件绑定。
|
||||
|
||||
处理保存设置、测试 Admin 连接、测试设备连接等动作。
|
||||
|
||||
改动本文件前必读 `client/AGENTS.md`。以下几条最容易踩:
|
||||
|
||||
- **凭据(token、密码)不写进 SQLite 的 `app_settings`,也不写进日志。**
|
||||
存到 Windows 凭据管理器,见 `docs/client/06-quality-security.md` §7。
|
||||
`data/` 目录是明文的、设计上就是整个拷走的,拷一次等于泄露一次。
|
||||
- “测试连接”会联网、会连手机,**必须走后台线程**,
|
||||
用 `QObject` + `moveToThread`(模板见 `docs/client/02-architecture.md` §5.1)。
|
||||
在主线程里直接连,界面会卡住好几秒。
|
||||
- 非敏感设置存 `app_settings` 表,键名沿用 `<分组>.<名称>` 的写法,
|
||||
清单见 `docs/client/03-data-model.md` §6。
|
||||
- 涉及路径的设置(日志目录、Artifact 目录)一律基于 `data_dir()`,
|
||||
**不要硬编码,也不要用 `os.getcwd()`**——打包成 exe 后会失效,
|
||||
见 `docs/client/03-data-model.md` §2.2。
|
||||
- 保存前要校验:数量上限、价格偏差、轮询周期这些填错了会直接影响
|
||||
采购安全,不能存进去再说。
|
||||
- 测试结果用 `InfoBar` 反馈,失败时说清“哪一步失败、下一步做什么”,
|
||||
不要把原始异常堆栈贴到界面上。
|
||||
|
||||
注意:真实下单开关属于红线,默认关闭,不得在这里做成
|
||||
一个随手能打开的普通开关,见根目录 `AGENTS.md` 红线第 2 条。
|
||||
本文件把界面操作转换为轻量信号,不直接执行 ADB、SQLite 或硬件信息读取。
|
||||
后续应用服务应在后台线程处理阻塞操作,再通过页面更新方法返回结果。
|
||||
"""
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
|
||||
|
||||
|
||||
class SettingsPageEventBinder(QObject):
|
||||
"""绑定设备管理控件,并向应用层发出稳定事件。"""
|
||||
|
||||
searchRequested = pyqtSignal()
|
||||
connectRequested = pyqtSignal(str)
|
||||
saveRequested = pyqtSignal(str, str)
|
||||
deleteRequested = pyqtSignal(str)
|
||||
|
||||
def __init__(self, page):
|
||||
super().__init__(page)
|
||||
self._page = page
|
||||
self._busy = False
|
||||
|
||||
page.searchButton.clicked.connect(self._request_search)
|
||||
page.connectButton.clicked.connect(self._request_connect)
|
||||
page.saveButton.clicked.connect(self._request_save)
|
||||
page.deleteButton.clicked.connect(self._request_delete)
|
||||
page.addressInput.textChanged.connect(self._sync_button_state)
|
||||
page.deviceTableModel.checkedDeviceChanged.connect(self._sync_button_state)
|
||||
self._sync_button_state()
|
||||
|
||||
@pyqtSlot()
|
||||
def _request_search(self) -> None:
|
||||
if self._busy:
|
||||
return
|
||||
self.searchRequested.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
def _request_connect(self) -> None:
|
||||
if self._busy:
|
||||
return
|
||||
|
||||
address = self._page.addressInput.text().strip()
|
||||
if not address:
|
||||
self._page.deviceStatusLabel.setText(
|
||||
"连接失败:请先填写无线调试地址,例如 192.168.0.173:5555"
|
||||
)
|
||||
self._page.addressInput.setFocus()
|
||||
return
|
||||
self.connectRequested.emit(address)
|
||||
|
||||
@pyqtSlot()
|
||||
def _request_save(self) -> None:
|
||||
if self._busy:
|
||||
return
|
||||
|
||||
serial = self._page.deviceTableModel.checked_serial
|
||||
if not serial:
|
||||
self._page.deviceStatusLabel.setText("保存失败:请先勾选一台已连接设备")
|
||||
self._page.deviceTable.setFocus()
|
||||
return
|
||||
|
||||
device_name = self._page.deviceNameInput.text().strip()
|
||||
self.saveRequested.emit(device_name, serial)
|
||||
|
||||
@pyqtSlot()
|
||||
def _request_delete(self) -> None:
|
||||
if self._busy:
|
||||
return
|
||||
|
||||
serial = self._page.deviceTableModel.checked_serial
|
||||
if not serial:
|
||||
self._page.deviceStatusLabel.setText("删除失败:请先勾选一台设备")
|
||||
self._page.deviceTable.setFocus()
|
||||
return
|
||||
self.deleteRequested.emit(serial)
|
||||
|
||||
@pyqtSlot()
|
||||
def _sync_button_state(self, *_args) -> None:
|
||||
serial = self._page.deviceTableModel.checked_serial
|
||||
has_address = bool(self._page.addressInput.text().strip())
|
||||
self._page.searchButton.setEnabled(not self._busy)
|
||||
self._page.connectButton.setEnabled(not self._busy and has_address)
|
||||
self._page.saveButton.setEnabled(not self._busy and bool(serial))
|
||||
self._page.deleteButton.setEnabled(not self._busy and bool(serial))
|
||||
|
||||
def set_busy(self, busy: bool, message: str = "") -> None:
|
||||
"""切换界面忙碌状态,防止用户重复提交设备命令。"""
|
||||
|
||||
self._busy = busy
|
||||
self._sync_button_state()
|
||||
if message:
|
||||
self._page.deviceStatusLabel.setText(message)
|
||||
|
||||
Reference in New Issue
Block a user