diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index 241bffd2e..d3efe8339 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -1475,6 +1475,38 @@ class _EmptyCardsWillShowHook: empty_cards_will_show = _EmptyCardsWillShowHook() +class _MainWindowDidInitHook: + """Executed after the main window is fully initialized + + A sample use case for this hook would be to delay actions until Anki objects + like the profile or collection are fully initialized. In contrast to + `profile_did_open`, this hook will only fire once per Anki session and + is thus suitable for single-shot subscribers. + """ + + _hooks: List[Callable[[], None]] = [] + + def append(self, cb: Callable[[], None]) -> None: + """()""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[], None]) -> None: + if cb in self._hooks: + self._hooks.remove(cb) + + def __call__(self) -> None: + for hook in self._hooks: + try: + hook() + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + + +main_window_did_init = _MainWindowDidInitHook() + + class _MediaSyncDidProgressHook: _hooks: List[Callable[["aqt.mediasync.LogEntryWithTime"], None]] = [] @@ -1624,6 +1656,13 @@ overview_will_render_content = _OverviewWillRenderContentHook() class _ProfileDidOpenHook: + """Executed whenever a user profile has been opened + + Please note that this hook will also be called on profile switches, so if you + are looking to simply delay an add-on action in a single-shot manner, + `main_window_did_init` is likely the more suitable choice. + """ + _hooks: List[Callable[[], None]] = [] def append(self, cb: Callable[[], None]) -> None: diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 80595d6a9..ca37a124e 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -118,7 +118,12 @@ class AnkiQt(QMainWindow): fn = self.setupProfileAfterWebviewsLoaded else: fn = self.setupProfile - self.progress.timer(10, fn, False, requiresCollection=False) + + def on_window_init(): + fn() + gui_hooks.main_window_did_init() + + self.progress.timer(10, on_window_init, False, requiresCollection=False) def setupUI(self) -> None: self.col = None diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index 8da8a7e69..bbf8f8463 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -400,8 +400,27 @@ hooks = [ ), # Main ################### + Hook( + name="main_window_did_init", + doc="""Executed after the main window is fully initialized + + A sample use case for this hook would be to delay actions until Anki objects + like the profile or collection are fully initialized. In contrast to + `profile_did_open`, this hook will only fire once per Anki session and + is thus suitable for single-shot subscribers. + """, + ), Hook(name="backup_did_complete"), - Hook(name="profile_did_open", legacy_hook="profileLoaded"), + Hook( + name="profile_did_open", + legacy_hook="profileLoaded", + doc="""Executed whenever a user profile has been opened + + Please note that this hook will also be called on profile switches, so if you + are looking to simply delay an add-on action in a single-shot manner, + `main_window_did_init` is likely the more suitable choice. + """, + ), Hook(name="profile_will_close", legacy_hook="unloadProfile"), Hook( name="collection_did_load",