update to latest black
This commit is contained in:
parent
603210149c
commit
a517accee3
@ -129,9 +129,11 @@ class Card:
|
|||||||
self, reload: bool = False, browser: bool = False
|
self, reload: bool = False, browser: bool = False
|
||||||
) -> anki.template.TemplateRenderOutput:
|
) -> anki.template.TemplateRenderOutput:
|
||||||
if not self._render_output or reload:
|
if not self._render_output or reload:
|
||||||
self._render_output = anki.template.TemplateRenderContext.from_existing_card(
|
self._render_output = (
|
||||||
self, browser
|
anki.template.TemplateRenderContext.from_existing_card(
|
||||||
).render()
|
self, browser
|
||||||
|
).render()
|
||||||
|
)
|
||||||
return self._render_output
|
return self._render_output
|
||||||
|
|
||||||
def set_render_output(self, output: anki.template.TemplateRenderOutput) -> None:
|
def set_render_output(self, output: anki.template.TemplateRenderOutput) -> None:
|
||||||
|
@ -104,7 +104,12 @@ class DeckManager:
|
|||||||
# Deck save/load
|
# Deck save/load
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def id(self, name: str, create: bool = True, type: int = 0,) -> Optional[int]:
|
def id(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
create: bool = True,
|
||||||
|
type: int = 0,
|
||||||
|
) -> Optional[int]:
|
||||||
"Add a deck with NAME. Reuse deck if already exists. Return id as int."
|
"Add a deck with NAME. Reuse deck if already exists. Return id as int."
|
||||||
id = self.id_for_name(name)
|
id = self.id_for_name(name)
|
||||||
if id:
|
if id:
|
||||||
|
@ -233,8 +233,8 @@ exporters_list_created = _ExportersListCreatedHook()
|
|||||||
class _FieldFilterFilter:
|
class _FieldFilterFilter:
|
||||||
"""Allows you to define custom {{filters:..}}
|
"""Allows you to define custom {{filters:..}}
|
||||||
|
|
||||||
Your add-on can check filter_name to decide whether it should modify
|
Your add-on can check filter_name to decide whether it should modify
|
||||||
field_text or not before returning it."""
|
field_text or not before returning it."""
|
||||||
|
|
||||||
_hooks: List[
|
_hooks: List[
|
||||||
Callable[[str, str, str, "anki.template.TemplateRenderContext"], str]
|
Callable[[str, str, str, "anki.template.TemplateRenderContext"], str]
|
||||||
@ -395,7 +395,7 @@ notes_will_be_deleted = _NotesWillBeDeletedHook()
|
|||||||
|
|
||||||
class _SchedulerNewLimitForSingleDeckFilter:
|
class _SchedulerNewLimitForSingleDeckFilter:
|
||||||
"""Allows changing the number of new card for this deck (without
|
"""Allows changing the number of new card for this deck (without
|
||||||
considering descendants)."""
|
considering descendants)."""
|
||||||
|
|
||||||
_hooks: List[Callable[[int, "anki.decks.Deck"], int]] = []
|
_hooks: List[Callable[[int, "anki.decks.Deck"], int]] = []
|
||||||
|
|
||||||
@ -426,7 +426,7 @@ scheduler_new_limit_for_single_deck = _SchedulerNewLimitForSingleDeckFilter()
|
|||||||
|
|
||||||
class _SchedulerReviewLimitForSingleDeckFilter:
|
class _SchedulerReviewLimitForSingleDeckFilter:
|
||||||
"""Allows changing the number of rev card for this deck (without
|
"""Allows changing the number of rev card for this deck (without
|
||||||
considering descendants)."""
|
considering descendants)."""
|
||||||
|
|
||||||
_hooks: List[Callable[[int, "anki.decks.Deck"], int]] = []
|
_hooks: List[Callable[[int, "anki.decks.Deck"], int]] = []
|
||||||
|
|
||||||
|
@ -437,7 +437,15 @@ and notes.mid = ? and cards.ord = ?""",
|
|||||||
for c in range(nfields):
|
for c in range(nfields):
|
||||||
flds.append(newflds.get(c, ""))
|
flds.append(newflds.get(c, ""))
|
||||||
flds = joinFields(flds)
|
flds = joinFields(flds)
|
||||||
d.append((flds, newModel["id"], intTime(), self.col.usn(), nid,))
|
d.append(
|
||||||
|
(
|
||||||
|
flds,
|
||||||
|
newModel["id"],
|
||||||
|
intTime(),
|
||||||
|
self.col.usn(),
|
||||||
|
nid,
|
||||||
|
)
|
||||||
|
)
|
||||||
self.col.db.executemany(
|
self.col.db.executemany(
|
||||||
"update notes set flds=?,mid=?,mod=?,usn=? where id = ?", d
|
"update notes set flds=?,mid=?,mod=?,usn=? where id = ?", d
|
||||||
)
|
)
|
||||||
|
@ -209,7 +209,9 @@ class RustBackend(RustBackendGenerated):
|
|||||||
langs = [anki.lang.currentLang]
|
langs = [anki.lang.currentLang]
|
||||||
|
|
||||||
init_msg = pb.BackendInit(
|
init_msg = pb.BackendInit(
|
||||||
locale_folder_path=ftl_folder, preferred_langs=langs, server=server,
|
locale_folder_path=ftl_folder,
|
||||||
|
preferred_langs=langs,
|
||||||
|
server=server,
|
||||||
)
|
)
|
||||||
self._backend = ankirspy.open_backend(init_msg.SerializeToString())
|
self._backend = ankirspy.open_backend(init_msg.SerializeToString())
|
||||||
|
|
||||||
|
@ -668,7 +668,10 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
|
|||||||
return tot + tod * 1000
|
return tot + tod * 1000
|
||||||
|
|
||||||
def _leftToday(
|
def _leftToday(
|
||||||
self, delays: List[int], left: int, now: Optional[int] = None,
|
self,
|
||||||
|
delays: List[int],
|
||||||
|
left: int,
|
||||||
|
now: Optional[int] = None,
|
||||||
) -> int:
|
) -> int:
|
||||||
"The number of steps that can be completed by the day cutoff."
|
"The number of steps that can be completed by the day cutoff."
|
||||||
if not now:
|
if not now:
|
||||||
@ -1603,7 +1606,16 @@ and (queue={QUEUE_TYPE_NEW} or (queue={QUEUE_TYPE_REV} and due<=?))""",
|
|||||||
mod = intTime()
|
mod = intTime()
|
||||||
for id in ids:
|
for id in ids:
|
||||||
r = random.randint(imin, imax)
|
r = random.randint(imin, imax)
|
||||||
d.append((max(1, r), r + t, self.col.usn(), mod, STARTING_FACTOR, id,))
|
d.append(
|
||||||
|
(
|
||||||
|
max(1, r),
|
||||||
|
r + t,
|
||||||
|
self.col.usn(),
|
||||||
|
mod,
|
||||||
|
STARTING_FACTOR,
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
)
|
||||||
self.remFromDyn(ids)
|
self.remFromDyn(ids)
|
||||||
self.col.db.executemany(
|
self.col.db.executemany(
|
||||||
f"""
|
f"""
|
||||||
|
@ -243,7 +243,9 @@ from revlog where id > ? """
|
|||||||
def _dueInfo(self, tot: int, num: int) -> str:
|
def _dueInfo(self, tot: int, num: int) -> str:
|
||||||
i: List[str] = []
|
i: List[str] = []
|
||||||
self._line(
|
self._line(
|
||||||
i, _("Total"), self.col.tr(TR.STATISTICS_REVIEWS, reviews=tot),
|
i,
|
||||||
|
_("Total"),
|
||||||
|
self.col.tr(TR.STATISTICS_REVIEWS, reviews=tot),
|
||||||
)
|
)
|
||||||
self._line(i, _("Average"), self._avgDay(tot, num, _("reviews")))
|
self._line(i, _("Average"), self._avgDay(tot, num, _("reviews")))
|
||||||
tomorrow = self.col.db.scalar(
|
tomorrow = self.col.db.scalar(
|
||||||
@ -436,7 +438,9 @@ group by day order by day"""
|
|||||||
return self._lineTbl(i), int(tot)
|
return self._lineTbl(i), int(tot)
|
||||||
|
|
||||||
def _splitRepData(
|
def _splitRepData(
|
||||||
self, data: List[Tuple[Any, ...]], spec: Sequence[Tuple[int, str, str]],
|
self,
|
||||||
|
data: List[Tuple[Any, ...]],
|
||||||
|
spec: Sequence[Tuple[int, str, str]],
|
||||||
) -> Tuple[List[Dict[str, Any]], List[Tuple[Any, Any]]]:
|
) -> Tuple[List[Dict[str, Any]], List[Tuple[Any, Any]]]:
|
||||||
sep: Dict[int, Any] = {}
|
sep: Dict[int, Any] = {}
|
||||||
totcnt = {}
|
totcnt = {}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
wheel
|
wheel
|
||||||
mypy
|
mypy
|
||||||
mypy_protobuf>=1.21
|
mypy_protobuf>=1.21
|
||||||
black
|
black>=20.1b0
|
||||||
pytest>=6.0.1
|
pytest>=6.0.1
|
||||||
stringcase==1.2.0
|
stringcase==1.2.0
|
||||||
|
@ -93,10 +93,14 @@ hooks = [
|
|||||||
doc="Obsolete, do not use.",
|
doc="Obsolete, do not use.",
|
||||||
),
|
),
|
||||||
Hook(
|
Hook(
|
||||||
name="sync_stage_did_change", args=["stage: str"], doc="Obsolete, do not use.",
|
name="sync_stage_did_change",
|
||||||
|
args=["stage: str"],
|
||||||
|
doc="Obsolete, do not use.",
|
||||||
),
|
),
|
||||||
Hook(
|
Hook(
|
||||||
name="sync_progress_did_change", args=["msg: str"], doc="Obsolete, do not use.",
|
name="sync_progress_did_change",
|
||||||
|
args=["msg: str"],
|
||||||
|
doc="Obsolete, do not use.",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -760,7 +760,9 @@ class AddonsDialog(QDialog):
|
|||||||
def should_grey(self, addon: AddonMeta) -> bool:
|
def should_grey(self, addon: AddonMeta) -> bool:
|
||||||
return not addon.enabled or not addon.compatible()
|
return not addon.enabled or not addon.compatible()
|
||||||
|
|
||||||
def redrawAddons(self,) -> None:
|
def redrawAddons(
|
||||||
|
self,
|
||||||
|
) -> None:
|
||||||
addonList = self.form.addonList
|
addonList = self.form.addonList
|
||||||
mgr = self.mgr
|
mgr = self.mgr
|
||||||
|
|
||||||
@ -1321,7 +1323,11 @@ class ConfigEditor(QDialog):
|
|||||||
|
|
||||||
def updateText(self, conf: Dict[str, Any]) -> None:
|
def updateText(self, conf: Dict[str, Any]) -> None:
|
||||||
text = json.dumps(
|
text = json.dumps(
|
||||||
conf, ensure_ascii=False, sort_keys=True, indent=4, separators=(",", ": "),
|
conf,
|
||||||
|
ensure_ascii=False,
|
||||||
|
sort_keys=True,
|
||||||
|
indent=4,
|
||||||
|
separators=(",", ": "),
|
||||||
)
|
)
|
||||||
text = gui_hooks.addon_config_editor_will_display_json(text)
|
text = gui_hooks.addon_config_editor_will_display_json(text)
|
||||||
self.form.editor.setPlainText(text)
|
self.form.editor.setPlainText(text)
|
||||||
|
@ -166,13 +166,19 @@ class CardLayout(QDialog):
|
|||||||
self.tform.back_button.setToolTip(shortcut("Ctrl+2"))
|
self.tform.back_button.setToolTip(shortcut("Ctrl+2"))
|
||||||
self.tform.style_button.setToolTip(shortcut("Ctrl+3"))
|
self.tform.style_button.setToolTip(shortcut("Ctrl+3"))
|
||||||
QShortcut( # type: ignore
|
QShortcut( # type: ignore
|
||||||
QKeySequence("Ctrl+1"), self, activated=self.tform.front_button.click,
|
QKeySequence("Ctrl+1"),
|
||||||
|
self,
|
||||||
|
activated=self.tform.front_button.click,
|
||||||
)
|
)
|
||||||
QShortcut( # type: ignore
|
QShortcut( # type: ignore
|
||||||
QKeySequence("Ctrl+2"), self, activated=self.tform.back_button.click,
|
QKeySequence("Ctrl+2"),
|
||||||
|
self,
|
||||||
|
activated=self.tform.back_button.click,
|
||||||
)
|
)
|
||||||
QShortcut( # type: ignore
|
QShortcut( # type: ignore
|
||||||
QKeySequence("Ctrl+3"), self, activated=self.tform.style_button.click,
|
QKeySequence("Ctrl+3"),
|
||||||
|
self,
|
||||||
|
activated=self.tform.style_button.click,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Main area setup
|
# Main area setup
|
||||||
@ -303,7 +309,10 @@ class CardLayout(QDialog):
|
|||||||
"reviewer.js",
|
"reviewer.js",
|
||||||
]
|
]
|
||||||
self.preview_web.stdHtml(
|
self.preview_web.stdHtml(
|
||||||
self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc, context=self,
|
self.mw.reviewer.revHtml(),
|
||||||
|
css=["reviewer.css"],
|
||||||
|
js=jsinc,
|
||||||
|
context=self,
|
||||||
)
|
)
|
||||||
self.preview_web.set_bridge_command(self._on_bridge_cmd, self)
|
self.preview_web.set_bridge_command(self._on_bridge_cmd, self)
|
||||||
|
|
||||||
@ -764,7 +773,9 @@ Enter deck to place new %s cards in, or leave blank:"""
|
|||||||
row = form.fields.currentIndex().row()
|
row = form.fields.currentIndex().row()
|
||||||
if row >= 0:
|
if row >= 0:
|
||||||
self._addField(
|
self._addField(
|
||||||
fields[row], form.font.currentFont().family(), form.size.value(),
|
fields[row],
|
||||||
|
form.font.currentFont().family(),
|
||||||
|
form.size.value(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _addField(self, field, font, size):
|
def _addField(self, field, font, size):
|
||||||
|
@ -118,7 +118,8 @@ class DeckBrowser:
|
|||||||
|
|
||||||
def __renderPage(self, offset):
|
def __renderPage(self, offset):
|
||||||
content = DeckBrowserContent(
|
content = DeckBrowserContent(
|
||||||
tree=self._renderDeckTree(self._dueTree), stats=self._renderStats(),
|
tree=self._renderDeckTree(self._dueTree),
|
||||||
|
stats=self._renderStats(),
|
||||||
)
|
)
|
||||||
gui_hooks.deck_browser_will_render_content(self, content)
|
gui_hooks.deck_browser_will_render_content(self, content)
|
||||||
self.web.stdHtml(
|
self.web.stdHtml(
|
||||||
|
@ -233,7 +233,9 @@ class Editor:
|
|||||||
self._links[cmd] = func
|
self._links[cmd] = func
|
||||||
if keys:
|
if keys:
|
||||||
QShortcut( # type: ignore
|
QShortcut( # type: ignore
|
||||||
QKeySequence(keys), self.widget, activated=lambda s=self: func(s),
|
QKeySequence(keys),
|
||||||
|
self.widget,
|
||||||
|
activated=lambda s=self: func(s),
|
||||||
)
|
)
|
||||||
btn = self._addButton(
|
btn = self._addButton(
|
||||||
icon,
|
icon,
|
||||||
|
@ -120,7 +120,12 @@ class ExportDialog(QDialog):
|
|||||||
key_str = self.exporter.key
|
key_str = self.exporter.key
|
||||||
while 1:
|
while 1:
|
||||||
file = getSaveFile(
|
file = getSaveFile(
|
||||||
self, _("Export"), "export", key_str, self.exporter.ext, fname=filename,
|
self,
|
||||||
|
_("Export"),
|
||||||
|
"export",
|
||||||
|
key_str,
|
||||||
|
self.exporter.ext,
|
||||||
|
fname=filename,
|
||||||
)
|
)
|
||||||
if not file:
|
if not file:
|
||||||
return
|
return
|
||||||
@ -175,14 +180,18 @@ class ExportDialog(QDialog):
|
|||||||
if self.isTextNote:
|
if self.isTextNote:
|
||||||
msg = (
|
msg = (
|
||||||
ngettext(
|
ngettext(
|
||||||
"%d note exported.", "%d notes exported.", self.exporter.count,
|
"%d note exported.",
|
||||||
|
"%d notes exported.",
|
||||||
|
self.exporter.count,
|
||||||
)
|
)
|
||||||
% self.exporter.count
|
% self.exporter.count
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
msg = (
|
msg = (
|
||||||
ngettext(
|
ngettext(
|
||||||
"%d card exported.", "%d cards exported.", self.exporter.count,
|
"%d card exported.",
|
||||||
|
"%d cards exported.",
|
||||||
|
self.exporter.count,
|
||||||
)
|
)
|
||||||
% self.exporter.count
|
% self.exporter.count
|
||||||
)
|
)
|
||||||
|
@ -84,12 +84,12 @@ add_cards_did_init = _AddCardsDidInitHook()
|
|||||||
|
|
||||||
class _AddCardsWillAddNoteFilter:
|
class _AddCardsWillAddNoteFilter:
|
||||||
"""Decides whether the note should be added to the collection or
|
"""Decides whether the note should be added to the collection or
|
||||||
not. It is assumed to come from the addCards window.
|
not. It is assumed to come from the addCards window.
|
||||||
|
|
||||||
reason_to_already_reject is the first reason to reject that
|
reason_to_already_reject is the first reason to reject that
|
||||||
was found, or None. If your filter wants to reject, it should
|
was found, or None. If your filter wants to reject, it should
|
||||||
replace return the reason to reject. Otherwise return the
|
replace return the reason to reject. Otherwise return the
|
||||||
input."""
|
input."""
|
||||||
|
|
||||||
_hooks: List[Callable[[Optional[str], "anki.notes.Note"], Optional[str]]] = []
|
_hooks: List[Callable[[Optional[str], "anki.notes.Note"], Optional[str]]] = []
|
||||||
|
|
||||||
@ -183,10 +183,10 @@ addcards_will_add_history_entry = _AddcardsWillAddHistoryEntryFilter()
|
|||||||
|
|
||||||
class _AddonConfigEditorWillDisplayJsonFilter:
|
class _AddonConfigEditorWillDisplayJsonFilter:
|
||||||
"""Allows changing the text of the json configuration before actually
|
"""Allows changing the text of the json configuration before actually
|
||||||
displaying it to the user. For example, you can replace "\n" by
|
displaying it to the user. For example, you can replace "\n" by
|
||||||
some actual new line. Then you can replace the new lines by "\n"
|
some actual new line. Then you can replace the new lines by "\n"
|
||||||
while reading the file and let the user uses real new line in
|
while reading the file and let the user uses real new line in
|
||||||
string instead of its encoding."""
|
string instead of its encoding."""
|
||||||
|
|
||||||
_hooks: List[Callable[[str], str]] = []
|
_hooks: List[Callable[[str], str]] = []
|
||||||
|
|
||||||
@ -217,8 +217,8 @@ addon_config_editor_will_display_json = _AddonConfigEditorWillDisplayJsonFilter(
|
|||||||
|
|
||||||
class _AddonConfigEditorWillSaveJsonFilter:
|
class _AddonConfigEditorWillSaveJsonFilter:
|
||||||
"""Allows changing the text of the json configuration that was
|
"""Allows changing the text of the json configuration that was
|
||||||
received from the user before actually reading it. For
|
received from the user before actually reading it. For
|
||||||
example, you can replace new line in strings by some "\n"."""
|
example, you can replace new line in strings by some "\n"."""
|
||||||
|
|
||||||
_hooks: List[Callable[[str], str]] = []
|
_hooks: List[Callable[[str], str]] = []
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ addons_dialog_did_change_selected_addon = _AddonsDialogDidChangeSelectedAddonHoo
|
|||||||
|
|
||||||
class _AddonsDialogWillShowHook:
|
class _AddonsDialogWillShowHook:
|
||||||
"""Allows changing the add-on dialog before it is shown. E.g. add
|
"""Allows changing the add-on dialog before it is shown. E.g. add
|
||||||
buttons."""
|
buttons."""
|
||||||
|
|
||||||
_hooks: List[Callable[["aqt.addons.AddonsDialog"], None]] = []
|
_hooks: List[Callable[["aqt.addons.AddonsDialog"], None]] = []
|
||||||
|
|
||||||
@ -543,39 +543,39 @@ browser_menus_did_init = _BrowserMenusDidInitHook()
|
|||||||
class _BrowserWillBuildTreeFilter:
|
class _BrowserWillBuildTreeFilter:
|
||||||
"""Used to add or replace items in the browser sidebar tree
|
"""Used to add or replace items in the browser sidebar tree
|
||||||
|
|
||||||
'tree' is the root SidebarItem that all other items are added to.
|
'tree' is the root SidebarItem that all other items are added to.
|
||||||
|
|
||||||
'stage' is an enum describing the different construction stages of
|
'stage' is an enum describing the different construction stages of
|
||||||
the sidebar tree at which you can interject your changes.
|
the sidebar tree at which you can interject your changes.
|
||||||
The different values can be inspected by looking at
|
The different values can be inspected by looking at
|
||||||
aqt.browser.SidebarStage.
|
aqt.browser.SidebarStage.
|
||||||
|
|
||||||
If you want Anki to proceed with the construction of the tree stage
|
If you want Anki to proceed with the construction of the tree stage
|
||||||
in question after your have performed your changes or additions,
|
in question after your have performed your changes or additions,
|
||||||
return the 'handled' boolean unchanged.
|
return the 'handled' boolean unchanged.
|
||||||
|
|
||||||
On the other hand, if you want to prevent Anki from adding its own
|
On the other hand, if you want to prevent Anki from adding its own
|
||||||
items at a particular construction stage (e.g. in case your add-on
|
items at a particular construction stage (e.g. in case your add-on
|
||||||
implements its own version of that particular stage), return 'True'.
|
implements its own version of that particular stage), return 'True'.
|
||||||
|
|
||||||
If you return 'True' at SidebarStage.ROOT, the sidebar will not be
|
If you return 'True' at SidebarStage.ROOT, the sidebar will not be
|
||||||
populated by any of the other construction stages. For any other stage
|
populated by any of the other construction stages. For any other stage
|
||||||
the tree construction will just continue as usual.
|
the tree construction will just continue as usual.
|
||||||
|
|
||||||
For example, if your code wishes to replace the tag tree, you could do:
|
For example, if your code wishes to replace the tag tree, you could do:
|
||||||
|
|
||||||
def on_browser_will_build_tree(handled, root, stage, browser):
|
def on_browser_will_build_tree(handled, root, stage, browser):
|
||||||
if stage != SidebarStage.TAGS:
|
if stage != SidebarStage.TAGS:
|
||||||
# not at tag tree building stage, pass on
|
# not at tag tree building stage, pass on
|
||||||
return handled
|
return handled
|
||||||
|
|
||||||
# your tag tree construction code
|
# your tag tree construction code
|
||||||
# root.addChild(...)
|
# root.addChild(...)
|
||||||
|
|
||||||
# your code handled tag tree construction, no need for Anki
|
# your code handled tag tree construction, no need for Anki
|
||||||
# or other add-ons to build the tag tree
|
# or other add-ons to build the tag tree
|
||||||
return True
|
return True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[
|
_hooks: List[
|
||||||
Callable[
|
Callable[
|
||||||
@ -645,15 +645,15 @@ browser_will_build_tree = _BrowserWillBuildTreeFilter()
|
|||||||
class _BrowserWillSearchHook:
|
class _BrowserWillSearchHook:
|
||||||
"""Allows you to modify the search text, or perform your own search.
|
"""Allows you to modify the search text, or perform your own search.
|
||||||
|
|
||||||
You can modify context.search to change the text that is sent to the
|
You can modify context.search to change the text that is sent to the
|
||||||
searching backend.
|
searching backend.
|
||||||
|
|
||||||
If you set context.card_ids to a list of ids, the regular search will
|
If you set context.card_ids to a list of ids, the regular search will
|
||||||
not be performed, and the provided ids will be used instead.
|
not be performed, and the provided ids will be used instead.
|
||||||
|
|
||||||
Your add-on should check if context.card_ids is not None, and return
|
Your add-on should check if context.card_ids is not None, and return
|
||||||
without making changes if it has been set.
|
without making changes if it has been set.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[Callable[["aqt.browser.SearchContext"], None]] = []
|
_hooks: List[Callable[["aqt.browser.SearchContext"], None]] = []
|
||||||
|
|
||||||
@ -739,7 +739,7 @@ browser_will_show_context_menu = _BrowserWillShowContextMenuHook()
|
|||||||
|
|
||||||
class _CardLayoutWillShowHook:
|
class _CardLayoutWillShowHook:
|
||||||
"""Allow to change the display of the card layout. After most values are
|
"""Allow to change the display of the card layout. After most values are
|
||||||
set and before the window is actually shown."""
|
set and before the window is actually shown."""
|
||||||
|
|
||||||
_hooks: List[Callable[["aqt.clayout.CardLayout"], None]] = []
|
_hooks: List[Callable[["aqt.clayout.CardLayout"], None]] = []
|
||||||
|
|
||||||
@ -859,7 +859,7 @@ current_note_type_did_change = _CurrentNoteTypeDidChangeHook()
|
|||||||
|
|
||||||
class _DebugConsoleDidEvaluatePythonFilter:
|
class _DebugConsoleDidEvaluatePythonFilter:
|
||||||
"""Allows processing the debug result. E.g. logging queries and
|
"""Allows processing the debug result. E.g. logging queries and
|
||||||
result, saving last query to display it later..."""
|
result, saving last query to display it later..."""
|
||||||
|
|
||||||
_hooks: List[Callable[[str, str, QDialog], str]] = []
|
_hooks: List[Callable[[str, str, QDialog], str]] = []
|
||||||
|
|
||||||
@ -890,7 +890,7 @@ debug_console_did_evaluate_python = _DebugConsoleDidEvaluatePythonFilter()
|
|||||||
|
|
||||||
class _DebugConsoleWillShowHook:
|
class _DebugConsoleWillShowHook:
|
||||||
"""Allows editing the debug window. E.g. setting a default code, or
|
"""Allows editing the debug window. E.g. setting a default code, or
|
||||||
previous code."""
|
previous code."""
|
||||||
|
|
||||||
_hooks: List[Callable[[QDialog], None]] = []
|
_hooks: List[Callable[[QDialog], None]] = []
|
||||||
|
|
||||||
@ -950,18 +950,18 @@ deck_browser_did_render = _DeckBrowserDidRenderHook()
|
|||||||
class _DeckBrowserWillRenderContentHook:
|
class _DeckBrowserWillRenderContentHook:
|
||||||
"""Used to modify HTML content sections in the deck browser body
|
"""Used to modify HTML content sections in the deck browser body
|
||||||
|
|
||||||
'content' contains the sections of HTML content the deck browser body
|
'content' contains the sections of HTML content the deck browser body
|
||||||
will be updated with.
|
will be updated with.
|
||||||
|
|
||||||
When modifying the content of a particular section, please make sure your
|
When modifying the content of a particular section, please make sure your
|
||||||
changes only perform the minimum required edits to make your add-on work.
|
changes only perform the minimum required edits to make your add-on work.
|
||||||
You should avoid overwriting or interfering with existing data as much
|
You should avoid overwriting or interfering with existing data as much
|
||||||
as possible, instead opting to append your own changes, e.g.:
|
as possible, instead opting to append your own changes, e.g.:
|
||||||
|
|
||||||
def on_deck_browser_will_render_content(deck_browser, content):
|
def on_deck_browser_will_render_content(deck_browser, content):
|
||||||
content.stats += "
|
content.stats += "
|
||||||
<div>my html</div>"
|
<div>my html</div>"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[
|
_hooks: List[
|
||||||
Callable[
|
Callable[
|
||||||
@ -1039,14 +1039,14 @@ deck_browser_will_show_options_menu = _DeckBrowserWillShowOptionsMenuHook()
|
|||||||
class _DeckConfDidAddConfigHook:
|
class _DeckConfDidAddConfigHook:
|
||||||
"""Allows modification of a newly created config group
|
"""Allows modification of a newly created config group
|
||||||
|
|
||||||
This hook is called after the config group was created, but
|
This hook is called after the config group was created, but
|
||||||
before initializing the widget state.
|
before initializing the widget state.
|
||||||
|
|
||||||
`deck_conf` will point to the old config group, `new_conf_id` will
|
`deck_conf` will point to the old config group, `new_conf_id` will
|
||||||
point to the newly created config group.
|
point to the newly created config group.
|
||||||
|
|
||||||
Config groups are created as clones of the current one.
|
Config groups are created as clones of the current one.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[
|
_hooks: List[
|
||||||
Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str, int], None]
|
Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str, int], None]
|
||||||
@ -1549,7 +1549,7 @@ editor_web_view_did_init = _EditorWebViewDidInitHook()
|
|||||||
|
|
||||||
class _EditorWillLoadNoteFilter:
|
class _EditorWillLoadNoteFilter:
|
||||||
"""Allows changing the javascript commands to load note before
|
"""Allows changing the javascript commands to load note before
|
||||||
executing it and do change in the QT editor."""
|
executing it and do change in the QT editor."""
|
||||||
|
|
||||||
_hooks: List[Callable[[str, "anki.notes.Note", "aqt.editor.Editor"], str]] = []
|
_hooks: List[Callable[[str, "anki.notes.Note", "aqt.editor.Editor"], str]] = []
|
||||||
|
|
||||||
@ -1705,11 +1705,11 @@ empty_cards_will_show = _EmptyCardsWillShowHook()
|
|||||||
class _MainWindowDidInitHook:
|
class _MainWindowDidInitHook:
|
||||||
"""Executed after the main window is fully initialized
|
"""Executed after the main window is fully initialized
|
||||||
|
|
||||||
A sample use case for this hook would be to delay actions until Anki objects
|
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
|
like the profile or collection are fully initialized. In contrast to
|
||||||
`profile_did_open`, this hook will only fire once per Anki session and
|
`profile_did_open`, this hook will only fire once per Anki session and
|
||||||
is thus suitable for single-shot subscribers.
|
is thus suitable for single-shot subscribers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[Callable[[], None]] = []
|
_hooks: List[Callable[[], None]] = []
|
||||||
|
|
||||||
@ -1740,13 +1740,13 @@ main_window_did_init = _MainWindowDidInitHook()
|
|||||||
class _MainWindowShouldRequireResetFilter:
|
class _MainWindowShouldRequireResetFilter:
|
||||||
"""Executed before the main window will require a reset
|
"""Executed before the main window will require a reset
|
||||||
|
|
||||||
This hook can be used to change the behavior of the main window,
|
This hook can be used to change the behavior of the main window,
|
||||||
when other dialogs, like the AddCards or Browser, require a reset
|
when other dialogs, like the AddCards or Browser, require a reset
|
||||||
from the main window.
|
from the main window.
|
||||||
If you decide to use this hook, make you sure you check the reason for the reset.
|
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
|
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).
|
put the main window into an invalid state (e.g. display a deleted note).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[
|
_hooks: List[
|
||||||
Callable[[bool, "Union[aqt.main.ResetReason, str]", Optional[Any]], bool]
|
Callable[[bool, "Union[aqt.main.ResetReason, str]", Optional[Any]], bool]
|
||||||
@ -1871,7 +1871,7 @@ models_advanced_will_show = _ModelsAdvancedWillShowHook()
|
|||||||
|
|
||||||
class _OverviewDidRefreshHook:
|
class _OverviewDidRefreshHook:
|
||||||
"""Allow to update the overview window. E.g. add the deck name in the
|
"""Allow to update the overview window. E.g. add the deck name in the
|
||||||
title."""
|
title."""
|
||||||
|
|
||||||
_hooks: List[Callable[["aqt.overview.Overview"], None]] = []
|
_hooks: List[Callable[["aqt.overview.Overview"], None]] = []
|
||||||
|
|
||||||
@ -1902,18 +1902,18 @@ overview_did_refresh = _OverviewDidRefreshHook()
|
|||||||
class _OverviewWillRenderContentHook:
|
class _OverviewWillRenderContentHook:
|
||||||
"""Used to modify HTML content sections in the overview body
|
"""Used to modify HTML content sections in the overview body
|
||||||
|
|
||||||
'content' contains the sections of HTML content the overview body
|
'content' contains the sections of HTML content the overview body
|
||||||
will be updated with.
|
will be updated with.
|
||||||
|
|
||||||
When modifying the content of a particular section, please make sure your
|
When modifying the content of a particular section, please make sure your
|
||||||
changes only perform the minimum required edits to make your add-on work.
|
changes only perform the minimum required edits to make your add-on work.
|
||||||
You should avoid overwriting or interfering with existing data as much
|
You should avoid overwriting or interfering with existing data as much
|
||||||
as possible, instead opting to append your own changes, e.g.:
|
as possible, instead opting to append your own changes, e.g.:
|
||||||
|
|
||||||
def on_overview_will_render_content(overview, content):
|
def on_overview_will_render_content(overview, content):
|
||||||
content.table += "
|
content.table += "
|
||||||
<div>my html</div>"
|
<div>my html</div>"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[
|
_hooks: List[
|
||||||
Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None]
|
Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None]
|
||||||
@ -1954,10 +1954,10 @@ overview_will_render_content = _OverviewWillRenderContentHook()
|
|||||||
class _ProfileDidOpenHook:
|
class _ProfileDidOpenHook:
|
||||||
"""Executed whenever a user profile has been opened
|
"""Executed whenever a user profile has been opened
|
||||||
|
|
||||||
Please note that this hook will also be called on profile switches, so if you
|
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,
|
are looking to simply delay an add-on action in a single-shot manner,
|
||||||
`main_window_did_init` is likely the more suitable choice.
|
`main_window_did_init` is likely the more suitable choice.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[Callable[[], None]] = []
|
_hooks: List[Callable[[], None]] = []
|
||||||
|
|
||||||
@ -2132,14 +2132,14 @@ reviewer_did_show_question = _ReviewerDidShowQuestionHook()
|
|||||||
|
|
||||||
class _ReviewerWillAnswerCardFilter:
|
class _ReviewerWillAnswerCardFilter:
|
||||||
"""Used to modify the ease at which a card is rated or to bypass
|
"""Used to modify the ease at which a card is rated or to bypass
|
||||||
rating the card completely.
|
rating the card completely.
|
||||||
|
|
||||||
ease_tuple is a tuple consisting of a boolean expressing whether the reviewer
|
ease_tuple is a tuple consisting of a boolean expressing whether the reviewer
|
||||||
should continue with rating the card, and an integer expressing the ease at
|
should continue with rating the card, and an integer expressing the ease at
|
||||||
which the card should be rated.
|
which the card should be rated.
|
||||||
|
|
||||||
If your code just needs to be notified of the card rating event, you should use
|
If your code just needs to be notified of the card rating event, you should use
|
||||||
the reviewer_did_answer_card hook instead."""
|
the reviewer_did_answer_card hook instead."""
|
||||||
|
|
||||||
_hooks: List[
|
_hooks: List[
|
||||||
Callable[[Tuple[bool, int], "aqt.reviewer.Reviewer", Card], Tuple[bool, int]]
|
Callable[[Tuple[bool, int], "aqt.reviewer.Reviewer", Card], Tuple[bool, int]]
|
||||||
@ -2216,16 +2216,16 @@ reviewer_will_end = _ReviewerWillEndHook()
|
|||||||
class _ReviewerWillInitAnswerButtonsFilter:
|
class _ReviewerWillInitAnswerButtonsFilter:
|
||||||
"""Used to modify list of answer buttons
|
"""Used to modify list of answer buttons
|
||||||
|
|
||||||
buttons_tuple is a tuple of buttons, with each button represented by a
|
buttons_tuple is a tuple of buttons, with each button represented by a
|
||||||
tuple containing an int for the button's ease and a string for the
|
tuple containing an int for the button's ease and a string for the
|
||||||
button's label.
|
button's label.
|
||||||
|
|
||||||
Return a tuple of the form ((int, str), ...), e.g.:
|
Return a tuple of the form ((int, str), ...), e.g.:
|
||||||
((1, "Label1"), (2, "Label2"), ...)
|
((1, "Label1"), (2, "Label2"), ...)
|
||||||
|
|
||||||
Note: import _ from anki.lang to support translation, using, e.g.,
|
Note: import _ from anki.lang to support translation, using, e.g.,
|
||||||
((1, _("Label1")), ...)
|
((1, _("Label1")), ...)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[
|
_hooks: List[
|
||||||
Callable[
|
Callable[
|
||||||
@ -2279,15 +2279,15 @@ reviewer_will_init_answer_buttons = _ReviewerWillInitAnswerButtonsFilter()
|
|||||||
class _ReviewerWillPlayAnswerSoundsHook:
|
class _ReviewerWillPlayAnswerSoundsHook:
|
||||||
"""Called before showing the answer/back side.
|
"""Called before showing the answer/back side.
|
||||||
|
|
||||||
`tags` can be used to inspect and manipulate the sounds
|
`tags` can be used to inspect and manipulate the sounds
|
||||||
that will be played (if any).
|
that will be played (if any).
|
||||||
|
|
||||||
This won't be called when the user manually plays sounds
|
This won't be called when the user manually plays sounds
|
||||||
using `Replay Audio`.
|
using `Replay Audio`.
|
||||||
|
|
||||||
Note that this hook is called even when the `Automatically play audio`
|
Note that this hook is called even when the `Automatically play audio`
|
||||||
option is unchecked; This is so as to allow playing custom
|
option is unchecked; This is so as to allow playing custom
|
||||||
sounds regardless of that option."""
|
sounds regardless of that option."""
|
||||||
|
|
||||||
_hooks: List[Callable[[Card, "List[anki.sound.AVTag]"], None]] = []
|
_hooks: List[Callable[[Card, "List[anki.sound.AVTag]"], None]] = []
|
||||||
|
|
||||||
@ -2318,15 +2318,15 @@ reviewer_will_play_answer_sounds = _ReviewerWillPlayAnswerSoundsHook()
|
|||||||
class _ReviewerWillPlayQuestionSoundsHook:
|
class _ReviewerWillPlayQuestionSoundsHook:
|
||||||
"""Called before showing the question/front side.
|
"""Called before showing the question/front side.
|
||||||
|
|
||||||
`tags` can be used to inspect and manipulate the sounds
|
`tags` can be used to inspect and manipulate the sounds
|
||||||
that will be played (if any).
|
that will be played (if any).
|
||||||
|
|
||||||
This won't be called when the user manually plays sounds
|
This won't be called when the user manually plays sounds
|
||||||
using `Replay Audio`.
|
using `Replay Audio`.
|
||||||
|
|
||||||
Note that this hook is called even when the `Automatically play audio`
|
Note that this hook is called even when the `Automatically play audio`
|
||||||
option is unchecked; This is so as to allow playing custom
|
option is unchecked; This is so as to allow playing custom
|
||||||
sounds regardless of that option."""
|
sounds regardless of that option."""
|
||||||
|
|
||||||
_hooks: List[Callable[[Card, "List[anki.sound.AVTag]"], None]] = []
|
_hooks: List[Callable[[Card, "List[anki.sound.AVTag]"], None]] = []
|
||||||
|
|
||||||
@ -2702,14 +2702,14 @@ tag_editor_did_process_key = _TagEditorDidProcessKeyHook()
|
|||||||
class _TopToolbarDidInitLinksHook:
|
class _TopToolbarDidInitLinksHook:
|
||||||
"""Used to modify or add links in the top toolbar of Anki's main window
|
"""Used to modify or add links in the top toolbar of Anki's main window
|
||||||
|
|
||||||
'links' is a list of HTML link elements. Add-ons can generate their own links
|
'links' is a list of HTML link elements. Add-ons can generate their own links
|
||||||
by using aqt.toolbar.Toolbar.create_link. Links created in that way can then be
|
by using aqt.toolbar.Toolbar.create_link. Links created in that way can then be
|
||||||
appended to the link list, e.g.:
|
appended to the link list, e.g.:
|
||||||
|
|
||||||
def on_top_toolbar_did_init_links(links, toolbar):
|
def on_top_toolbar_did_init_links(links, toolbar):
|
||||||
my_link = toolbar.create_link(...)
|
my_link = toolbar.create_link(...)
|
||||||
links.append(my_link)
|
links.append(my_link)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[Callable[[List[str], "aqt.toolbar.Toolbar"], None]] = []
|
_hooks: List[Callable[[List[str], "aqt.toolbar.Toolbar"], None]] = []
|
||||||
|
|
||||||
@ -2798,34 +2798,34 @@ undo_state_did_change = _UndoStateDidChangeHook()
|
|||||||
class _WebviewDidReceiveJsMessageFilter:
|
class _WebviewDidReceiveJsMessageFilter:
|
||||||
"""Used to handle pycmd() messages sent from Javascript.
|
"""Used to handle pycmd() messages sent from Javascript.
|
||||||
|
|
||||||
Message is the string passed to pycmd().
|
Message is the string passed to pycmd().
|
||||||
|
|
||||||
For messages you don't want to handle, return 'handled' unchanged.
|
For messages you don't want to handle, return 'handled' unchanged.
|
||||||
|
|
||||||
If you handle a message and don't want it passed to the original
|
If you handle a message and don't want it passed to the original
|
||||||
bridge command handler, return (True, None).
|
bridge command handler, return (True, None).
|
||||||
|
|
||||||
If you want to pass a value to pycmd's result callback, you can
|
If you want to pass a value to pycmd's result callback, you can
|
||||||
return it with (True, some_value).
|
return it with (True, some_value).
|
||||||
|
|
||||||
Context is the instance that was passed to set_bridge_command().
|
Context is the instance that was passed to set_bridge_command().
|
||||||
It can be inspected to check which screen this hook is firing
|
It can be inspected to check which screen this hook is firing
|
||||||
in, and to get a reference to the screen. For example, if your
|
in, and to get a reference to the screen. For example, if your
|
||||||
code wishes to function only in the review screen, you could do:
|
code wishes to function only in the review screen, you could do:
|
||||||
|
|
||||||
if not isinstance(context, aqt.reviewer.Reviewer):
|
if not isinstance(context, aqt.reviewer.Reviewer):
|
||||||
# not reviewer, pass on message
|
# not reviewer, pass on message
|
||||||
return handled
|
return handled
|
||||||
|
|
||||||
if message == "my-mark-action":
|
if message == "my-mark-action":
|
||||||
# our message, call onMark() on the reviewer instance
|
# our message, call onMark() on the reviewer instance
|
||||||
context.onMark()
|
context.onMark()
|
||||||
# and don't pass message to other handlers
|
# and don't pass message to other handlers
|
||||||
return (True, None)
|
return (True, None)
|
||||||
else:
|
else:
|
||||||
# some other command, pass it on
|
# some other command, pass it on
|
||||||
return handled
|
return handled
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[Callable[[Tuple[bool, Any], str, Any], Tuple[bool, Any]]] = []
|
_hooks: List[Callable[[Tuple[bool, Any], str, Any], Tuple[bool, Any]]] = []
|
||||||
|
|
||||||
@ -2863,34 +2863,34 @@ webview_did_receive_js_message = _WebviewDidReceiveJsMessageFilter()
|
|||||||
class _WebviewWillSetContentHook:
|
class _WebviewWillSetContentHook:
|
||||||
"""Used to modify web content before it is rendered.
|
"""Used to modify web content before it is rendered.
|
||||||
|
|
||||||
Web_content contains the HTML, JS, and CSS the web view will be
|
Web_content contains the HTML, JS, and CSS the web view will be
|
||||||
populated with.
|
populated with.
|
||||||
|
|
||||||
Context is the instance that was passed to stdHtml().
|
Context is the instance that was passed to stdHtml().
|
||||||
It can be inspected to check which screen this hook is firing
|
It can be inspected to check which screen this hook is firing
|
||||||
in, and to get a reference to the screen. For example, if your
|
in, and to get a reference to the screen. For example, if your
|
||||||
code wishes to function only in the review screen, you could do:
|
code wishes to function only in the review screen, you could do:
|
||||||
|
|
||||||
def on_webview_will_set_content(web_content: WebContent, context):
|
def on_webview_will_set_content(web_content: WebContent, context):
|
||||||
|
|
||||||
if not isinstance(context, aqt.reviewer.Reviewer):
|
if not isinstance(context, aqt.reviewer.Reviewer):
|
||||||
# not reviewer, do not modify content
|
# not reviewer, do not modify content
|
||||||
return
|
return
|
||||||
|
|
||||||
# reviewer, perform changes to content
|
# reviewer, perform changes to content
|
||||||
|
|
||||||
context: aqt.reviewer.Reviewer
|
context: aqt.reviewer.Reviewer
|
||||||
|
|
||||||
addon_package = mw.addonManager.addonFromModule(__name__)
|
addon_package = mw.addonManager.addonFromModule(__name__)
|
||||||
|
|
||||||
web_content.css.append(
|
web_content.css.append(
|
||||||
f"/_addons/{addon_package}/web/my-addon.css")
|
f"/_addons/{addon_package}/web/my-addon.css")
|
||||||
web_content.js.append(
|
web_content.js.append(
|
||||||
f"/_addons/{addon_package}/web/my-addon.js")
|
f"/_addons/{addon_package}/web/my-addon.js")
|
||||||
|
|
||||||
web_content.head += "<script>console.log('my-addon')</script>"
|
web_content.head += "<script>console.log('my-addon')</script>"
|
||||||
web_content.body += "<div id='my-addon'></div>"
|
web_content.body += "<div id='my-addon'></div>"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_hooks: List[Callable[["aqt.webview.WebContent", Optional[Any]], None]] = []
|
_hooks: List[Callable[["aqt.webview.WebContent", Optional[Any]], None]] = []
|
||||||
|
|
||||||
|
@ -92,7 +92,10 @@ def allroutes(pathin):
|
|||||||
try:
|
try:
|
||||||
directory, path = _redirectWebExports(pathin)
|
directory, path = _redirectWebExports(pathin)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return flask.make_response(f"Invalid path: {pathin}", HTTPStatus.FORBIDDEN,)
|
return flask.make_response(
|
||||||
|
f"Invalid path: {pathin}",
|
||||||
|
HTTPStatus.FORBIDDEN,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
isdir = os.path.isdir(os.path.join(directory, path))
|
isdir = os.path.isdir(os.path.join(directory, path))
|
||||||
@ -153,7 +156,10 @@ def allroutes(pathin):
|
|||||||
return flask.send_file(fullpath, mimetype=mimetype, conditional=True)
|
return flask.send_file(fullpath, mimetype=mimetype, conditional=True)
|
||||||
else:
|
else:
|
||||||
print(f"Not found: {ascii(pathin)}")
|
print(f"Not found: {ascii(pathin)}")
|
||||||
return flask.make_response(f"Invalid path: {pathin}", HTTPStatus.NOT_FOUND,)
|
return flask.make_response(
|
||||||
|
f"Invalid path: {pathin}",
|
||||||
|
HTTPStatus.NOT_FOUND,
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
if devMode:
|
if devMode:
|
||||||
@ -165,7 +171,10 @@ def allroutes(pathin):
|
|||||||
# swallow it - user likely surfed away from
|
# swallow it - user likely surfed away from
|
||||||
# review screen before an image had finished
|
# review screen before an image had finished
|
||||||
# downloading
|
# downloading
|
||||||
return flask.make_response(str(error), HTTPStatus.INTERNAL_SERVER_ERROR,)
|
return flask.make_response(
|
||||||
|
str(error),
|
||||||
|
HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _redirectWebExports(path):
|
def _redirectWebExports(path):
|
||||||
|
111
qt/aqt/mpv.py
111
qt/aqt/mpv.py
@ -76,7 +76,7 @@ if isWin:
|
|||||||
|
|
||||||
class MPVBase:
|
class MPVBase:
|
||||||
"""Base class for communication with the mpv media player via unix socket
|
"""Base class for communication with the mpv media player via unix socket
|
||||||
based JSON IPC.
|
based JSON IPC.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
executable = find_executable("mpv")
|
executable = find_executable("mpv")
|
||||||
@ -116,8 +116,7 @@ class MPVBase:
|
|||||||
# Process
|
# Process
|
||||||
#
|
#
|
||||||
def _prepare_process(self):
|
def _prepare_process(self):
|
||||||
"""Prepare the argument list for the mpv process.
|
"""Prepare the argument list for the mpv process."""
|
||||||
"""
|
|
||||||
self.argv = [self.executable]
|
self.argv = [self.executable]
|
||||||
self.argv += self.default_argv
|
self.argv += self.default_argv
|
||||||
self.argv += ["--input-ipc-server=" + self._sock_filename]
|
self.argv += ["--input-ipc-server=" + self._sock_filename]
|
||||||
@ -125,13 +124,11 @@ class MPVBase:
|
|||||||
self.argv += ["--wid=" + str(self.window_id)]
|
self.argv += ["--wid=" + str(self.window_id)]
|
||||||
|
|
||||||
def _start_process(self):
|
def _start_process(self):
|
||||||
"""Start the mpv process.
|
"""Start the mpv process."""
|
||||||
"""
|
|
||||||
self._proc = subprocess.Popen(self.argv, env=self.popenEnv)
|
self._proc = subprocess.Popen(self.argv, env=self.popenEnv)
|
||||||
|
|
||||||
def _stop_process(self):
|
def _stop_process(self):
|
||||||
"""Stop the mpv process.
|
"""Stop the mpv process."""
|
||||||
"""
|
|
||||||
if hasattr(self, "_proc"):
|
if hasattr(self, "_proc"):
|
||||||
try:
|
try:
|
||||||
self._proc.terminate()
|
self._proc.terminate()
|
||||||
@ -144,7 +141,7 @@ class MPVBase:
|
|||||||
#
|
#
|
||||||
def _prepare_socket(self):
|
def _prepare_socket(self):
|
||||||
"""Create a random socket filename which we pass to mpv with the
|
"""Create a random socket filename which we pass to mpv with the
|
||||||
--input-unix-socket option.
|
--input-unix-socket option.
|
||||||
"""
|
"""
|
||||||
if isWin:
|
if isWin:
|
||||||
self._sock_filename = "ankimpv"
|
self._sock_filename = "ankimpv"
|
||||||
@ -155,7 +152,7 @@ class MPVBase:
|
|||||||
|
|
||||||
def _start_socket(self):
|
def _start_socket(self):
|
||||||
"""Wait for the mpv process to create the unix socket and finish
|
"""Wait for the mpv process to create the unix socket and finish
|
||||||
startup.
|
startup.
|
||||||
"""
|
"""
|
||||||
start = time.time()
|
start = time.time()
|
||||||
while self.is_running() and time.time() < start + 10:
|
while self.is_running() and time.time() < start + 10:
|
||||||
@ -197,8 +194,7 @@ class MPVBase:
|
|||||||
raise MPVProcessError("unable to start process")
|
raise MPVProcessError("unable to start process")
|
||||||
|
|
||||||
def _stop_socket(self):
|
def _stop_socket(self):
|
||||||
"""Clean up the socket.
|
"""Clean up the socket."""
|
||||||
"""
|
|
||||||
if hasattr(self, "_sock"):
|
if hasattr(self, "_sock"):
|
||||||
self._sock.close()
|
self._sock.close()
|
||||||
if hasattr(self, "_sock_filename"):
|
if hasattr(self, "_sock_filename"):
|
||||||
@ -208,23 +204,20 @@ class MPVBase:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def _prepare_thread(self):
|
def _prepare_thread(self):
|
||||||
"""Set up the queues for the communication threads.
|
"""Set up the queues for the communication threads."""
|
||||||
"""
|
|
||||||
self._request_queue = Queue(1)
|
self._request_queue = Queue(1)
|
||||||
self._response_queues = {}
|
self._response_queues = {}
|
||||||
self._event_queue = Queue()
|
self._event_queue = Queue()
|
||||||
self._stop_event = threading.Event()
|
self._stop_event = threading.Event()
|
||||||
|
|
||||||
def _start_thread(self):
|
def _start_thread(self):
|
||||||
"""Start up the communication threads.
|
"""Start up the communication threads."""
|
||||||
"""
|
|
||||||
self._thread = threading.Thread(target=self._reader)
|
self._thread = threading.Thread(target=self._reader)
|
||||||
self._thread.daemon = True
|
self._thread.daemon = True
|
||||||
self._thread.start()
|
self._thread.start()
|
||||||
|
|
||||||
def _stop_thread(self):
|
def _stop_thread(self):
|
||||||
"""Stop the communication threads.
|
"""Stop the communication threads."""
|
||||||
"""
|
|
||||||
if hasattr(self, "_stop_event"):
|
if hasattr(self, "_stop_event"):
|
||||||
self._stop_event.set()
|
self._stop_event.set()
|
||||||
if hasattr(self, "_thread"):
|
if hasattr(self, "_thread"):
|
||||||
@ -232,7 +225,7 @@ class MPVBase:
|
|||||||
|
|
||||||
def _reader(self):
|
def _reader(self):
|
||||||
"""Read the incoming json messages from the unix socket that is
|
"""Read the incoming json messages from the unix socket that is
|
||||||
connected to the mpv process. Pass them on to the message handler.
|
connected to the mpv process. Pass them on to the message handler.
|
||||||
"""
|
"""
|
||||||
buf = b""
|
buf = b""
|
||||||
while not self._stop_event.is_set():
|
while not self._stop_event.is_set():
|
||||||
@ -276,22 +269,20 @@ class MPVBase:
|
|||||||
# Message handling
|
# Message handling
|
||||||
#
|
#
|
||||||
def _compose_message(self, message):
|
def _compose_message(self, message):
|
||||||
"""Return a json representation from a message dictionary.
|
"""Return a json representation from a message dictionary."""
|
||||||
"""
|
|
||||||
# XXX may be strict is too strict ;-)
|
# XXX may be strict is too strict ;-)
|
||||||
data = json.dumps(message)
|
data = json.dumps(message)
|
||||||
return data.encode("utf8", "strict") + b"\n"
|
return data.encode("utf8", "strict") + b"\n"
|
||||||
|
|
||||||
def _parse_message(self, data):
|
def _parse_message(self, data):
|
||||||
"""Return a message dictionary from a json representation.
|
"""Return a message dictionary from a json representation."""
|
||||||
"""
|
|
||||||
# XXX may be strict is too strict ;-)
|
# XXX may be strict is too strict ;-)
|
||||||
data = data.decode("utf8", "strict")
|
data = data.decode("utf8", "strict")
|
||||||
return json.loads(data)
|
return json.loads(data)
|
||||||
|
|
||||||
def _handle_message(self, message):
|
def _handle_message(self, message):
|
||||||
"""Handle different types of incoming messages, i.e. responses to
|
"""Handle different types of incoming messages, i.e. responses to
|
||||||
commands or asynchronous events.
|
commands or asynchronous events.
|
||||||
"""
|
"""
|
||||||
if "error" in message:
|
if "error" in message:
|
||||||
# This message is a reply to a request.
|
# This message is a reply to a request.
|
||||||
@ -311,8 +302,8 @@ class MPVBase:
|
|||||||
|
|
||||||
def _send_message(self, message, timeout=None):
|
def _send_message(self, message, timeout=None):
|
||||||
"""Send a message/command to the mpv process, message must be a
|
"""Send a message/command to the mpv process, message must be a
|
||||||
dictionary of the form {"command": ["arg1", "arg2", ...]}. Responses
|
dictionary of the form {"command": ["arg1", "arg2", ...]}. Responses
|
||||||
from the mpv process must be collected using _get_response().
|
from the mpv process must be collected using _get_response().
|
||||||
"""
|
"""
|
||||||
data = self._compose_message(message)
|
data = self._compose_message(message)
|
||||||
|
|
||||||
@ -348,8 +339,8 @@ class MPVBase:
|
|||||||
|
|
||||||
def _get_response(self, timeout=None):
|
def _get_response(self, timeout=None):
|
||||||
"""Collect the response message to a previous request. If there was an
|
"""Collect the response message to a previous request. If there was an
|
||||||
error a MPVCommandError exception is raised, otherwise the command
|
error a MPVCommandError exception is raised, otherwise the command
|
||||||
specific data is returned.
|
specific data is returned.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
message = self._response_queues[self._thread_id()].get(
|
message = self._response_queues[self._thread_id()].get(
|
||||||
@ -365,8 +356,8 @@ class MPVBase:
|
|||||||
|
|
||||||
def _get_event(self, timeout=None):
|
def _get_event(self, timeout=None):
|
||||||
"""Collect a single event message that has been received out-of-band
|
"""Collect a single event message that has been received out-of-band
|
||||||
from the mpv process. If a timeout is specified and there have not
|
from the mpv process. If a timeout is specified and there have not
|
||||||
been any events during that period, None is returned.
|
been any events during that period, None is returned.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._event_queue.get(block=timeout is not None, timeout=timeout)
|
return self._event_queue.get(block=timeout is not None, timeout=timeout)
|
||||||
@ -374,8 +365,7 @@ class MPVBase:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _send_request(self, message, timeout=None, _retry=1):
|
def _send_request(self, message, timeout=None, _retry=1):
|
||||||
"""Send a command to the mpv process and collect the result.
|
"""Send a command to the mpv process and collect the result."""
|
||||||
"""
|
|
||||||
self.ensure_running()
|
self.ensure_running()
|
||||||
try:
|
try:
|
||||||
self._send_message(message, timeout)
|
self._send_message(message, timeout)
|
||||||
@ -392,15 +382,14 @@ class MPVBase:
|
|||||||
|
|
||||||
def _register_callbacks(self):
|
def _register_callbacks(self):
|
||||||
"""Will be called after mpv restart to reinitialize callbacks
|
"""Will be called after mpv restart to reinitialize callbacks
|
||||||
defined in MPV subclass
|
defined in MPV subclass
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#
|
#
|
||||||
# Public API
|
# Public API
|
||||||
#
|
#
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
"""Return True if the mpv process is still active.
|
"""Return True if the mpv process is still active."""
|
||||||
"""
|
|
||||||
return self._proc.poll() is None
|
return self._proc.poll() is None
|
||||||
|
|
||||||
def ensure_running(self):
|
def ensure_running(self):
|
||||||
@ -417,8 +406,7 @@ class MPVBase:
|
|||||||
self._register_callbacks()
|
self._register_callbacks()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Shutdown the mpv process and our communication setup.
|
"""Shutdown the mpv process and our communication setup."""
|
||||||
"""
|
|
||||||
if self.is_running():
|
if self.is_running():
|
||||||
self._send_request({"command": ["quit"]}, timeout=1)
|
self._send_request({"command": ["quit"]}, timeout=1)
|
||||||
self._stop_process()
|
self._stop_process()
|
||||||
@ -429,22 +417,22 @@ class MPVBase:
|
|||||||
|
|
||||||
class MPV(MPVBase):
|
class MPV(MPVBase):
|
||||||
"""Class for communication with the mpv media player via unix socket
|
"""Class for communication with the mpv media player via unix socket
|
||||||
based JSON IPC. It adds a few usable methods and a callback API.
|
based JSON IPC. It adds a few usable methods and a callback API.
|
||||||
|
|
||||||
To automatically register methods as event callbacks, subclass this
|
To automatically register methods as event callbacks, subclass this
|
||||||
class and define specially named methods as follows:
|
class and define specially named methods as follows:
|
||||||
|
|
||||||
def on_file_loaded(self):
|
def on_file_loaded(self):
|
||||||
# This is called for every 'file-loaded' event.
|
# This is called for every 'file-loaded' event.
|
||||||
...
|
...
|
||||||
|
|
||||||
def on_property_time_pos(self, position):
|
def on_property_time_pos(self, position):
|
||||||
# This is called whenever the 'time-pos' property is updated.
|
# This is called whenever the 'time-pos' property is updated.
|
||||||
...
|
...
|
||||||
|
|
||||||
Please note that callbacks are executed inside a separate thread. The
|
Please note that callbacks are executed inside a separate thread. The
|
||||||
MPV class itself is completely thread-safe. Requests from different
|
MPV class itself is completely thread-safe. Requests from different
|
||||||
threads to the same MPV instance are synchronized.
|
threads to the same MPV instance are synchronized.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -498,8 +486,7 @@ class MPV(MPVBase):
|
|||||||
# Socket communication
|
# Socket communication
|
||||||
#
|
#
|
||||||
def _start_thread(self):
|
def _start_thread(self):
|
||||||
"""Start up the communication threads.
|
"""Start up the communication threads."""
|
||||||
"""
|
|
||||||
super()._start_thread()
|
super()._start_thread()
|
||||||
if not hasattr(self, "_event_thread"):
|
if not hasattr(self, "_event_thread"):
|
||||||
self._event_thread = threading.Thread(target=self._event_reader)
|
self._event_thread = threading.Thread(target=self._event_reader)
|
||||||
@ -510,8 +497,7 @@ class MPV(MPVBase):
|
|||||||
# Event/callback API
|
# Event/callback API
|
||||||
#
|
#
|
||||||
def _event_reader(self):
|
def _event_reader(self):
|
||||||
"""Collect incoming event messages and call the event handler.
|
"""Collect incoming event messages and call the event handler."""
|
||||||
"""
|
|
||||||
while True:
|
while True:
|
||||||
message = self._get_event(timeout=1)
|
message = self._get_event(timeout=1)
|
||||||
if message is None:
|
if message is None:
|
||||||
@ -520,8 +506,7 @@ class MPV(MPVBase):
|
|||||||
self._handle_event(message)
|
self._handle_event(message)
|
||||||
|
|
||||||
def _handle_event(self, message):
|
def _handle_event(self, message):
|
||||||
"""Lookup and call the callbacks for a particular event message.
|
"""Lookup and call the callbacks for a particular event message."""
|
||||||
"""
|
|
||||||
if not self._callbacks_initialized:
|
if not self._callbacks_initialized:
|
||||||
self._callbacks_queue.put(message)
|
self._callbacks_queue.put(message)
|
||||||
return
|
return
|
||||||
@ -538,8 +523,7 @@ class MPV(MPVBase):
|
|||||||
callback()
|
callback()
|
||||||
|
|
||||||
def register_callback(self, name, callback):
|
def register_callback(self, name, callback):
|
||||||
"""Register a function `callback` for the event `name`.
|
"""Register a function `callback` for the event `name`."""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
self.command("enable_event", name)
|
self.command("enable_event", name)
|
||||||
except MPVCommandError:
|
except MPVCommandError:
|
||||||
@ -549,7 +533,7 @@ class MPV(MPVBase):
|
|||||||
|
|
||||||
def unregister_callback(self, name, callback):
|
def unregister_callback(self, name, callback):
|
||||||
"""Unregister a previously registered function `callback` for the event
|
"""Unregister a previously registered function `callback` for the event
|
||||||
`name`.
|
`name`.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
callbacks = self._callbacks[name]
|
callbacks = self._callbacks[name]
|
||||||
@ -563,7 +547,7 @@ class MPV(MPVBase):
|
|||||||
|
|
||||||
def register_property_callback(self, name, callback):
|
def register_property_callback(self, name, callback):
|
||||||
"""Register a function `callback` for the property-change event on
|
"""Register a function `callback` for the property-change event on
|
||||||
property `name`.
|
property `name`.
|
||||||
"""
|
"""
|
||||||
# Property changes are normally not sent over the connection unless they
|
# Property changes are normally not sent over the connection unless they
|
||||||
# are requested using the 'observe_property' command.
|
# are requested using the 'observe_property' command.
|
||||||
@ -585,7 +569,7 @@ class MPV(MPVBase):
|
|||||||
|
|
||||||
def unregister_property_callback(self, name, callback):
|
def unregister_property_callback(self, name, callback):
|
||||||
"""Unregister a previously registered function `callback` for the
|
"""Unregister a previously registered function `callback` for the
|
||||||
property-change event on property `name`.
|
property-change event on property `name`.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
callbacks = self._callbacks["property-" + name]
|
callbacks = self._callbacks["property-" + name]
|
||||||
@ -606,18 +590,15 @@ class MPV(MPVBase):
|
|||||||
# Public API
|
# Public API
|
||||||
#
|
#
|
||||||
def command(self, *args, timeout=1):
|
def command(self, *args, timeout=1):
|
||||||
"""Execute a single command on the mpv process and return the result.
|
"""Execute a single command on the mpv process and return the result."""
|
||||||
"""
|
|
||||||
return self._send_request({"command": list(args)}, timeout=timeout)
|
return self._send_request({"command": list(args)}, timeout=timeout)
|
||||||
|
|
||||||
def get_property(self, name):
|
def get_property(self, name):
|
||||||
"""Return the value of property `name`.
|
"""Return the value of property `name`."""
|
||||||
"""
|
|
||||||
return self.command("get_property", name)
|
return self.command("get_property", name)
|
||||||
|
|
||||||
def set_property(self, name, value):
|
def set_property(self, name, value):
|
||||||
"""Set the value of property `name`.
|
"""Set the value of property `name`."""
|
||||||
"""
|
|
||||||
return self.command("set_property", name, value)
|
return self.command("set_property", name, value)
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,7 +119,10 @@ class Previewer(QDialog):
|
|||||||
"reviewer.js",
|
"reviewer.js",
|
||||||
]
|
]
|
||||||
self._web.stdHtml(
|
self._web.stdHtml(
|
||||||
self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc, context=self,
|
self.mw.reviewer.revHtml(),
|
||||||
|
css=["reviewer.css"],
|
||||||
|
js=jsinc,
|
||||||
|
context=self,
|
||||||
)
|
)
|
||||||
self._web.set_bridge_command(self._on_bridge_cmd, self)
|
self._web.set_bridge_command(self._on_bridge_cmd, self)
|
||||||
|
|
||||||
|
@ -146,7 +146,9 @@ class ProfileManager:
|
|||||||
app = QtWidgets.QApplication([])
|
app = QtWidgets.QApplication([])
|
||||||
icon = QtGui.QIcon()
|
icon = QtGui.QIcon()
|
||||||
icon.addPixmap(
|
icon.addPixmap(
|
||||||
QtGui.QPixmap(":/icons/anki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off,
|
QtGui.QPixmap(":/icons/anki.png"),
|
||||||
|
QtGui.QIcon.Normal,
|
||||||
|
QtGui.QIcon.Off,
|
||||||
)
|
)
|
||||||
window_title = "Data Folder Migration"
|
window_title = "Data Folder Migration"
|
||||||
migration_directories = f"\n\n {oldBase}\n\nto\n\n {self.base}"
|
migration_directories = f"\n\n {oldBase}\n\nto\n\n {self.base}"
|
||||||
|
@ -77,7 +77,8 @@ def on_normal_sync_timer(mw: aqt.main.AnkiQt) -> None:
|
|||||||
|
|
||||||
assert isinstance(progress.val, NormalSyncProgress)
|
assert isinstance(progress.val, NormalSyncProgress)
|
||||||
mw.progress.update(
|
mw.progress.update(
|
||||||
label=f"{progress.val.added}\n{progress.val.removed}", process=False,
|
label=f"{progress.val.added}\n{progress.val.removed}",
|
||||||
|
process=False,
|
||||||
)
|
)
|
||||||
mw.progress.set_title(progress.val.stage)
|
mw.progress.set_title(progress.val.stage)
|
||||||
|
|
||||||
|
@ -412,7 +412,10 @@ hooks = [
|
|||||||
),
|
),
|
||||||
Hook(
|
Hook(
|
||||||
name="webview_will_set_content",
|
name="webview_will_set_content",
|
||||||
args=["web_content: aqt.webview.WebContent", "context: Optional[Any]",],
|
args=[
|
||||||
|
"web_content: aqt.webview.WebContent",
|
||||||
|
"context: Optional[Any]",
|
||||||
|
],
|
||||||
doc="""Used to modify web content before it is rendered.
|
doc="""Used to modify web content before it is rendered.
|
||||||
|
|
||||||
Web_content contains the HTML, JS, and CSS the web view will be
|
Web_content contains the HTML, JS, and CSS the web view will be
|
||||||
@ -526,7 +529,8 @@ hooks = [
|
|||||||
doc="""Executed when the top toolbar is redrawn""",
|
doc="""Executed when the top toolbar is redrawn""",
|
||||||
),
|
),
|
||||||
Hook(
|
Hook(
|
||||||
name="media_sync_did_progress", args=["entry: aqt.mediasync.LogEntryWithTime"],
|
name="media_sync_did_progress",
|
||||||
|
args=["entry: aqt.mediasync.LogEntryWithTime"],
|
||||||
),
|
),
|
||||||
Hook(name="media_sync_did_start_or_stop", args=["running: bool"]),
|
Hook(name="media_sync_did_start_or_stop", args=["running: bool"]),
|
||||||
Hook(
|
Hook(
|
||||||
@ -541,7 +545,10 @@ hooks = [
|
|||||||
args=["addcards: aqt.addcards.AddCards", "menu: QMenu"],
|
args=["addcards: aqt.addcards.AddCards", "menu: QMenu"],
|
||||||
legacy_hook="AddCards.onHistory",
|
legacy_hook="AddCards.onHistory",
|
||||||
),
|
),
|
||||||
Hook(name="add_cards_did_init", args=["addcards: aqt.addcards.AddCards"],),
|
Hook(
|
||||||
|
name="add_cards_did_init",
|
||||||
|
args=["addcards: aqt.addcards.AddCards"],
|
||||||
|
),
|
||||||
Hook(
|
Hook(
|
||||||
name="add_cards_did_add_note",
|
name="add_cards_did_add_note",
|
||||||
args=["note: anki.notes.Note"],
|
args=["note: anki.notes.Note"],
|
||||||
@ -623,7 +630,10 @@ hooks = [
|
|||||||
name="editor_web_view_did_init",
|
name="editor_web_view_did_init",
|
||||||
args=["editor_web_view: aqt.editor.EditorWebView"],
|
args=["editor_web_view: aqt.editor.EditorWebView"],
|
||||||
),
|
),
|
||||||
Hook(name="editor_did_init", args=["editor: aqt.editor.Editor"],),
|
Hook(
|
||||||
|
name="editor_did_init",
|
||||||
|
args=["editor: aqt.editor.Editor"],
|
||||||
|
),
|
||||||
Hook(
|
Hook(
|
||||||
name="editor_will_load_note",
|
name="editor_will_load_note",
|
||||||
args=["js: str", "note: anki.notes.Note", "editor: aqt.editor.Editor"],
|
args=["js: str", "note: anki.notes.Note", "editor: aqt.editor.Editor"],
|
||||||
@ -675,7 +685,10 @@ hooks = [
|
|||||||
),
|
),
|
||||||
# Model
|
# Model
|
||||||
###################
|
###################
|
||||||
Hook(name="models_advanced_will_show", args=["advanced: QDialog"],),
|
Hook(
|
||||||
|
name="models_advanced_will_show",
|
||||||
|
args=["advanced: QDialog"],
|
||||||
|
),
|
||||||
# Stats
|
# Stats
|
||||||
###################
|
###################
|
||||||
Hook(
|
Hook(
|
||||||
|
Loading…
Reference in New Issue
Block a user