Merge pull request #726 from hgiesel/resethook

Add main_window_will_require_reset gui_hook
This commit is contained in:
Damien Elmes 2020-08-17 21:01:02 +10:00 committed by GitHub
commit 56431872fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 13 deletions

View File

@ -12,6 +12,7 @@ from anki.lang import _
from anki.notes import Note from anki.notes import Note
from anki.utils import htmlToTextLine, isMac from anki.utils import htmlToTextLine, isMac
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.main import ResetReason
from aqt.qt import * from aqt.qt import *
from aqt.sound import av_player from aqt.sound import av_player
from aqt.utils import ( from aqt.utils import (
@ -177,8 +178,8 @@ class AddCards(QDialog):
self.mw.col.add_note(note, self.deckChooser.selectedId()) self.mw.col.add_note(note, self.deckChooser.selectedId())
self.mw.col.clearUndo() self.mw.col.clearUndo()
self.addHistory(note) self.addHistory(note)
self.mw.requireReset()
self.previousNote = note self.previousNote = note
self.mw.requireReset(reason=ResetReason.AddCardsAddNote, context=self)
gui_hooks.add_cards_did_add_note(note) gui_hooks.add_cards_did_add_note(note)
return note return note

View File

@ -26,6 +26,7 @@ from anki.utils import htmlToTextLine, ids2str, intTime, isMac, isWin
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.editor import Editor from aqt.editor import Editor
from aqt.exporting import ExportDialog from aqt.exporting import ExportDialog
from aqt.main import ResetReason
from aqt.previewer import BrowserPreviewer as PreviewDialog from aqt.previewer import BrowserPreviewer as PreviewDialog
from aqt.previewer import Previewer from aqt.previewer import Previewer
from aqt.qt import * from aqt.qt import *
@ -1616,7 +1617,7 @@ update cards set usn=?, mod=?, did=? where id in """
did, did,
) )
self.model.endReset() self.model.endReset()
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.BrowserSetDeck, context=self)
# Tags # Tags
###################################################################### ######################################################################
@ -1642,7 +1643,7 @@ update cards set usn=?, mod=?, did=? where id in """
self.model.beginReset() self.model.beginReset()
func(self.selectedNotes(), tags) func(self.selectedNotes(), tags)
self.model.endReset() self.model.endReset()
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.BrowserAddTags, context=self)
def deleteTags(self, tags=None, label=None): def deleteTags(self, tags=None, label=None):
if label is None: if label is None:
@ -1675,7 +1676,7 @@ update cards set usn=?, mod=?, did=? where id in """
else: else:
self.col.sched.unsuspendCards(c) self.col.sched.unsuspendCards(c)
self.model.reset() self.model.reset()
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.BrowserSuspend, context=self)
# Exporting # Exporting
###################################################################### ######################################################################
@ -1763,7 +1764,7 @@ update cards set usn=?, mod=?, did=? where id in """
shift=frm.shift.isChecked(), shift=frm.shift.isChecked(),
) )
self.search() self.search()
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.BrowserReposition, context=self)
self.model.endReset() self.model.endReset()
# Rescheduling # Rescheduling
@ -1789,7 +1790,7 @@ update cards set usn=?, mod=?, did=? where id in """
fmax = max(fmin, fmax) fmax = max(fmin, fmax)
self.col.sched.reschedCards(self.selectedCards(), fmin, fmax) self.col.sched.reschedCards(self.selectedCards(), fmin, fmax)
self.search() self.search()
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.BrowserReschedule, context=self)
self.model.endReset() self.model.endReset()
# Edit: selection # Edit: selection
@ -1923,7 +1924,7 @@ update cards set usn=?, mod=?, did=? where id in """
def on_done(fut): def on_done(fut):
self.search() self.search()
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.BrowserFindReplace, context=self)
self.model.endReset() self.model.endReset()
total = len(nids) total = len(nids)
@ -2025,7 +2026,7 @@ update cards set usn=?, mod=?, did=? where id in """
self.col.tags.bulkAdd(list(nids), _("duplicate")) self.col.tags.bulkAdd(list(nids), _("duplicate"))
self.mw.progress.finish() self.mw.progress.finish()
self.model.endReset() self.model.endReset()
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.BrowserTagDupes, context=self)
tooltip(_("Notes tagged.")) tooltip(_("Notes tagged."))
def dupeLinkClicked(self, link): def dupeLinkClicked(self, link):

View File

@ -5,6 +5,7 @@
import aqt.editor import aqt.editor
from anki.lang import _ from anki.lang import _
from aqt import gui_hooks from aqt import gui_hooks
from aqt.main import ResetReason
from aqt.qt import * from aqt.qt import *
from aqt.utils import restoreGeom, saveGeom, tooltip from aqt.utils import restoreGeom, saveGeom, tooltip
@ -27,7 +28,7 @@ class EditCurrent(QDialog):
self.editor.setNote(self.mw.reviewer.card.note(), focusTo=0) self.editor.setNote(self.mw.reviewer.card.note(), focusTo=0)
restoreGeom(self, "editcurrent") restoreGeom(self, "editcurrent")
gui_hooks.state_did_reset.append(self.onReset) gui_hooks.state_did_reset.append(self.onReset)
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.EditCurrentInit, context=self)
self.show() self.show()
# reset focus after open, taking care not to retain webview # reset focus after open, taking care not to retain webview
# pylint: disable=unnecessary-lambda # pylint: disable=unnecessary-lambda

View File

@ -26,6 +26,7 @@ from anki.lang import _
from anki.notes import Note from anki.notes import Note
from anki.utils import checksum, isLin, isWin, namedtmp, stripHTMLMedia from anki.utils import checksum, isLin, isWin, namedtmp, stripHTMLMedia
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.main import ResetReason
from aqt.qt import * from aqt.qt import *
from aqt.sound import av_player, getAudio from aqt.sound import av_player, getAudio
from aqt.theme import theme_manager from aqt.theme import theme_manager
@ -393,7 +394,7 @@ class Editor:
if not self.addMode: if not self.addMode:
self.note.flush() self.note.flush()
self.mw.requireReset() self.mw.requireReset(reason=ResetReason.EditorBridgeCmd, context=self)
if type == "blur": if type == "blur":
self.currentField = None self.currentField = None
# run any filters # run any filters

View File

@ -7,7 +7,7 @@ See pylib/anki/hooks.py
from __future__ import annotations from __future__ import annotations
from typing import Any, Callable, List, Optional, Tuple from typing import Any, Callable, List, Optional, Tuple, Union
import anki import anki
import aqt import aqt
@ -1737,6 +1737,57 @@ class _MainWindowDidInitHook:
main_window_did_init = _MainWindowDidInitHook() main_window_did_init = _MainWindowDidInitHook()
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, "Union[aqt.main.ResetReason, str]", Optional[Any]], bool]
] = []
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, "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: Union[aqt.main.ResetReason, str],
context: Optional[Any],
) -> bool:
for filter in self._hooks:
try:
will_reset = filter(will_reset, reason, context)
except:
# if the hook fails, remove it
self._hooks.remove(filter)
raise
return will_reset
main_window_should_require_reset = _MainWindowShouldRequireResetFilter()
class _MediaSyncDidProgressHook: class _MediaSyncDidProgressHook:
_hooks: List[Callable[["aqt.mediasync.LogEntryWithTime"], None]] = [] _hooks: List[Callable[["aqt.mediasync.LogEntryWithTime"], None]] = []

View File

@ -4,6 +4,7 @@
from __future__ import annotations from __future__ import annotations
import enum
import faulthandler import faulthandler
import gc import gc
import os import os
@ -68,6 +69,19 @@ from aqt.utils import (
install_pylib_legacy() install_pylib_legacy()
class ResetReason(enum.Enum):
AddCardsAddNote = "addCardsAddNote"
EditCurrentInit = "editCurrentInit"
EditorBridgeCmd = "editorBridgeCmd"
BrowserSetDeck = "browserSetDeck"
BrowserAddTags = "browserAddTags"
BrowserSuspend = "browserSuspend"
BrowserReposition = "browserReposition"
BrowserReschedule = "browserReschedule"
BrowserFindReplace = "browserFindReplace"
BrowserTagDupes = "browserTagDupes"
class ResetRequired: class ResetRequired:
def __init__(self, mw: AnkiQt): def __init__(self, mw: AnkiQt):
self.mw = mw self.mw = mw
@ -684,11 +698,13 @@ from the profile screen."
self.maybeEnableUndo() self.maybeEnableUndo()
self.moveToState(self.state) self.moveToState(self.state)
def requireReset(self, modal=False): def requireReset(self, modal=False, reason="unknown", context=None):
"Signal queue needs to be rebuilt when edits are finished or by user." "Signal queue needs to be rebuilt when edits are finished or by user."
self.autosave() self.autosave()
self.resetModal = modal self.resetModal = modal
if self.interactiveState(): if gui_hooks.main_window_should_require_reset(
self.interactiveState(), reason, context
):
self.moveToState("resetRequired") self.moveToState("resetRequired")
def interactiveState(self): def interactiveState(self):

View File

@ -440,6 +440,24 @@ hooks = [
is thus suitable for single-shot subscribers. is thus suitable for single-shot subscribers.
""", """,
), ),
Hook(
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"), Hook(name="backup_did_complete"),
Hook( Hook(
name="profile_did_open", name="profile_did_open",