Files
cmshoppe/app/gui/__init__.py
T

150 lines
5.3 KiB
Python

"""PySide6 GUI package entry point."""
from __future__ import annotations
import os
import sys
import webbrowser
from .. import appconfig, chrome, diagnostics, update_check
from ..version import APP_NAME, display_name
from . import widgets as _widgets
from .widgets import *
QT_IMPORT_ERROR = _widgets.QT_IMPORT_ERROR
QMessageBox = _widgets._RawQMessageBox
run_worker = _widgets._raw_run_worker if QT_IMPORT_ERROR is None else _widgets.run_worker
if QT_IMPORT_ERROR is None:
from .models import ApplyTaskTableModel, GenerateTaskTableModel, TaskTableModel
from .workers import (
AccountLoginCheckWorker,
AIModelTestWorker,
CMHubSettingsWorker,
ApplyWorker,
CollectWorker,
GenerateWorker,
WriteBackWorker,
)
from .tabs.accounts import AccountDialog, AccountsTab
from .tabs.apply import ApplyTab
from .tabs.collect import CollectTab
from .tabs.generate import GenerateTab
from .tabs.settings import SettingsTab
from .main_window import MainWindow
else:
class MainWindow(QMainWindow):
def __init__(self):
raise RuntimeError(f"{APP_NAME} GUI 无法启动:当前 Python 环境未安装 PySide6。")
def _ensure_offscreen_for_headless_tests():
if "PYTEST_CURRENT_TEST" in os.environ and "QT_QPA_PLATFORM" not in os.environ:
os.environ["QT_QPA_PLATFORM"] = "offscreen"
def _write_update_check_diagnostic(message, *, result=None, exc=None):
payload = None
if result is not None:
payload = {
"checked": result.checked,
"forced": result.forced,
"current_version": result.current_version,
"latest_version": result.latest_version,
"min_supported_version": result.min_supported_version,
"download_url": result.download_url,
"error": result.error,
}
try:
diagnostics.write_diagnostic_log(
message,
level="WARNING",
step="startup_update_check",
payload=payload,
exc=exc,
)
except Exception:
# 版本检查第一版必须失败放行,诊断日志不可写也不能阻断启动。
pass
def _forced_update_details(result):
online_version = result.latest_version or result.min_supported_version or "未知"
lines = [
f"当前版本:{result.current_version}",
f"线上版本:{online_version}",
]
if result.min_supported_version:
lines.append(f"最低支持版本:{result.min_supported_version}")
if result.message:
lines.append(f"升级说明:{result.message}")
if result.download_url:
lines.append("请下载新版,关闭程序后覆盖程序文件和 _internal/,保留 data/ 目录。")
else:
lines.append("版本接口未提供下载地址,请联系管理员获取新版后再使用。")
return "\n".join(lines)
def _show_forced_update_dialog(result, *, parent=None, opener=None) -> bool:
opener = opener or webbrowser.open
box = QMessageBox(parent)
box.setIcon(QMessageBox.Warning)
box.setWindowTitle("必须升级")
box.setText("当前版本已不能继续使用,请先升级到新版。")
box.setInformativeText(_forced_update_details(result))
download_button = box.addButton("下载新版", QMessageBox.AcceptRole)
exit_button = box.addButton("退出程序", QMessageBox.RejectRole)
if not result.download_url and hasattr(download_button, "setEnabled"):
download_button.setEnabled(False)
box.setDefaultButton(download_button if result.download_url else exit_button)
box.exec()
if box.clickedButton() is download_button and result.download_url:
opener(result.download_url)
return False
def _run_startup_update_gate(*, checker=None, opener=None) -> bool:
try:
result = (checker or update_check.check_for_update)()
except Exception as exc:
_write_update_check_diagnostic("启动版本检查异常,已允许继续使用", exc=exc)
return True
if result.error:
_write_update_check_diagnostic("启动版本检查失败,已允许继续使用", result=result)
if result.forced:
return _show_forced_update_dialog(result, opener=opener)
return True
def main() -> int:
if QT_IMPORT_ERROR is not None:
print(f"{APP_NAME} GUI 无法启动:当前 Python 环境未安装 PySide6。")
return 1
_ensure_offscreen_for_headless_tests()
app = QApplication.instance() or QApplication(sys.argv)
try:
appconfig.prepare_data_dir()
except appconfig.DataMigrationConflictError as exc:
QMessageBox.critical(None, "数据迁移冲突", str(exc))
return 1
except appconfig.DataDirectoryWriteError as exc:
QMessageBox.critical(None, "数据目录不可写", str(exc))
return 1
except appconfig.ConfigError as exc:
QMessageBox.critical(None, "启动配置错误", str(exc))
return 1
if not _run_startup_update_gate():
return 1
try:
startup = chrome.ensure_configured_chrome_path()
except (OSError, appconfig.ConfigError) as exc:
QMessageBox.critical(None, "启动配置错误", str(exc))
return 1
window = MainWindow(
config=startup["config"],
startup_status=startup["message"],
)
window.show()
return app.exec()