"""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, ImageStudioDownloadOriginalWorker, ImageStudioExportWorker, ImageStudioGenerateJobsWorker, ImageStudioPullImagesWorker, WriteBackWorker, ) from .tabs.accounts import AccountDialog, AccountsTab from .tabs.apply import ApplyTab from .tabs.collect import CollectTab from .tabs.generate import GenerateTab from .tabs.image_studio import ImageStudioPreviewDialog, ImageStudioTab 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()