From bff42d55dbfc6a68a9a4ca544cc5ed8922edcfc6 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sat, 8 Aug 2020 14:59:13 +0200 Subject: [PATCH 01/11] Add main_window_will_require_reset hook --- qt/aqt/gui_hooks.py | 35 +++++++++++++++++++++++++++++++++++ qt/tools/genhooks_gui.py | 11 +++++++++++ 2 files changed, 46 insertions(+) diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index 71aec7506..3cc7673ca 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -1737,6 +1737,41 @@ class _MainWindowDidInitHook: main_window_did_init = _MainWindowDidInitHook() +class _MainWindowWillRequireResetFilter: + """Executed before the main window will require a reset + + This hook can be used to change the behavior of the main window, + when other dialogs, like the AddCards or Browser, require a reset + from the main window. + """ + + _hooks: List[Callable[[bool], bool]] = [] + + def append(self, cb: Callable[[bool], bool]) -> None: + """(will_reset: bool)""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[bool], bool]) -> None: + if cb in self._hooks: + self._hooks.remove(cb) + + def count(self) -> int: + return len(self._hooks) + + def __call__(self, will_reset: bool) -> bool: + for filter in self._hooks: + try: + will_reset = filter(will_reset) + except: + # if the hook fails, remove it + self._hooks.remove(filter) + raise + return will_reset + + +main_window_will_require_reset = _MainWindowWillRequireResetFilter() + + class _MediaSyncDidProgressHook: _hooks: List[Callable[["aqt.mediasync.LogEntryWithTime"], None]] = [] diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index a9d5b1096..5c7264656 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -440,6 +440,17 @@ hooks = [ is thus suitable for single-shot subscribers. """, ), + Hook( + name="main_window_will_require_reset", + args=["will_reset: bool"], + return_type="bool", + doc="""Executed before the main window will require a reset + + This hook can be used to change the behavior of the main window, + when other dialogs, like the AddCards or Browser, require a reset + from the main window. + """, + ), Hook(name="backup_did_complete"), Hook( name="profile_did_open", From 31323719bcf3ef54c53bcc94b225ca7c6ac58c6b Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sat, 8 Aug 2020 15:08:30 +0200 Subject: [PATCH 02/11] Insert gui_hook main_window_will_require_reset * into AnkiQt.requireReset method --- qt/aqt/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/aqt/main.py b/qt/aqt/main.py index e1d680a4f..6861eaeec 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -688,7 +688,7 @@ from the profile screen." "Signal queue needs to be rebuilt when edits are finished or by user." self.autosave() self.resetModal = modal - if self.interactiveState(): + if gui_hooks.main_window_will_require_reset(self.interactiveState()): self.moveToState("resetRequired") def interactiveState(self): From dd515e65e76e69a0adbd82589c2edbf80eea54e4 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sun, 9 Aug 2020 13:10:14 +0200 Subject: [PATCH 03/11] Add new use of requireReset --- qt/aqt/addcards.py | 2 +- qt/aqt/browser.py | 16 ++++++++-------- qt/aqt/editcurrent.py | 2 +- qt/aqt/editor.py | 2 +- qt/aqt/main.py | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 0194c3296..0f602cda8 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -177,8 +177,8 @@ class AddCards(QDialog): self.mw.col.add_note(note, self.deckChooser.selectedId()) self.mw.col.clearUndo() self.addHistory(note) - self.mw.requireReset() self.previousNote = note + self.mw.requireReset('addCardsAddNote', self) gui_hooks.add_cards_did_add_note(note) return note diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index f68ad3670..21a77a918 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -1564,7 +1564,7 @@ where id in %s""" newRow = max(newRow, 0) self.model.focusedCard = self.model.cards[newRow] self.model.endReset() - self.mw.requireReset() + self.mw.requireReset('browserDeleteNote', self) tooltip( ngettext("%d note deleted.", "%d notes deleted.", len(nids)) % len(nids) ) @@ -1616,7 +1616,7 @@ update cards set usn=?, mod=?, did=? where id in """ did, ) self.model.endReset() - self.mw.requireReset() + self.mw.requireReset('browserSetDeck', self) # Tags ###################################################################### @@ -1642,7 +1642,7 @@ update cards set usn=?, mod=?, did=? where id in """ self.model.beginReset() func(self.selectedNotes(), tags) self.model.endReset() - self.mw.requireReset() + self.mw.requireReset('browserAddTags', self) def deleteTags(self, tags=None, label=None): if label is None: @@ -1675,7 +1675,7 @@ update cards set usn=?, mod=?, did=? where id in """ else: self.col.sched.unsuspendCards(c) self.model.reset() - self.mw.requireReset() + self.mw.requireReset('browserSuspend', self) # Exporting ###################################################################### @@ -1763,7 +1763,7 @@ update cards set usn=?, mod=?, did=? where id in """ shift=frm.shift.isChecked(), ) self.search() - self.mw.requireReset() + self.mw.requireReset('browserReposition', self) self.model.endReset() # Rescheduling @@ -1789,7 +1789,7 @@ update cards set usn=?, mod=?, did=? where id in """ fmax = max(fmin, fmax) self.col.sched.reschedCards(self.selectedCards(), fmin, fmax) self.search() - self.mw.requireReset() + self.mw.requireReset('browserReschedule', self) self.model.endReset() # Edit: selection @@ -1923,7 +1923,7 @@ update cards set usn=?, mod=?, did=? where id in """ def on_done(fut): self.search() - self.mw.requireReset() + self.mw.requireReset('browserFindReplace', self) self.model.endReset() total = len(nids) @@ -2025,7 +2025,7 @@ update cards set usn=?, mod=?, did=? where id in """ self.col.tags.bulkAdd(list(nids), _("duplicate")) self.mw.progress.finish() self.model.endReset() - self.mw.requireReset() + self.mw.requireReset('browserTagDupes', self) tooltip(_("Notes tagged.")) def dupeLinkClicked(self, link): diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index e612f6c65..2452652b3 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -27,7 +27,7 @@ class EditCurrent(QDialog): self.editor.setNote(self.mw.reviewer.card.note(), focusTo=0) restoreGeom(self, "editcurrent") gui_hooks.state_did_reset.append(self.onReset) - self.mw.requireReset() + self.mw.requireReset('editCurrentInit', self) self.show() # reset focus after open, taking care not to retain webview # pylint: disable=unnecessary-lambda diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 2f0e95c81..4e418bc4e 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -393,7 +393,7 @@ class Editor: if not self.addMode: self.note.flush() - self.mw.requireReset() + self.mw.requireReset('editorBridgeCmd', self) if type == "blur": self.currentField = None # run any filters diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 6861eaeec..62d626be4 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -684,11 +684,11 @@ from the profile screen." self.maybeEnableUndo() self.moveToState(self.state) - def requireReset(self, modal=False): + def requireReset(self, reason, context=None, modal=False): "Signal queue needs to be rebuilt when edits are finished or by user." self.autosave() self.resetModal = modal - if gui_hooks.main_window_will_require_reset(self.interactiveState()): + if gui_hooks.main_window_will_require_reset(self.interactiveState(), reason, context): self.moveToState("resetRequired") def interactiveState(self): From f1286a0ce3b1e095740f6ab9041de6ef6e419b14 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sun, 9 Aug 2020 13:22:23 +0200 Subject: [PATCH 04/11] Update signature of main_window_will_require_reset hook --- qt/aqt/gui_hooks.py | 12 ++++++------ qt/tools/genhooks_gui.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index 3cc7673ca..058ca59c3 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -1745,23 +1745,23 @@ class _MainWindowWillRequireResetFilter: from the main window. """ - _hooks: List[Callable[[bool], bool]] = [] + _hooks: List[Callable[[bool, str, Optional[Any]], bool]] = [] - def append(self, cb: Callable[[bool], bool]) -> None: - """(will_reset: bool)""" + def append(self, cb: Callable[[bool, str, Optional[Any]], bool]) -> None: + """(will_reset: bool, reason: str, context: Optional[Any])""" self._hooks.append(cb) - def remove(self, cb: Callable[[bool], bool]) -> None: + def remove(self, cb: Callable[[bool, str, Optional[Any]], bool]) -> None: if cb in self._hooks: self._hooks.remove(cb) def count(self) -> int: return len(self._hooks) - def __call__(self, will_reset: bool) -> bool: + def __call__(self, will_reset: bool, reason: str, context: Optional[Any]) -> bool: for filter in self._hooks: try: - will_reset = filter(will_reset) + will_reset = filter(will_reset, reason, context) except: # if the hook fails, remove it self._hooks.remove(filter) diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index 5c7264656..7fa2f7449 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -442,7 +442,7 @@ hooks = [ ), Hook( name="main_window_will_require_reset", - args=["will_reset: bool"], + args=["will_reset: bool", "reason: str", "context: Optional[Any]"], return_type="bool", doc="""Executed before the main window will require a reset From ccc56d435540ece6e2d91ee7a3f508d60fa20c16 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sun, 9 Aug 2020 13:51:26 +0200 Subject: [PATCH 05/11] Satisfy formatter --- qt/aqt/addcards.py | 2 +- qt/aqt/browser.py | 16 ++++++++-------- qt/aqt/editcurrent.py | 2 +- qt/aqt/editor.py | 2 +- qt/aqt/main.py | 4 +++- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 0f602cda8..23523e9d8 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -178,7 +178,7 @@ class AddCards(QDialog): self.mw.col.clearUndo() self.addHistory(note) self.previousNote = note - self.mw.requireReset('addCardsAddNote', self) + self.mw.requireReset("addCardsAddNote", self) gui_hooks.add_cards_did_add_note(note) return note diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index 21a77a918..d58767759 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -1564,7 +1564,7 @@ where id in %s""" newRow = max(newRow, 0) self.model.focusedCard = self.model.cards[newRow] self.model.endReset() - self.mw.requireReset('browserDeleteNote', self) + self.mw.requireReset("browserDeleteNote", self) tooltip( ngettext("%d note deleted.", "%d notes deleted.", len(nids)) % len(nids) ) @@ -1616,7 +1616,7 @@ update cards set usn=?, mod=?, did=? where id in """ did, ) self.model.endReset() - self.mw.requireReset('browserSetDeck', self) + self.mw.requireReset("browserSetDeck", self) # Tags ###################################################################### @@ -1642,7 +1642,7 @@ update cards set usn=?, mod=?, did=? where id in """ self.model.beginReset() func(self.selectedNotes(), tags) self.model.endReset() - self.mw.requireReset('browserAddTags', self) + self.mw.requireReset("browserAddTags", self) def deleteTags(self, tags=None, label=None): if label is None: @@ -1675,7 +1675,7 @@ update cards set usn=?, mod=?, did=? where id in """ else: self.col.sched.unsuspendCards(c) self.model.reset() - self.mw.requireReset('browserSuspend', self) + self.mw.requireReset("browserSuspend", self) # Exporting ###################################################################### @@ -1763,7 +1763,7 @@ update cards set usn=?, mod=?, did=? where id in """ shift=frm.shift.isChecked(), ) self.search() - self.mw.requireReset('browserReposition', self) + self.mw.requireReset("browserReposition", self) self.model.endReset() # Rescheduling @@ -1789,7 +1789,7 @@ update cards set usn=?, mod=?, did=? where id in """ fmax = max(fmin, fmax) self.col.sched.reschedCards(self.selectedCards(), fmin, fmax) self.search() - self.mw.requireReset('browserReschedule', self) + self.mw.requireReset("browserReschedule", self) self.model.endReset() # Edit: selection @@ -1923,7 +1923,7 @@ update cards set usn=?, mod=?, did=? where id in """ def on_done(fut): self.search() - self.mw.requireReset('browserFindReplace', self) + self.mw.requireReset("browserFindReplace", self) self.model.endReset() total = len(nids) @@ -2025,7 +2025,7 @@ update cards set usn=?, mod=?, did=? where id in """ self.col.tags.bulkAdd(list(nids), _("duplicate")) self.mw.progress.finish() self.model.endReset() - self.mw.requireReset('browserTagDupes', self) + self.mw.requireReset("browserTagDupes", self) tooltip(_("Notes tagged.")) def dupeLinkClicked(self, link): diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index 2452652b3..3b96a846e 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -27,7 +27,7 @@ class EditCurrent(QDialog): self.editor.setNote(self.mw.reviewer.card.note(), focusTo=0) restoreGeom(self, "editcurrent") gui_hooks.state_did_reset.append(self.onReset) - self.mw.requireReset('editCurrentInit', self) + self.mw.requireReset("editCurrentInit", self) self.show() # reset focus after open, taking care not to retain webview # pylint: disable=unnecessary-lambda diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 4e418bc4e..a26301d67 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -393,7 +393,7 @@ class Editor: if not self.addMode: self.note.flush() - self.mw.requireReset('editorBridgeCmd', self) + self.mw.requireReset("editorBridgeCmd", self) if type == "blur": self.currentField = None # run any filters diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 62d626be4..a8c650dcd 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -688,7 +688,9 @@ from the profile screen." "Signal queue needs to be rebuilt when edits are finished or by user." self.autosave() self.resetModal = modal - if gui_hooks.main_window_will_require_reset(self.interactiveState(), reason, context): + if gui_hooks.main_window_will_require_reset( + self.interactiveState(), reason, context + ): self.moveToState("resetRequired") def interactiveState(self): From db0a18106fe739e247f6a22afe5f2fd1f6b4008f Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Wed, 12 Aug 2020 13:53:21 +0200 Subject: [PATCH 06/11] Reorder + make all parameters optional --- qt/aqt/addcards.py | 2 +- qt/aqt/browser.py | 16 ++++++++-------- qt/aqt/editcurrent.py | 2 +- qt/aqt/editor.py | 2 +- qt/aqt/main.py | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 23523e9d8..5a226fa3e 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -178,7 +178,7 @@ class AddCards(QDialog): self.mw.col.clearUndo() self.addHistory(note) self.previousNote = note - self.mw.requireReset("addCardsAddNote", self) + self.mw.requireReset(reason="addCardsAddNote", context=self) gui_hooks.add_cards_did_add_note(note) return note diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index d58767759..a97a487a4 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -1564,7 +1564,7 @@ where id in %s""" newRow = max(newRow, 0) self.model.focusedCard = self.model.cards[newRow] self.model.endReset() - self.mw.requireReset("browserDeleteNote", self) + self.mw.requireReset(reason="browserDeleteNote", context=self) tooltip( ngettext("%d note deleted.", "%d notes deleted.", len(nids)) % len(nids) ) @@ -1616,7 +1616,7 @@ update cards set usn=?, mod=?, did=? where id in """ did, ) self.model.endReset() - self.mw.requireReset("browserSetDeck", self) + self.mw.requireReset(reason="browserSetDeck", context=self) # Tags ###################################################################### @@ -1642,7 +1642,7 @@ update cards set usn=?, mod=?, did=? where id in """ self.model.beginReset() func(self.selectedNotes(), tags) self.model.endReset() - self.mw.requireReset("browserAddTags", self) + self.mw.requireReset(reason="browserAddTags", context=self) def deleteTags(self, tags=None, label=None): if label is None: @@ -1675,7 +1675,7 @@ update cards set usn=?, mod=?, did=? where id in """ else: self.col.sched.unsuspendCards(c) self.model.reset() - self.mw.requireReset("browserSuspend", self) + self.mw.requireReset(reason="browserSuspend", context=self) # Exporting ###################################################################### @@ -1763,7 +1763,7 @@ update cards set usn=?, mod=?, did=? where id in """ shift=frm.shift.isChecked(), ) self.search() - self.mw.requireReset("browserReposition", self) + self.mw.requireReset(reason="browserReposition", context=self) self.model.endReset() # Rescheduling @@ -1789,7 +1789,7 @@ update cards set usn=?, mod=?, did=? where id in """ fmax = max(fmin, fmax) self.col.sched.reschedCards(self.selectedCards(), fmin, fmax) self.search() - self.mw.requireReset("browserReschedule", self) + self.mw.requireReset(reason="browserReschedule", context=self) self.model.endReset() # Edit: selection @@ -1923,7 +1923,7 @@ update cards set usn=?, mod=?, did=? where id in """ def on_done(fut): self.search() - self.mw.requireReset("browserFindReplace", self) + self.mw.requireReset(reason="browserFindReplace", context=self) self.model.endReset() total = len(nids) @@ -2025,7 +2025,7 @@ update cards set usn=?, mod=?, did=? where id in """ self.col.tags.bulkAdd(list(nids), _("duplicate")) self.mw.progress.finish() self.model.endReset() - self.mw.requireReset("browserTagDupes", self) + self.mw.requireReset(reason="browserTagDupes", context=self) tooltip(_("Notes tagged.")) def dupeLinkClicked(self, link): diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index 3b96a846e..7b5a290b0 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -27,7 +27,7 @@ class EditCurrent(QDialog): self.editor.setNote(self.mw.reviewer.card.note(), focusTo=0) restoreGeom(self, "editcurrent") gui_hooks.state_did_reset.append(self.onReset) - self.mw.requireReset("editCurrentInit", self) + self.mw.requireReset(reason="editCurrentInit", context=self) self.show() # reset focus after open, taking care not to retain webview # pylint: disable=unnecessary-lambda diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index a26301d67..86e53c4c5 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -393,7 +393,7 @@ class Editor: if not self.addMode: self.note.flush() - self.mw.requireReset("editorBridgeCmd", self) + self.mw.requireReset(reason="editorBridgeCmd", context=self) if type == "blur": self.currentField = None # run any filters diff --git a/qt/aqt/main.py b/qt/aqt/main.py index a8c650dcd..33660684e 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -684,7 +684,7 @@ from the profile screen." self.maybeEnableUndo() self.moveToState(self.state) - def requireReset(self, reason, context=None, modal=False): + def requireReset(self, modal=False, reason="unknown", context=None): "Signal queue needs to be rebuilt when edits are finished or by user." self.autosave() self.resetModal = modal From 97a4a0ef34387c95d98993ae199be38c89021291 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sun, 16 Aug 2020 18:33:33 +0200 Subject: [PATCH 07/11] Rename to should_require_reset --- qt/aqt/gui_hooks.py | 32 ++++++++++++++++++++++++-------- qt/aqt/main.py | 7 ++++++- qt/tools/genhooks_gui.py | 13 ++++++++++--- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index 058ca59c3..1c35d9d5f 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -1737,28 +1737,44 @@ class _MainWindowDidInitHook: main_window_did_init = _MainWindowDidInitHook() -class _MainWindowWillRequireResetFilter: +class _MainWindowShouldRequireResetFilter: """Executed before the main window will require a reset - + This hook can be used to change the behavior of the main window, when other dialogs, like the AddCards or Browser, require a reset from the main window. + If you decide to use this hook, make you sure you check the reason for the reset. + Some reasons require more attention than others, and skipping important ones might + put the main window into an invalid state (e.g. display a deleted note). """ - _hooks: List[Callable[[bool, str, Optional[Any]], bool]] = [] + _hooks: List[ + Callable[[bool, "Union[aqt.main.ResetReason, str]", Optional[Any]], bool] + ] = [] - def append(self, cb: Callable[[bool, str, Optional[Any]], bool]) -> None: - """(will_reset: bool, reason: str, context: Optional[Any])""" + def append( + self, + cb: Callable[[bool, "Union[aqt.main.ResetReason, str]", Optional[Any]], bool], + ) -> None: + """(will_reset: bool, reason: Union[aqt.main.ResetReason, str], context: Optional[Any])""" self._hooks.append(cb) - def remove(self, cb: Callable[[bool, str, Optional[Any]], bool]) -> None: + def remove( + self, + cb: Callable[[bool, "Union[aqt.main.ResetReason, str]", Optional[Any]], bool], + ) -> None: if cb in self._hooks: self._hooks.remove(cb) def count(self) -> int: return len(self._hooks) - def __call__(self, will_reset: bool, reason: str, context: Optional[Any]) -> bool: + def __call__( + self, + will_reset: bool, + reason: Union[aqt.main.ResetReason, str], + context: Optional[Any], + ) -> bool: for filter in self._hooks: try: will_reset = filter(will_reset, reason, context) @@ -1769,7 +1785,7 @@ class _MainWindowWillRequireResetFilter: return will_reset -main_window_will_require_reset = _MainWindowWillRequireResetFilter() +main_window_should_require_reset = _MainWindowShouldRequireResetFilter() class _MediaSyncDidProgressHook: diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 33660684e..f45035185 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -4,6 +4,7 @@ from __future__ import annotations +import enum import faulthandler import gc import os @@ -68,6 +69,10 @@ from aqt.utils import ( install_pylib_legacy() +class ResetReason(enum.Enum): + pass + + class ResetRequired: def __init__(self, mw: AnkiQt): self.mw = mw @@ -688,7 +693,7 @@ from the profile screen." "Signal queue needs to be rebuilt when edits are finished or by user." self.autosave() self.resetModal = modal - if gui_hooks.main_window_will_require_reset( + if gui_hooks.main_window_should_require_reset( self.interactiveState(), reason, context ): self.moveToState("resetRequired") diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index 7fa2f7449..ca00e4578 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -441,14 +441,21 @@ hooks = [ """, ), Hook( - name="main_window_will_require_reset", - args=["will_reset: bool", "reason: str", "context: Optional[Any]"], + name="main_window_should_require_reset", + args=[ + "will_reset: bool", + "reason: Union[aqt.main.ResetReason, str]", + "context: Optional[Any]", + ], return_type="bool", doc="""Executed before the main window will require a reset - + This hook can be used to change the behavior of the main window, when other dialogs, like the AddCards or Browser, require a reset from the main window. + If you decide to use this hook, make you sure you check the reason for the reset. + Some reasons require more attention than others, and skipping important ones might + put the main window into an invalid state (e.g. display a deleted note). """, ), Hook(name="backup_did_complete"), From 7f503895d7699dee984e887341265a005b7ba629 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sun, 16 Aug 2020 18:49:51 +0200 Subject: [PATCH 08/11] Create ResetReason enum --- qt/aqt/addcards.py | 3 ++- qt/aqt/browser.py | 17 +++++++++-------- qt/aqt/editcurrent.py | 3 ++- qt/aqt/editor.py | 3 ++- qt/aqt/main.py | 12 +++++++++++- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 5a226fa3e..7c0d171d2 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -12,6 +12,7 @@ from anki.lang import _ from anki.notes import Note from anki.utils import htmlToTextLine, isMac from aqt import AnkiQt, gui_hooks +from aqt.main import ResetReason from aqt.qt import * from aqt.sound import av_player from aqt.utils import ( @@ -178,7 +179,7 @@ class AddCards(QDialog): self.mw.col.clearUndo() self.addHistory(note) self.previousNote = note - self.mw.requireReset(reason="addCardsAddNote", context=self) + self.mw.requireReset(reason=ResetReason.AddCardsAddNote, context=self) gui_hooks.add_cards_did_add_note(note) return note diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index a97a487a4..9caa73f3e 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -26,6 +26,7 @@ from anki.utils import htmlToTextLine, ids2str, intTime, isMac, isWin from aqt import AnkiQt, gui_hooks from aqt.editor import Editor from aqt.exporting import ExportDialog +from aqt.main import ResetReason from aqt.previewer import BrowserPreviewer as PreviewDialog from aqt.previewer import Previewer from aqt.qt import * @@ -1564,7 +1565,7 @@ where id in %s""" newRow = max(newRow, 0) self.model.focusedCard = self.model.cards[newRow] self.model.endReset() - self.mw.requireReset(reason="browserDeleteNote", context=self) + self.mw.requireReset(reason=ResetReason.BrowserDeleteNote, context=self) tooltip( ngettext("%d note deleted.", "%d notes deleted.", len(nids)) % len(nids) ) @@ -1616,7 +1617,7 @@ update cards set usn=?, mod=?, did=? where id in """ did, ) self.model.endReset() - self.mw.requireReset(reason="browserSetDeck", context=self) + self.mw.requireReset(reason=ResetReason.BrowserSetDeck, context=self) # Tags ###################################################################### @@ -1642,7 +1643,7 @@ update cards set usn=?, mod=?, did=? where id in """ self.model.beginReset() func(self.selectedNotes(), tags) self.model.endReset() - self.mw.requireReset(reason="browserAddTags", context=self) + self.mw.requireReset(reason=ResetReason.BrowserAddTags, context=self) def deleteTags(self, tags=None, label=None): if label is None: @@ -1675,7 +1676,7 @@ update cards set usn=?, mod=?, did=? where id in """ else: self.col.sched.unsuspendCards(c) self.model.reset() - self.mw.requireReset(reason="browserSuspend", context=self) + self.mw.requireReset(reason=ResetReason.BrowserSuspend, context=self) # Exporting ###################################################################### @@ -1763,7 +1764,7 @@ update cards set usn=?, mod=?, did=? where id in """ shift=frm.shift.isChecked(), ) self.search() - self.mw.requireReset(reason="browserReposition", context=self) + self.mw.requireReset(reason=ResetReason.BrowserReposition, context=self) self.model.endReset() # Rescheduling @@ -1789,7 +1790,7 @@ update cards set usn=?, mod=?, did=? where id in """ fmax = max(fmin, fmax) self.col.sched.reschedCards(self.selectedCards(), fmin, fmax) self.search() - self.mw.requireReset(reason="browserReschedule", context=self) + self.mw.requireReset(reason=ResetReason.BrowserReschedule, context=self) self.model.endReset() # Edit: selection @@ -1923,7 +1924,7 @@ update cards set usn=?, mod=?, did=? where id in """ def on_done(fut): self.search() - self.mw.requireReset(reason="browserFindReplace", context=self) + self.mw.requireReset(reason=ResetReason.BrowserFindReplace, context=self) self.model.endReset() total = len(nids) @@ -2025,7 +2026,7 @@ update cards set usn=?, mod=?, did=? where id in """ self.col.tags.bulkAdd(list(nids), _("duplicate")) self.mw.progress.finish() self.model.endReset() - self.mw.requireReset(reason="browserTagDupes", context=self) + self.mw.requireReset(reason=ResetReason.BrowserTagDupes, context=self) tooltip(_("Notes tagged.")) def dupeLinkClicked(self, link): diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index 7b5a290b0..0d0e82ce9 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -5,6 +5,7 @@ import aqt.editor from anki.lang import _ from aqt import gui_hooks +from aqt.main import ResetReason from aqt.qt import * from aqt.utils import restoreGeom, saveGeom, tooltip @@ -27,7 +28,7 @@ class EditCurrent(QDialog): self.editor.setNote(self.mw.reviewer.card.note(), focusTo=0) restoreGeom(self, "editcurrent") gui_hooks.state_did_reset.append(self.onReset) - self.mw.requireReset(reason="editCurrentInit", context=self) + self.mw.requireReset(reason=ResetReason.EditCurrentInit, context=self) self.show() # reset focus after open, taking care not to retain webview # pylint: disable=unnecessary-lambda diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 86e53c4c5..86edfeb45 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -26,6 +26,7 @@ from anki.lang import _ from anki.notes import Note from anki.utils import checksum, isLin, isWin, namedtmp, stripHTMLMedia from aqt import AnkiQt, gui_hooks +from aqt.main import ResetReason from aqt.qt import * from aqt.sound import av_player, getAudio from aqt.theme import theme_manager @@ -393,7 +394,7 @@ class Editor: if not self.addMode: self.note.flush() - self.mw.requireReset(reason="editorBridgeCmd", context=self) + self.mw.requireReset(reason=ResetReason.EditorBridgeCmd, context=self) if type == "blur": self.currentField = None # run any filters diff --git a/qt/aqt/main.py b/qt/aqt/main.py index f45035185..86dc7b534 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -70,7 +70,17 @@ install_pylib_legacy() class ResetReason(enum.Enum): - pass + AddCardsAddNote = "addCardsAddNote" + EditCurrentInit = "editCurrentInit" + EditorBridgeCmd = "editorBridgeCmd" + BrowserDeleteNote = "browserDeleteNote" + BrowserSetDeck = "browserSetDeck" + BrowserAddTags = "browserAddTags" + BrowserSuspend = "browserSuspend" + BrowserReposition = "browserReposition" + BrowserReschedule = "browserReschedule" + BrowserFindReplace = "browserFindReplace" + BrowserTagDupes = "browserTagDupes" class ResetRequired: From 85d614548257f82c3e18efc4196b1499bb6a9071 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sun, 16 Aug 2020 19:11:11 +0200 Subject: [PATCH 09/11] Add Union import --- qt/aqt/gui_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index 1c35d9d5f..bfe30f97c 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -7,7 +7,7 @@ See pylib/anki/hooks.py from __future__ import annotations -from typing import Any, Callable, List, Optional, Tuple +from typing import Any, Callable, List, Optional, Union, Tuple import anki import aqt From e3d01de2ba5e3b572d881aced27e95a1671dbebc Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Sun, 16 Aug 2020 23:34:24 +0200 Subject: [PATCH 10/11] Reorder gui_hooks imports --- qt/aqt/gui_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index bfe30f97c..535f3c595 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -7,7 +7,7 @@ See pylib/anki/hooks.py from __future__ import annotations -from typing import Any, Callable, List, Optional, Union, Tuple +from typing import Any, Callable, List, Optional, Tuple, Union import anki import aqt From 4e43d27fb8790da5acfe20788585b058bc754c61 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Mon, 17 Aug 2020 12:50:40 +0200 Subject: [PATCH 11/11] Remove ResetReason.BrowserDeleteNote --- qt/aqt/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 58616de3f..73332af14 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -73,7 +73,6 @@ class ResetReason(enum.Enum): AddCardsAddNote = "addCardsAddNote" EditCurrentInit = "editCurrentInit" EditorBridgeCmd = "editorBridgeCmd" - BrowserDeleteNote = "browserDeleteNote" BrowserSetDeck = "browserSetDeck" BrowserAddTags = "browserAddTags" BrowserSuspend = "browserSuspend"