Coverage for paintmaskanimator/__main__.py: 0%
78 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 04:50 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 04:50 +0000
1"""Application entry point."""
2from .dependency_check import require_runtime_dependencies
4# Must run before anything pulls in Qt, so a missing PySide6/numpy produces a
5# readable dialog instead of a traceback that vanishes with the console.
6require_runtime_dependencies()
8import sys # noqa: E402
9from pathlib import Path # noqa: E402
11from PySide6.QtCore import QTimer # noqa: E402
12from PySide6.QtGui import QIcon # noqa: E402
13from PySide6.QtWidgets import QApplication, QMessageBox # noqa: E402
15from . import i18n # noqa: E402
16from .i18n import tr # noqa: E402
17from .constants import APP_DISPLAY_NAME, APP_VERSION # noqa: E402
18from .main_window import MainWindow # noqa: E402
21def _show_unhandled_exception(exc_type, exc_value, exc_tb):
22 import traceback
23 details = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
24 # Prefer the per-user config dir (always writable, unlike an installed
25 # Program Files app dir); fall back to next to the executable.
26 log_path = None
27 try:
28 from . import config
29 import time
30 log_path = config.config_dir() / "crash.log"
31 with open(log_path, "a", encoding="utf-8") as handle:
32 handle.write(f"\n===== {time.strftime('%Y-%m-%d %H:%M:%S')} =====\n")
33 handle.write(details)
34 except Exception:
35 try:
36 app_file = Path(sys.executable) if getattr(sys, "frozen", False) else Path(__file__)
37 log_path = app_file.with_name("oekaki_animator_crash.log")
38 log_path.write_text(details, encoding="utf-8")
39 except Exception:
40 log_path = None
41 text = tr("起動中または実行中にエラーが発生しました。")
42 if log_path:
43 text += tr("\n\n詳細を保存しました:\n{path}").format(path=log_path)
44 text += f"\n\n{exc_value}"
45 app = QApplication.instance()
46 # Never open another modal error dialog while Qt is shutting down. Doing so
47 # can create an endless loop of errors from already-destroyed widgets.
48 if app is not None and not app.closingDown():
49 try:
50 QMessageBox.critical(None, tr("{NAME} エラー").format(NAME=APP_DISPLAY_NAME), text)
51 except RuntimeError:
52 print(details)
53 else:
54 print(details)
57def _apply_startup_preferences():
58 """Apply the Preferences that must be in place before the window exists."""
59 from . import constants, preferences
60 from .preferences_dialog import apply_undo_preferences
61 constants.CANVAS_WIDTH, constants.CANVAS_HEIGHT = preferences.new_canvas_size()
62 apply_undo_preferences()
65def main():
66 from .logging_setup import configure_logging
67 configure_logging()
68 sys.excepthook = _show_unhandled_exception
69 app = QApplication(sys.argv)
70 # Before anything else runs the event loop: macOS sends the double-clicked
71 # file as an event right after launch.
72 from .file_open import ProjectFileOpener, openable_paths_from_argv
73 file_opener = ProjectFileOpener(app, openable_paths_from_argv(sys.argv))
74 app.setApplicationName(APP_DISPLAY_NAME)
75 app.setApplicationVersion(APP_VERSION)
76 # ウィンドウ・Dock・タスクバーのアイコン。配布版は実行ファイル側にも
77 # 同じアイコンを埋め込む(scripts/make_app_icons.py)。
78 icon_path = Path(__file__).with_name("assets") / "app_icon.png"
79 if icon_path.exists():
80 app.setWindowIcon(QIcon(str(icon_path)))
81 # Before any widget is built: Qt resolves tr() at call time, but menus and
82 # dialogs constructed earlier would keep the source-language strings.
83 i18n.install_preferred(app)
84 from . import theme, tooltip
85 theme.apply_theme(app)
86 tooltip.install(app)
87 _apply_startup_preferences()
88 window = MainWindow()
89 window.show()
90 file_opener.attach(window)
91 QTimer.singleShot(0, window.fit_canvas)
92 # 新しい版があればメニューバーで知らせる(起動の少しあとに裏で確かめる)。
93 window.update_notice.start()
94 return app.exec()
97if __name__ == '__main__':
98 try:
99 raise SystemExit(main())
100 except SystemExit:
101 raise
102 except Exception:
103 exc_type, exc_value, exc_tb = sys.exc_info()
104 if exc_type is not None and exc_value is not None:
105 sys.excepthook(exc_type, exc_value, exc_tb)
106 raise SystemExit(1) from None