185 lines
6.1 KiB
Python
185 lines
6.1 KiB
Python
"""应用与主窗口装配。
|
|
|
|
本文件只负责把窗口、导航和页面拼起来,不写业务逻辑。
|
|
|
|
改动本文件前必读 `client/AGENTS.md`。以下几条最容易踩:
|
|
|
|
- 图标一律用 `FluentIcon`,**不得使用表情符号充当图标**。
|
|
- 不得在这里发起 Admin 请求、SQLite 长查询或 uiautomator2 调用,
|
|
这些会卡死 Qt 主线程。业务事件放到各页面的 `*_ui_event.py`。
|
|
- 页面的 `objectName` 必须唯一且稳定,改名会破坏导航。
|
|
- 用布局和伸缩项排版,**不要用固定坐标**。
|
|
- 不要用全局 QSS 覆盖 Fluent 控件;局部样式必须保留悬停、按下、
|
|
焦点和禁用四种状态。
|
|
- 需要文件路径时走 `data_dir()` / `app_dir()`,不要硬编码,
|
|
见 `docs/client/03-data-model.md` §2.2。
|
|
"""
|
|
|
|
import sys
|
|
|
|
from PyQt5.QtCore import Qt, QTimer
|
|
from PyQt5.QtWidgets import QApplication
|
|
from qfluentwidgets import (
|
|
FluentIcon as FIF,
|
|
FluentWindow,
|
|
MessageBox,
|
|
NavigationItemPosition,
|
|
Theme,
|
|
setTheme,
|
|
)
|
|
|
|
from .adb_runtime import BundledAdbError, configure_bundled_adb_environment
|
|
from .pdd_ui import PDDTaskPage
|
|
from .pdd_ui_event import PDDTaskPageEvent
|
|
from .pdd_u2_purchase_adapter import (
|
|
create_u2_live_purchase_adapter,
|
|
create_u2_purchase_adapter,
|
|
)
|
|
from .pdd_u2_purchase_reconcile_adapter import (
|
|
create_u2_purchase_reconcile_adapter,
|
|
)
|
|
from .settings_ui import SettingsPage
|
|
from .task_repository import TaskRepository
|
|
from .update_service import mark_current_version_healthy
|
|
|
|
|
|
class MainWindow(FluentWindow):
|
|
"""应用主窗口。"""
|
|
|
|
def __init__(
|
|
self,
|
|
task_repository=None,
|
|
settings_repository=None,
|
|
admin_gateway=None,
|
|
):
|
|
super().__init__()
|
|
|
|
self.pddTaskPage = PDDTaskPage(self)
|
|
self.settingsPage = SettingsPage(
|
|
self,
|
|
settings_repository=settings_repository,
|
|
admin_gateway=admin_gateway,
|
|
live_purchase_adapter_ready=True,
|
|
)
|
|
self.pddTaskPageEvent = PDDTaskPageEvent(
|
|
self.pddTaskPage,
|
|
task_repository or TaskRepository(),
|
|
self,
|
|
claim_gateway=admin_gateway,
|
|
settings_repository=settings_repository,
|
|
purchase_adapter_factory=create_u2_purchase_adapter,
|
|
live_purchase_adapter_factory=create_u2_live_purchase_adapter,
|
|
purchase_reconcile_factory=create_u2_purchase_reconcile_adapter,
|
|
)
|
|
|
|
self.pddTaskPage.openSettingsRequested.connect(
|
|
lambda: self.switchTo(self.settingsPage)
|
|
)
|
|
self.settingsPage.eventBinder.androidDeviceConfigurationChanged.connect(
|
|
self.pddTaskPageEvent.android_device_configuration_changed
|
|
)
|
|
self.settingsPage.eventBinder.adminBaseUrlChanged.connect(
|
|
self.pddTaskPageEvent.update_admin_base_url
|
|
)
|
|
self.addSubInterface(self.pddTaskPage, FIF.HOME, "pdd")
|
|
self.settingsNavigationItem = self.addSubInterface(
|
|
self.settingsPage,
|
|
FIF.SETTING,
|
|
"设置",
|
|
NavigationItemPosition.BOTTOM,
|
|
)
|
|
self._settings_navigation_text = self.settingsNavigationItem.text()
|
|
self._settings_navigation_light_color = (
|
|
self.settingsNavigationItem.lightTextColor
|
|
)
|
|
self._settings_navigation_dark_color = (
|
|
self.settingsNavigationItem.darkTextColor
|
|
)
|
|
self.settingsPage.eventBinder.updateEventBinder.updateAvailabilityChanged.connect(
|
|
self._set_update_navigation_reminder
|
|
)
|
|
|
|
self.setWindowTitle("采集采购工具")
|
|
self.setMinimumSize(820, 560)
|
|
self.resize(1040, 680)
|
|
self._moveToScreenCenter()
|
|
self.pddTaskPageEvent.load_initial_tasks()
|
|
|
|
def _set_update_navigation_reminder(
|
|
self,
|
|
available: bool,
|
|
current_version: str,
|
|
latest_version: str,
|
|
) -> None:
|
|
"""用颜色和文字共同提醒更新,不让提示只依赖颜色。"""
|
|
|
|
if available:
|
|
self.settingsNavigationItem.setText(
|
|
f"{self._settings_navigation_text}(有更新)"
|
|
)
|
|
self.settingsNavigationItem.setTextColor("#B45309", "#FBBF24")
|
|
self.settingsNavigationItem.setToolTip(
|
|
f"发现新版本 {latest_version},当前版本 {current_version}"
|
|
)
|
|
return
|
|
|
|
self.settingsNavigationItem.setText(self._settings_navigation_text)
|
|
self.settingsNavigationItem.setTextColor(
|
|
self._settings_navigation_light_color,
|
|
self._settings_navigation_dark_color,
|
|
)
|
|
self.settingsNavigationItem.setToolTip(self._settings_navigation_text)
|
|
|
|
def _moveToScreenCenter(self):
|
|
screen = QApplication.primaryScreen()
|
|
if screen is None:
|
|
return
|
|
|
|
frame = self.frameGeometry()
|
|
frame.moveCenter(screen.availableGeometry().center())
|
|
self.move(frame.topLeft())
|
|
|
|
|
|
def ui_main():
|
|
"""启动桌面应用。"""
|
|
|
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
|
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
|
|
|
|
app = QApplication(sys.argv)
|
|
setTheme(Theme.AUTO)
|
|
|
|
adb_error = ""
|
|
try:
|
|
configure_bundled_adb_environment()
|
|
except BundledAdbError as exc:
|
|
adb_error = str(exc)
|
|
|
|
window = MainWindow()
|
|
window.showMaximized()
|
|
if adb_error:
|
|
QTimer.singleShot(
|
|
0,
|
|
lambda: _show_adb_missing_dialog(window, adb_error),
|
|
)
|
|
try:
|
|
mark_current_version_healthy()
|
|
except OSError:
|
|
# 健康标记失败不能让界面崩溃;Launcher 会保留旧版本并在下次启动恢复。
|
|
pass
|
|
return app.exec_()
|
|
|
|
|
|
def _show_adb_missing_dialog(parent: MainWindow, message: str) -> None:
|
|
"""在主窗口中央提示内置 ADB 缺失,用户可用大按钮关闭。"""
|
|
|
|
dialog = MessageBox("Android 调试组件缺失", message, parent)
|
|
dialog.yesButton.setText("知道了")
|
|
dialog.yesButton.setMinimumSize(120, 40)
|
|
dialog.cancelButton.hide()
|
|
dialog.exec()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(ui_main())
|