bfef908c6c
* Refactor media sync handling - The media USN is now returned in sync/meta, which avoids an extra round-trip. - Media syncing is now automatically started by the syncing code at the end of a normal or full sync, which avoids it competing for bandwidth and resources, and avoids duplicate invalid login messages when the auth token is invalid. - Added a new media_sync_progress() method to both check if media is syncing, and get access to the latest progress. - Updated the sync log screen to only show the latest line, like AnkiMobile. - Show media sync errors in a pop-up, so they don't get missed. Use a non-modal pop-up to avoid potential conflicts with other modals. * Remove print statement
1324 lines
46 KiB
Python
1324 lines
46 KiB
Python
# Copyright: Ankitects Pty Ltd and contributors
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
"""
|
|
See pylib/tools/genhooks.py for more info.
|
|
"""
|
|
|
|
import sys
|
|
|
|
sys.path.append("pylib/tools")
|
|
|
|
from hookslib import Hook, write_file
|
|
|
|
prefix = """\
|
|
# Copyright: Ankitects Pty Ltd and contributors
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
# This file is automatically generated; edit tools/genhooks_gui.py instead.
|
|
# Please import from anki.hooks instead of this file.
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Callable, Sequence, Literal, Type
|
|
|
|
import anki
|
|
import aqt
|
|
from anki.cards import Card
|
|
from anki.decks import DeckDict, DeckConfigDict
|
|
from anki.hooks import runFilter, runHook
|
|
from anki.models import NotetypeDict
|
|
from anki.collection import OpChangesAfterUndo
|
|
from aqt.qt import QDialog, QEvent, QMenu, QModelIndex, QWidget, QMimeData
|
|
from aqt.tagedit import TagEdit
|
|
from aqt.undo import UndoActionsInfo
|
|
"""
|
|
|
|
# Hook list
|
|
######################################################################
|
|
|
|
hooks = [
|
|
# Reviewing
|
|
###################
|
|
Hook(
|
|
name="overview_did_refresh",
|
|
args=["overview: aqt.overview.Overview"],
|
|
doc="""Allow to update the overview window. E.g. add the deck name in the
|
|
title.""",
|
|
),
|
|
Hook(
|
|
name="overview_will_render_content",
|
|
args=[
|
|
"overview: aqt.overview.Overview",
|
|
"content: aqt.overview.OverviewContent",
|
|
],
|
|
doc="""Used to modify HTML content sections in the overview body
|
|
|
|
'content' contains the sections of HTML content the overview body
|
|
will be updated with.
|
|
|
|
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.
|
|
You should avoid overwriting or interfering with existing data as much
|
|
as possible, instead opting to append your own changes, e.g.:
|
|
|
|
def on_overview_will_render_content(overview, content):
|
|
content.table += "\n<div>my html</div>"
|
|
""",
|
|
),
|
|
Hook(
|
|
name="overview_will_render_bottom",
|
|
args=[
|
|
"link_handler: Callable[[str], bool]",
|
|
"links: list[list[str]]",
|
|
],
|
|
return_type="Callable[[str], bool]",
|
|
doc="""Allows adding buttons to the Overview bottom bar.
|
|
|
|
Append a list of strings to 'links' argument to add new buttons.
|
|
- The first value is the shortcut to appear in the tooltip.
|
|
- The second value is the url to be triggered.
|
|
- The third value is the text of the new button.
|
|
|
|
Extend the callable 'link_handler' to handle new urls. This callable
|
|
accepts one argument: the triggered url.
|
|
Make a check of the triggered url, call any functions related to
|
|
that trigger, and return the new link_handler.
|
|
|
|
Example:
|
|
links.append(['H', 'hello', 'Click me!'])
|
|
def custom_link_handler(url):
|
|
if url == 'hello':
|
|
print('Hello World!')
|
|
return link_handler(url=url)
|
|
return custom_link_handler
|
|
""",
|
|
),
|
|
Hook(
|
|
name="reviewer_did_show_question",
|
|
args=["card: Card"],
|
|
legacy_hook="showQuestion",
|
|
legacy_no_args=True,
|
|
),
|
|
Hook(
|
|
name="reviewer_did_show_answer",
|
|
args=["card: Card"],
|
|
legacy_hook="showAnswer",
|
|
legacy_no_args=True,
|
|
),
|
|
Hook(
|
|
name="reviewer_will_init_answer_buttons",
|
|
args=[
|
|
"buttons_tuple: tuple[tuple[int, str], ...]",
|
|
"reviewer: aqt.reviewer.Reviewer",
|
|
"card: Card",
|
|
],
|
|
return_type="tuple[tuple[int, str], ...]",
|
|
doc="""Used to modify list of answer buttons
|
|
|
|
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
|
|
button's label.
|
|
|
|
Return a tuple of the form ((int, str), ...), e.g.:
|
|
((1, "Label1"), (2, "Label2"), ...)
|
|
|
|
Note: import _ from anki.lang to support translation, using, e.g.,
|
|
((1, _("Label1")), ...)
|
|
""",
|
|
),
|
|
Hook(
|
|
name="reviewer_will_answer_card",
|
|
args=[
|
|
"ease_tuple: tuple[bool, Literal[1, 2, 3, 4]]",
|
|
"reviewer: aqt.reviewer.Reviewer",
|
|
"card: Card",
|
|
],
|
|
return_type="tuple[bool, Literal[1, 2, 3, 4]]",
|
|
doc="""Used to modify the ease at which a card is rated or to bypass
|
|
rating the card completely.
|
|
|
|
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
|
|
which the card should be rated.
|
|
|
|
If your code just needs to be notified of the card rating event, you should use
|
|
the reviewer_did_answer_card hook instead.""",
|
|
),
|
|
Hook(
|
|
name="reviewer_did_answer_card",
|
|
args=[
|
|
"reviewer: aqt.reviewer.Reviewer",
|
|
"card: Card",
|
|
"ease: Literal[1, 2, 3, 4]",
|
|
],
|
|
),
|
|
Hook(
|
|
name="reviewer_will_show_context_menu",
|
|
args=["reviewer: aqt.reviewer.Reviewer", "menu: QMenu"],
|
|
legacy_hook="Reviewer.contextMenuEvent",
|
|
),
|
|
Hook(
|
|
name="reviewer_will_end",
|
|
legacy_hook="reviewCleanup",
|
|
doc="Called before Anki transitions from the review screen to another screen.",
|
|
),
|
|
Hook(
|
|
name="reviewer_will_play_question_sounds",
|
|
args=["card: Card", "tags: list[anki.sound.AVTag]"],
|
|
doc="""Called before showing the question/front side.
|
|
|
|
`tags` can be used to inspect and manipulate the sounds
|
|
that will be played (if any).
|
|
|
|
This won't be called when the user manually plays sounds
|
|
using `Replay Audio`.
|
|
|
|
Note that this hook is called even when the `Automatically play audio`
|
|
option is unchecked; This is so as to allow playing custom
|
|
sounds regardless of that option.""",
|
|
),
|
|
Hook(
|
|
name="reviewer_will_play_answer_sounds",
|
|
args=["card: Card", "tags: list[anki.sound.AVTag]"],
|
|
doc="""Called before showing the answer/back side.
|
|
|
|
`tags` can be used to inspect and manipulate the sounds
|
|
that will be played (if any).
|
|
|
|
This won't be called when the user manually plays sounds
|
|
using `Replay Audio`.
|
|
|
|
Note that this hook is called even when the `Automatically play audio`
|
|
option is unchecked; This is so as to allow playing custom
|
|
sounds regardless of that option.""",
|
|
),
|
|
Hook(
|
|
name="reviewer_will_replay_recording",
|
|
args=["path: str"],
|
|
return_type="str",
|
|
doc="""Used to inspect and modify a recording recorded by "Record Own Voice" before replaying.""",
|
|
),
|
|
Hook(
|
|
name="reviewer_will_suspend_note",
|
|
args=["nid: int"],
|
|
),
|
|
Hook(
|
|
name="reviewer_will_suspend_card",
|
|
args=["id: int"],
|
|
),
|
|
Hook(
|
|
name="reviewer_will_bury_note",
|
|
args=["nid: int"],
|
|
),
|
|
Hook(
|
|
name="reviewer_will_bury_card",
|
|
args=["id: int"],
|
|
),
|
|
Hook(
|
|
name="audio_did_pause_or_unpause",
|
|
args=["webview: aqt.webview.AnkiWebView"],
|
|
doc="""Called when the audio is paused or unpaused.
|
|
This hook is triggered by the action in Anki's More menu or the related key binding.
|
|
The webview is provided in case you wish to use this hook with web-based audio.""",
|
|
),
|
|
Hook(
|
|
name="audio_did_seek_relative",
|
|
args=["webview: aqt.webview.AnkiWebView", "seek_seconds: int"],
|
|
doc="""Called when the audio is sought forward (positive seek) or backwards (negative seek).
|
|
This hook is triggered by the action in Anki's More menu or the related key binding.
|
|
The webview is provided in case you wish to use this hook with web-based audio.""",
|
|
),
|
|
# Debug
|
|
###################
|
|
Hook(
|
|
name="debug_console_will_show",
|
|
args=["debug_window: QDialog"],
|
|
doc="""Allows editing the debug window. E.g. setting a default code, or
|
|
previous code.""",
|
|
),
|
|
Hook(
|
|
name="debug_console_did_evaluate_python",
|
|
args=["output: str", "query: str", "debug_window: aqt.forms.debug.Ui_Dialog"],
|
|
return_type="str",
|
|
doc="""Allows processing the debug result. E.g. logging queries and
|
|
result, saving last query to display it later...""",
|
|
),
|
|
# Card layout
|
|
###################
|
|
Hook(
|
|
name="card_layout_will_show",
|
|
args=["clayout: aqt.clayout.CardLayout"],
|
|
doc="""Allow to change the display of the card layout. After most values are
|
|
set and before the window is actually shown.""",
|
|
),
|
|
# Reviewer
|
|
###################
|
|
Hook(
|
|
name="reviewer_did_init",
|
|
args=["reviewer: aqt.reviewer.Reviewer"],
|
|
doc="""Called after the reviewer is initialized.""",
|
|
),
|
|
# Multiple windows
|
|
###################
|
|
# reviewer and previewer
|
|
Hook(
|
|
name="audio_will_replay",
|
|
args=["webview: aqt.webview.AnkiWebView", "card: Card", "is_front_side: bool"],
|
|
doc="""Called when the user uses the 'replay audio' action, but not when they click on a play button.""",
|
|
),
|
|
# reviewer, clayout and browser
|
|
Hook(
|
|
name="card_will_show",
|
|
args=["text: str", "card: Card", "kind: str"],
|
|
return_type="str",
|
|
legacy_hook="prepareQA",
|
|
doc="Can modify card text before review/preview.",
|
|
),
|
|
# reviewer, main and clayout
|
|
Hook(
|
|
name="card_review_webview_did_init",
|
|
args=[
|
|
"webview: aqt.webview.AnkiWebView",
|
|
"kind: aqt.webview.AnkiWebViewKind",
|
|
],
|
|
doc="Called when initializing the webview for the review screen, the card layout screen, and the preview screen.",
|
|
),
|
|
# Deck browser
|
|
###################
|
|
Hook(
|
|
name="deck_browser_did_render",
|
|
args=["deck_browser: aqt.deckbrowser.DeckBrowser"],
|
|
doc="""Allow to update the deck browser window. E.g. change its title.""",
|
|
),
|
|
Hook(
|
|
name="deck_browser_will_render_content",
|
|
args=[
|
|
"deck_browser: aqt.deckbrowser.DeckBrowser",
|
|
"content: aqt.deckbrowser.DeckBrowserContent",
|
|
],
|
|
doc="""Used to modify HTML content sections in the deck browser body
|
|
|
|
'content' contains the sections of HTML content the deck browser body
|
|
will be updated with.
|
|
|
|
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.
|
|
You should avoid overwriting or interfering with existing data as much
|
|
as possible, instead opting to append your own changes, e.g.:
|
|
|
|
def on_deck_browser_will_render_content(deck_browser, content):
|
|
content.stats += "\\n<div>my html</div>"
|
|
""",
|
|
),
|
|
# Deck options (legacy screen)
|
|
###############################
|
|
Hook(
|
|
name="deck_conf_did_setup_ui_form",
|
|
args=["deck_conf: aqt.deckconf.DeckConf"],
|
|
doc="Allows modifying or adding widgets in the deck options UI form",
|
|
),
|
|
Hook(
|
|
name="deck_conf_will_show",
|
|
args=["deck_conf: aqt.deckconf.DeckConf"],
|
|
doc="Allows modifying the deck options dialog before it is shown",
|
|
),
|
|
Hook(
|
|
name="deck_conf_did_load_config",
|
|
args=[
|
|
"deck_conf: aqt.deckconf.DeckConf",
|
|
"deck: DeckDict",
|
|
"config: DeckConfigDict",
|
|
],
|
|
doc="Called once widget state has been set from deck config",
|
|
),
|
|
Hook(
|
|
name="deck_conf_will_save_config",
|
|
args=[
|
|
"deck_conf: aqt.deckconf.DeckConf",
|
|
"deck: DeckDict",
|
|
"config: DeckConfigDict",
|
|
],
|
|
doc="Called before widget state is saved to config",
|
|
),
|
|
Hook(
|
|
name="deck_conf_did_add_config",
|
|
args=[
|
|
"deck_conf: aqt.deckconf.DeckConf",
|
|
"deck: DeckDict",
|
|
"config: DeckConfigDict",
|
|
"new_name: str",
|
|
"new_conf_id: int",
|
|
],
|
|
doc="""Allows modification of a newly created config group
|
|
|
|
This hook is called after the config group was created, but
|
|
before initializing the widget state.
|
|
|
|
`deck_conf` will point to the old config group, `new_conf_id` will
|
|
point to the newly created config group.
|
|
|
|
Config groups are created as clones of the current one.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="deck_conf_will_remove_config",
|
|
args=[
|
|
"deck_conf: aqt.deckconf.DeckConf",
|
|
"deck: DeckDict",
|
|
"config: DeckConfigDict",
|
|
],
|
|
doc="Called before current config group is removed",
|
|
),
|
|
Hook(
|
|
name="deck_conf_will_rename_config",
|
|
args=[
|
|
"deck_conf: aqt.deckconf.DeckConf",
|
|
"deck: DeckDict",
|
|
"config: DeckConfigDict",
|
|
"new_name: str",
|
|
],
|
|
doc="Called before config group is renamed",
|
|
),
|
|
# Deck options (new screen)
|
|
############################
|
|
Hook(
|
|
name="deck_options_did_load",
|
|
args=[
|
|
"deck_options: aqt.deckoptions.DeckOptionsDialog",
|
|
],
|
|
doc="""Can be used to inject extra options into the config screen.
|
|
|
|
See the example add-ons at:
|
|
https://github.com/ankitects/anki-addons/tree/main/demos/deckoptions_svelte
|
|
https://github.com/ankitects/anki-addons/tree/main/demos/deckoptions_raw_html
|
|
""",
|
|
),
|
|
# Filtered deck options
|
|
###################
|
|
Hook(
|
|
name="filtered_deck_dialog_did_load_deck",
|
|
args=[
|
|
"filtered_deck_dialog: aqt.filtered_deck.FilteredDeckConfigDialog",
|
|
"filtered_deck: anki.scheduler.FilteredDeckForUpdate",
|
|
],
|
|
doc="Allows updating widget state once the filtered deck config is loaded",
|
|
),
|
|
Hook(
|
|
name="filtered_deck_dialog_will_add_or_update_deck",
|
|
args=[
|
|
"filtered_deck_dialog: aqt.filtered_deck.FilteredDeckConfigDialog",
|
|
"filtered_deck: anki.scheduler.FilteredDeckForUpdate",
|
|
],
|
|
doc="Allows modifying the filtered deck config object before it is written",
|
|
),
|
|
Hook(
|
|
name="filtered_deck_dialog_did_add_or_update_deck",
|
|
args=[
|
|
"filtered_deck_dialog: aqt.filtered_deck.FilteredDeckConfigDialog",
|
|
"filtered_deck: anki.scheduler.FilteredDeckForUpdate",
|
|
"deck_id: int",
|
|
],
|
|
doc="Allows performing changes after a filtered deck has been added or updated",
|
|
),
|
|
# Browser
|
|
###################
|
|
Hook(
|
|
name="default_search",
|
|
args=["current_search: str", "c: Card"],
|
|
return_type="str",
|
|
doc="Change the default search when the card browser is opened with card `c`.",
|
|
),
|
|
Hook(name="browser_will_show", args=["browser: aqt.browser.Browser"]),
|
|
Hook(
|
|
name="browser_menus_did_init",
|
|
args=["browser: aqt.browser.Browser"],
|
|
legacy_hook="browser.setupMenus",
|
|
),
|
|
Hook(
|
|
name="browser_will_show_context_menu",
|
|
args=["browser: aqt.browser.Browser", "menu: QMenu"],
|
|
legacy_hook="browser.onContextMenu",
|
|
),
|
|
Hook(
|
|
name="browser_sidebar_will_show_context_menu",
|
|
args=[
|
|
"sidebar: aqt.browser.SidebarTreeView",
|
|
"menu: QMenu",
|
|
"item: aqt.browser.SidebarItem",
|
|
"index: QModelIndex",
|
|
],
|
|
),
|
|
Hook(
|
|
name="browser_header_will_show_context_menu",
|
|
args=["browser: aqt.browser.Browser", "menu: QMenu"],
|
|
),
|
|
Hook(
|
|
name="browser_did_change_row",
|
|
args=["browser: aqt.browser.Browser"],
|
|
legacy_hook="browser.rowChanged",
|
|
),
|
|
Hook(
|
|
name="browser_will_build_tree",
|
|
args=[
|
|
"handled: bool",
|
|
"tree: aqt.browser.SidebarItem",
|
|
"stage: aqt.browser.SidebarStage",
|
|
"browser: aqt.browser.Browser",
|
|
],
|
|
return_type="bool",
|
|
doc="""Used to add or replace items in the browser sidebar tree
|
|
|
|
'tree' is the root SidebarItem that all other items are added to.
|
|
|
|
'stage' is an enum describing the different construction stages of
|
|
the sidebar tree at which you can interject your changes.
|
|
The different values can be inspected by looking at
|
|
aqt.browser.SidebarStage.
|
|
|
|
If you want Anki to proceed with the construction of the tree stage
|
|
in question after your have performed your changes or additions,
|
|
return the 'handled' boolean unchanged.
|
|
|
|
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
|
|
implements its own version of that particular stage), return 'True'.
|
|
|
|
If you return 'True' at SidebarStage.ROOT, the sidebar will not be
|
|
populated by any of the other construction stages. For any other stage
|
|
the tree construction will just continue as usual.
|
|
|
|
For example, if your code wishes to replace the tag tree, you could do:
|
|
|
|
def on_browser_will_build_tree(handled, root, stage, browser):
|
|
if stage != SidebarStage.TAGS:
|
|
# not at tag tree building stage, pass on
|
|
return handled
|
|
|
|
# your tag tree construction code
|
|
# root.addChild(...)
|
|
|
|
# your code handled tag tree construction, no need for Anki
|
|
# or other add-ons to build the tag tree
|
|
return True
|
|
""",
|
|
),
|
|
Hook(
|
|
name="browser_will_search",
|
|
args=["context: aqt.browser.SearchContext"],
|
|
doc="""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
|
|
searching backend.
|
|
|
|
If you set context.ids to a list of ids, the regular search will
|
|
not be performed, and the provided ids will be used instead.
|
|
|
|
Your add-on should check if context.ids is not None, and return
|
|
without making changes if it has been set.
|
|
|
|
In versions of Anki lower than 2.1.45 the field to check is
|
|
context.card_ids rather than context.ids
|
|
""",
|
|
),
|
|
Hook(
|
|
name="browser_did_search",
|
|
args=["context: aqt.browser.SearchContext"],
|
|
doc="""Allows you to modify the list of returned card ids from a search.""",
|
|
),
|
|
Hook(
|
|
name="browser_did_fetch_row",
|
|
args=[
|
|
"card_or_note_id: aqt.browser.ItemId",
|
|
"is_note: bool",
|
|
"row: aqt.browser.CellRow",
|
|
"columns: Sequence[str]",
|
|
],
|
|
doc="""Allows you to add or modify content to a row in the browser.
|
|
|
|
You can mutate the row object to change what is displayed. Any columns the
|
|
backend did not recognize will be returned as an empty string, and can be
|
|
replaced with custom content.
|
|
|
|
Columns is a list of string values identifying what each column in the row
|
|
represents.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="browser_did_fetch_columns",
|
|
args=["columns: dict[str, aqt.browser.Column]"],
|
|
doc="""Allows you to add custom columns to the browser.
|
|
|
|
columns is a dictionary of data objects. You can add an entry with a custom
|
|
column to describe how it should be displayed in the browser or modify
|
|
existing entries.
|
|
|
|
Every column in the dictionary will be toggleable by the user.
|
|
""",
|
|
),
|
|
# Previewer
|
|
###################
|
|
Hook(
|
|
name="previewer_did_init",
|
|
args=["previewer: aqt.browser.previewer.Previewer"],
|
|
doc="""Called after the previewer is initialized.""",
|
|
),
|
|
Hook(
|
|
name="previewer_will_redraw_after_show_both_sides_toggled",
|
|
args=[
|
|
"webview: aqt.webview.AnkiWebView",
|
|
"card: Card",
|
|
"is_front_side: bool",
|
|
"show_both_sides: bool",
|
|
],
|
|
doc="""Called when the checkbox <show both sides> is toggled by the user.""",
|
|
),
|
|
# Main window states
|
|
###################
|
|
# these refer to things like deckbrowser, overview and reviewer state,
|
|
Hook(
|
|
name="state_will_change",
|
|
args=[
|
|
"new_state: aqt.main.MainWindowState",
|
|
"old_state: aqt.main.MainWindowState",
|
|
],
|
|
legacy_hook="beforeStateChange",
|
|
),
|
|
Hook(
|
|
name="state_did_change",
|
|
args=[
|
|
"new_state: aqt.main.MainWindowState",
|
|
"old_state: aqt.main.MainWindowState",
|
|
],
|
|
legacy_hook="afterStateChange",
|
|
),
|
|
# different sig to original
|
|
Hook(
|
|
name="state_shortcuts_will_change",
|
|
args=[
|
|
"state: aqt.main.MainWindowState",
|
|
"shortcuts: list[tuple[str, Callable]]",
|
|
],
|
|
),
|
|
# UI state/refreshing
|
|
###################
|
|
Hook(
|
|
name="state_did_revert",
|
|
args=["action: str"],
|
|
legacy_hook="revertedState",
|
|
doc="Legacy hook, called after undoing.",
|
|
),
|
|
Hook(
|
|
name="state_did_undo",
|
|
args=["changes: OpChangesAfterUndo"],
|
|
doc="Called after backend undoes a change.",
|
|
),
|
|
Hook(
|
|
name="state_did_reset",
|
|
legacy_hook="reset",
|
|
doc="""Legacy 'reset' hook. Called by mw.reset() and CollectionOp() to redraw the UI.
|
|
|
|
New code should use `operation_did_execute` instead.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="operation_did_execute",
|
|
args=["changes: anki.collection.OpChanges", "handler: object | None"],
|
|
doc="""Called after an operation completes.
|
|
Changes can be inspected to determine whether the UI needs updating.
|
|
|
|
This will also be called when the legacy mw.reset() is used.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="focus_did_change",
|
|
args=[
|
|
"new: QWidget | None",
|
|
"old: QWidget | None",
|
|
],
|
|
doc="""Called each time the focus changes. Can be used to defer updates from
|
|
`operation_did_execute` until a window is brought to the front.""",
|
|
),
|
|
Hook(
|
|
name="backend_will_block",
|
|
doc="""Called before one or more DB tasks are run in the background.
|
|
|
|
Subscribers can use this to set a flag to avoid DB queries until the operation
|
|
completes, as doing so will freeze the UI until the long-running operation
|
|
completes.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="backend_did_block",
|
|
doc="""Called after one or more DB tasks finish running in the background.
|
|
Called regardless of the success of individual operations, and only called when
|
|
there are no outstanding ops.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="theme_did_change",
|
|
doc="Called after night mode is toggled.",
|
|
),
|
|
Hook(
|
|
name="body_classes_need_update",
|
|
doc="Called when a setting involving a webview body class is toggled.",
|
|
),
|
|
# Webview
|
|
###################
|
|
Hook(
|
|
name="webview_did_receive_js_message",
|
|
args=["handled: tuple[bool, Any]", "message: str", "context: Any"],
|
|
return_type="tuple[bool, Any]",
|
|
doc="""Used to handle pycmd() messages sent from Javascript.
|
|
|
|
Message is the string passed to pycmd().
|
|
|
|
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
|
|
bridge command handler, return (True, None).
|
|
|
|
If you want to pass a value to pycmd's result callback, you can
|
|
return it with (True, some_value).
|
|
|
|
Context is the instance that was passed to set_bridge_command().
|
|
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
|
|
code wishes to function only in the review screen, you could do:
|
|
|
|
if not isinstance(context, aqt.reviewer.Reviewer):
|
|
# not reviewer, pass on message
|
|
return handled
|
|
|
|
if message == "my-mark-action":
|
|
# our message, call onMark() on the reviewer instance
|
|
context.onMark()
|
|
# and don't pass message to other handlers
|
|
return (True, None)
|
|
else:
|
|
# some other command, pass it on
|
|
return handled
|
|
""",
|
|
),
|
|
Hook(
|
|
name="webview_will_set_content",
|
|
args=[
|
|
"web_content: aqt.webview.WebContent",
|
|
"context: object | None",
|
|
],
|
|
doc="""Used to modify web content before it is rendered.
|
|
|
|
Web_content contains the HTML, JS, and CSS the web view will be
|
|
populated with.
|
|
|
|
Context is the instance that was passed to stdHtml().
|
|
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
|
|
code wishes to function only in the review screen, you could do:
|
|
|
|
def on_webview_will_set_content(web_content: WebContent, context):
|
|
|
|
if not isinstance(context, aqt.reviewer.Reviewer):
|
|
# not reviewer, do not modify content
|
|
return
|
|
|
|
# reviewer, perform changes to content
|
|
|
|
context: aqt.reviewer.Reviewer
|
|
|
|
addon_package = mw.addonManager.addonFromModule(__name__)
|
|
|
|
web_content.css.append(
|
|
f"/_addons/{addon_package}/web/my-addon.css")
|
|
web_content.js.append(
|
|
f"/_addons/{addon_package}/web/my-addon.js")
|
|
|
|
web_content.head += "<script>console.log('my-addon')</script>"
|
|
web_content.body += "<div id='my-addon'></div>"
|
|
""",
|
|
),
|
|
Hook(
|
|
name="webview_will_show_context_menu",
|
|
args=["webview: aqt.webview.AnkiWebView", "menu: QMenu"],
|
|
legacy_hook="AnkiWebView.contextMenuEvent",
|
|
),
|
|
Hook(
|
|
name="webview_did_inject_style_into_page",
|
|
args=["webview: aqt.webview.AnkiWebView"],
|
|
doc='''Called after standard styling is injected into an external
|
|
html file, such as when loading the new graphs. You can use this hook to
|
|
mutate the DOM before the page is revealed.
|
|
|
|
For example:
|
|
|
|
def mytest(webview: AnkiWebView):
|
|
if webview.kind != AnkiWebViewKind.DECK_STATS:
|
|
return
|
|
web.eval(
|
|
"""
|
|
div = document.createElement("div");
|
|
div.innerHTML = 'hello';
|
|
document.body.appendChild(div);
|
|
"""
|
|
)
|
|
|
|
gui_hooks.webview_did_inject_style_into_page.append(mytest)
|
|
''',
|
|
),
|
|
# Main
|
|
###################
|
|
Hook(
|
|
name="main_window_did_init",
|
|
doc="""Executed after the main window is fully initialized
|
|
|
|
A sample use case for this hook would be to delay actions until Anki objects
|
|
like the profile or collection are fully initialized. In contrast to
|
|
`profile_did_open`, this hook will only fire once per Anki session and
|
|
is thus suitable for single-shot subscribers.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="main_window_should_require_reset",
|
|
args=[
|
|
"will_reset: bool",
|
|
"reason: aqt.main.ResetReason | str",
|
|
"context: object | None",
|
|
],
|
|
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="profile_did_open",
|
|
legacy_hook="profileLoaded",
|
|
doc="""Executed whenever a user profile has been opened
|
|
|
|
Please note that this hook will also be called on profile switches, so if you
|
|
are looking to simply delay an add-on action in a single-shot manner,
|
|
`main_window_did_init` is likely the more suitable choice.
|
|
""",
|
|
),
|
|
Hook(name="profile_will_close", legacy_hook="unloadProfile"),
|
|
Hook(
|
|
name="collection_will_temporarily_close",
|
|
args=["col: anki.collection.Collection"],
|
|
doc="""Called before one-way syncs and colpkg imports/exports.""",
|
|
),
|
|
Hook(
|
|
name="collection_did_temporarily_close",
|
|
args=["col: anki.collection.Collection"],
|
|
doc="""Called after one-way syncs and colpkg imports/exports.""",
|
|
),
|
|
Hook(
|
|
name="collection_did_load",
|
|
args=["col: anki.collection.Collection"],
|
|
legacy_hook="colLoading",
|
|
),
|
|
Hook(name="undo_state_did_change", args=["info: UndoActionsInfo"]),
|
|
Hook(name="review_did_undo", args=["card_id: int"], legacy_hook="revertedCard"),
|
|
Hook(
|
|
name="style_did_init",
|
|
args=["style: str"],
|
|
return_type="str",
|
|
legacy_hook="setupStyle",
|
|
),
|
|
Hook(
|
|
name="top_toolbar_did_init_links",
|
|
args=["links: list[str]", "top_toolbar: aqt.toolbar.Toolbar"],
|
|
doc="""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
|
|
by using aqt.toolbar.Toolbar.create_link. Links created in that way can then be
|
|
appended to the link list, e.g.:
|
|
|
|
def on_top_toolbar_did_init_links(links, toolbar):
|
|
my_link = toolbar.create_link(...)
|
|
links.append(my_link)
|
|
""",
|
|
),
|
|
Hook(
|
|
name="top_toolbar_will_set_left_tray_content",
|
|
args=["content: list[str]", "top_toolbar: aqt.toolbar.Toolbar"],
|
|
doc="""Used to add custom add-on components to the *left* area of Anki's main
|
|
window toolbar
|
|
|
|
'content' is a list of HTML strings added by add-ons which you can append your
|
|
own components or elements to. To equip your components with logic and styling
|
|
please see `webview_will_set_content` and `webview_did_receive_js_message`.
|
|
|
|
Please note that Anki's main screen is due to undergo a significant refactor
|
|
in the future and, as a result, add-ons subscribing to this hook will likely
|
|
require changes to continue working.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="top_toolbar_will_set_right_tray_content",
|
|
args=["content: list[str]", "top_toolbar: aqt.toolbar.Toolbar"],
|
|
doc="""Used to add custom add-on components to the *right* area of Anki's main
|
|
window toolbar
|
|
|
|
'content' is a list of HTML strings added by add-ons which you can append your
|
|
own components or elements to. To equip your components with logic and styling
|
|
please see `webview_will_set_content` and `webview_did_receive_js_message`.
|
|
|
|
Please note that Anki's main screen is due to undergo a significant refactor
|
|
in the future and, as a result, add-ons subscribing to this hook will likely
|
|
require changes to continue working.
|
|
""",
|
|
),
|
|
Hook(
|
|
name="top_toolbar_did_redraw",
|
|
args=["top_toolbar: aqt.toolbar.Toolbar"],
|
|
doc="""Executed when the top toolbar is redrawn""",
|
|
),
|
|
Hook(
|
|
name="media_sync_did_progress",
|
|
args=["entry: str"],
|
|
),
|
|
Hook(name="media_sync_did_start_or_stop", args=["running: bool"]),
|
|
Hook(
|
|
name="empty_cards_will_show",
|
|
args=["diag: aqt.emptycards.EmptyCardsDialog"],
|
|
doc="""Allows changing the list of cards to delete.""",
|
|
),
|
|
Hook(name="sync_will_start", args=[]),
|
|
Hook(
|
|
name="sync_did_finish",
|
|
args=[],
|
|
doc="""Executes after the sync of the collection concluded.
|
|
|
|
Note that the media sync did not necessarily finish at this point.""",
|
|
),
|
|
Hook(name="media_check_will_start", args=[]),
|
|
Hook(
|
|
name="media_check_did_finish",
|
|
args=["output: anki.media.CheckMediaResponse"],
|
|
doc="""Called after Media Check finishes.
|
|
|
|
`output` provides access to the unused/missing file lists and the text output that will be shown in the Check Media screen.""",
|
|
),
|
|
# Importing/exporting data
|
|
###################
|
|
Hook(
|
|
name="exporter_will_export",
|
|
args=[
|
|
"export_options: aqt.import_export.exporting.ExportOptions",
|
|
"exporter: aqt.import_export.exporting.Exporter",
|
|
],
|
|
return_type="aqt.import_export.exporting.ExportOptions",
|
|
doc="""Called before collection and deck exports.
|
|
|
|
Allows add-ons to be notified of impending deck exports and potentially
|
|
modify the export options. To perform the export unaltered, please return
|
|
`export_options` as is, e.g.:
|
|
|
|
def on_exporter_will_export(export_options: ExportOptions, exporter: Exporter):
|
|
if not isinstance(exporter, ApkgExporter):
|
|
return export_options
|
|
export_options.limit = ...
|
|
return export_options
|
|
""",
|
|
),
|
|
Hook(
|
|
name="exporter_did_export",
|
|
args=[
|
|
"export_options: aqt.import_export.exporting.ExportOptions",
|
|
"exporter: aqt.import_export.exporting.Exporter",
|
|
],
|
|
doc="""Called after collection and deck exports.""",
|
|
),
|
|
Hook(
|
|
name="legacy_exporter_will_export",
|
|
args=["legacy_exporter: anki.exporting.Exporter"],
|
|
doc="""Called before collection and deck exports performed by legacy exporters.""",
|
|
),
|
|
Hook(
|
|
name="legacy_exporter_did_export",
|
|
args=["legacy_exporter: anki.exporting.Exporter"],
|
|
doc="""Called after collection and deck exports performed by legacy exporters.""",
|
|
),
|
|
Hook(
|
|
name="exporters_list_did_initialize",
|
|
args=["exporters: list[Type[aqt.import_export.exporting.Exporter]]"],
|
|
doc="""Called after the list of exporter classes is created.
|
|
|
|
Allows you to register custom exporters and/or replace existing ones by
|
|
modifying the exporter list.
|
|
""",
|
|
),
|
|
# Dialog Manager
|
|
###################
|
|
Hook(
|
|
name="dialog_manager_did_open_dialog",
|
|
args=[
|
|
"dialog_manager: aqt.DialogManager",
|
|
"dialog_name: str",
|
|
"dialog_instance: QWidget",
|
|
],
|
|
doc="""Executed after aqt.dialogs creates a dialog window""",
|
|
),
|
|
# Adding cards
|
|
###################
|
|
Hook(
|
|
name="add_cards_will_show_history_menu",
|
|
args=["addcards: aqt.addcards.AddCards", "menu: QMenu"],
|
|
legacy_hook="AddCards.onHistory",
|
|
),
|
|
Hook(
|
|
name="add_cards_did_init",
|
|
args=["addcards: aqt.addcards.AddCards"],
|
|
),
|
|
Hook(
|
|
name="add_cards_did_add_note",
|
|
args=["note: anki.notes.Note"],
|
|
legacy_hook="AddCards.noteAdded",
|
|
),
|
|
Hook(
|
|
name="add_cards_will_add_note",
|
|
args=["problem: str | None", "note: anki.notes.Note"],
|
|
return_type="str | None",
|
|
doc="""Decides whether the note should be added to the collection or
|
|
not. It is assumed to come from the addCards window.
|
|
|
|
reason_to_already_reject is the first reason to reject that
|
|
was found, or None. If your filter wants to reject, it should
|
|
replace return the reason to reject. Otherwise return the
|
|
input.""",
|
|
),
|
|
Hook(
|
|
name="add_cards_might_add_note",
|
|
args=["optional_problems: list[str]", "note: anki.notes.Note"],
|
|
doc="""
|
|
Allows you to provide an optional reason to reject a note. A
|
|
yes / no dialog will open displaying the problem, to which the
|
|
user can decide if they would like to add the note anyway.
|
|
|
|
optional_problems is a list containing the optional reasons for which
|
|
you might reject a note. If your add-on wants to add a reason,
|
|
it should append the reason to the list.
|
|
|
|
An example add-on that asks the user for confirmation before adding a
|
|
card without tags:
|
|
|
|
def might_reject_empty_tag(optional_problems, note):
|
|
if not any(note.tags):
|
|
optional_problems.append("Add cards without tags?")
|
|
""",
|
|
),
|
|
Hook(
|
|
name="addcards_will_add_history_entry",
|
|
args=["line: str", "note: anki.notes.Note"],
|
|
return_type="str",
|
|
doc="""Allows changing the history line in the add-card window.""",
|
|
),
|
|
Hook(
|
|
name="add_cards_did_change_note_type",
|
|
args=["old: anki.models.NoteType", "new: anki.models.NoteType"],
|
|
doc="""Deprecated. Use addcards_did_change_note_type instead.
|
|
Executed after the user selects a new note type when adding
|
|
cards.""",
|
|
),
|
|
Hook(
|
|
name="addcards_did_change_note_type",
|
|
args=[
|
|
"addcards: aqt.addcards.AddCards",
|
|
"old: anki.models.NoteType",
|
|
"new: anki.models.NoteType",
|
|
],
|
|
replaces="add_cards_did_change_note_type",
|
|
replaced_hook_args=["old: anki.models.NoteType", "new: anki.models.NoteType"],
|
|
doc="""Executed after the user selects a new note type when adding
|
|
cards.""",
|
|
),
|
|
Hook(
|
|
name="add_cards_did_change_deck",
|
|
args=["new_deck_id: int"],
|
|
doc="""Executed after the user selects a new different deck when
|
|
adding cards.""",
|
|
),
|
|
# Editing
|
|
###################
|
|
Hook(
|
|
name="editor_did_init_left_buttons",
|
|
args=["buttons: list[str]", "editor: aqt.editor.Editor"],
|
|
),
|
|
Hook(
|
|
name="editor_did_init_buttons",
|
|
args=["buttons: list[str]", "editor: aqt.editor.Editor"],
|
|
),
|
|
Hook(
|
|
name="editor_did_init_shortcuts",
|
|
args=["shortcuts: list[tuple]", "editor: aqt.editor.Editor"],
|
|
legacy_hook="setupEditorShortcuts",
|
|
),
|
|
Hook(
|
|
name="editor_will_show_context_menu",
|
|
args=["editor_webview: aqt.editor.EditorWebView", "menu: QMenu"],
|
|
legacy_hook="EditorWebView.contextMenuEvent",
|
|
),
|
|
Hook(
|
|
name="editor_did_fire_typing_timer",
|
|
args=["note: anki.notes.Note"],
|
|
legacy_hook="editTimer",
|
|
),
|
|
Hook(
|
|
name="editor_did_focus_field",
|
|
args=["note: anki.notes.Note", "current_field_idx: int"],
|
|
legacy_hook="editFocusGained",
|
|
),
|
|
Hook(
|
|
name="editor_did_unfocus_field",
|
|
args=["changed: bool", "note: anki.notes.Note", "current_field_idx: int"],
|
|
return_type="bool",
|
|
legacy_hook="editFocusLost",
|
|
),
|
|
Hook(
|
|
name="editor_did_load_note",
|
|
args=["editor: aqt.editor.Editor"],
|
|
legacy_hook="loadNote",
|
|
),
|
|
Hook(
|
|
name="editor_did_update_tags",
|
|
args=["note: anki.notes.Note"],
|
|
legacy_hook="tagsUpdated",
|
|
),
|
|
Hook(
|
|
name="editor_will_munge_html",
|
|
args=["txt: str", "editor: aqt.editor.Editor"],
|
|
return_type="str",
|
|
doc="""Allows manipulating the text that will be saved by the editor""",
|
|
),
|
|
Hook(
|
|
name="editor_will_use_font_for_field",
|
|
args=["font: str"],
|
|
return_type="str",
|
|
legacy_hook="mungeEditingFontName",
|
|
),
|
|
Hook(
|
|
name="editor_web_view_did_init",
|
|
args=["editor_web_view: aqt.editor.EditorWebView"],
|
|
),
|
|
Hook(
|
|
name="editor_did_init",
|
|
args=["editor: aqt.editor.Editor"],
|
|
),
|
|
Hook(
|
|
name="editor_will_load_note",
|
|
args=["js: str", "note: anki.notes.Note", "editor: aqt.editor.Editor"],
|
|
return_type="str",
|
|
doc="""Allows changing the javascript commands to load note before
|
|
executing it and do change in the QT editor.""",
|
|
),
|
|
Hook(
|
|
name="editor_did_paste",
|
|
args=[
|
|
"editor: aqt.editor.Editor",
|
|
"html: str",
|
|
"internal: bool",
|
|
"extended: bool",
|
|
],
|
|
doc="""Called after some data is pasted by python into an editor field.""",
|
|
),
|
|
Hook(
|
|
name="editor_will_process_mime",
|
|
args=[
|
|
"mime: QMimeData",
|
|
"editor_web_view: aqt.editor.EditorWebView",
|
|
"internal: bool",
|
|
"extended: bool",
|
|
"drop_event: bool",
|
|
],
|
|
return_type="QMimeData",
|
|
doc="""
|
|
Used to modify MIME data stored in the clipboard after a drop or a paste.
|
|
Called after the user pastes or drag-and-drops something to Anki
|
|
before Anki processes the data.
|
|
|
|
The function should return a new or existing QMimeData object.
|
|
|
|
"mime" contains the corresponding QMimeData object.
|
|
"internal" indicates whether the drop or paste is performed between Anki fields.
|
|
Most likely you want to skip processing if "internal" was set to True.
|
|
"extended" indicates whether the user requested an extended paste.
|
|
"drop_event" indicates whether the event was triggered by a drag-and-drop
|
|
or by a right-click paste.
|
|
""",
|
|
),
|
|
# Tag
|
|
###################
|
|
Hook(name="tag_editor_did_process_key", args=["tag_edit: TagEdit", "evt: QEvent"]),
|
|
# Sound/video
|
|
###################
|
|
Hook(name="av_player_will_play", args=["tag: anki.sound.AVTag"]),
|
|
Hook(
|
|
name="av_player_did_begin_playing",
|
|
args=["player: aqt.sound.Player", "tag: anki.sound.AVTag"],
|
|
),
|
|
Hook(name="av_player_did_end_playing", args=["player: aqt.sound.Player"]),
|
|
Hook(
|
|
name="av_player_will_play_tags",
|
|
args=[
|
|
"tags: list[anki.sound.AVTag]",
|
|
"side: str",
|
|
"context: Any",
|
|
],
|
|
doc="""Called before playing a card side's sounds.
|
|
|
|
`tags` can be used to inspect and manipulate the sounds
|
|
that will be played (if any).
|
|
|
|
`side` can either be "question" or "answer".
|
|
|
|
`context` is the screen where the sounds will be played (e.g., Reviewer, Previewer, and CardLayout).
|
|
|
|
This won't be called when the user manually plays sounds
|
|
using `Replay Audio`.
|
|
|
|
Note that this hook is called even when the `Automatically play audio`
|
|
option is unchecked; This is so as to allow playing custom
|
|
sounds regardless of that option.""",
|
|
),
|
|
# Addon
|
|
###################
|
|
Hook(
|
|
name="addon_config_editor_will_display_json",
|
|
args=["text: str"],
|
|
return_type="str",
|
|
doc="""Allows changing the text of the json configuration before actually
|
|
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"
|
|
while reading the file and let the user uses real new line in
|
|
string instead of its encoding.""",
|
|
),
|
|
Hook(
|
|
name="addon_config_editor_will_save_json",
|
|
args=["text: str"],
|
|
return_type="str",
|
|
doc="""Deprecated. Use addon_config_editor_will_update_json instead.
|
|
Allows changing the text of the json configuration that was
|
|
received from the user before actually reading it. For
|
|
example, you can replace new line in strings by some "\\\\n".""",
|
|
),
|
|
Hook(
|
|
name="addon_config_editor_will_update_json",
|
|
args=["text: str", "addon: str"],
|
|
return_type="str",
|
|
replaces="addon_config_editor_will_save_json",
|
|
replaced_hook_args=["text: str"],
|
|
doc="""Allows changing the text of the json configuration that was
|
|
received from the user before actually reading it. For
|
|
example, you can replace new line in strings by some "\\\\n".""",
|
|
),
|
|
Hook(
|
|
name="addons_dialog_will_show",
|
|
args=["dialog: aqt.addons.AddonsDialog"],
|
|
doc="""Allows changing the add-on dialog before it is shown. E.g. add
|
|
buttons.""",
|
|
),
|
|
Hook(
|
|
name="addons_dialog_did_change_selected_addon",
|
|
args=["dialog: aqt.addons.AddonsDialog", "add_on: aqt.addons.AddonMeta"],
|
|
doc="""Allows doing an action when a single add-on is selected.""",
|
|
),
|
|
Hook(
|
|
name="addons_dialog_will_delete_addons",
|
|
args=["dialog: aqt.addons.AddonsDialog", "ids: list[str]"],
|
|
doc="""Allows doing an action before an add-on is deleted.""",
|
|
),
|
|
Hook(
|
|
name="addon_manager_will_install_addon",
|
|
args=["manager: aqt.addons.AddonManager", "module: str"],
|
|
doc="""Called before installing or updating an addon.
|
|
|
|
Can be used to release DB connections or open files that
|
|
would prevent an update from succeeding.""",
|
|
),
|
|
Hook(
|
|
name="addon_manager_did_install_addon",
|
|
args=["manager: aqt.addons.AddonManager", "module: str"],
|
|
doc="""Called after installing or updating an addon.
|
|
|
|
Can be used to restore DB connections or open files after
|
|
an add-on has been updated.""",
|
|
),
|
|
# Model
|
|
###################
|
|
Hook(
|
|
name="models_advanced_will_show",
|
|
args=["advanced: QDialog"],
|
|
),
|
|
Hook(
|
|
name="models_did_init_buttons",
|
|
args=[
|
|
"buttons: list[tuple[str, Callable[[], None]]]",
|
|
"models: aqt.models.Models",
|
|
],
|
|
return_type="list[tuple[str, Callable[[], None]]]",
|
|
doc="""Allows adding buttons to the Model dialog""",
|
|
),
|
|
# Fields
|
|
###################
|
|
Hook(
|
|
name="fields_did_add_field",
|
|
args=["dialog: aqt.fields.FieldDialog", "field: anki.models.FieldDict"],
|
|
),
|
|
Hook(
|
|
name="fields_did_rename_field",
|
|
args=[
|
|
"dialog: aqt.fields.FieldDialog",
|
|
"field: anki.models.FieldDict",
|
|
"old_name: str",
|
|
],
|
|
),
|
|
Hook(
|
|
name="fields_did_delete_field",
|
|
args=["dialog: aqt.fields.FieldDialog", "field: anki.models.FieldDict"],
|
|
),
|
|
# Stats
|
|
###################
|
|
Hook(
|
|
name="stats_dialog_will_show",
|
|
args=["dialog: aqt.stats.NewDeckStats"],
|
|
doc="""Allows changing the stats dialog before it is shown.""",
|
|
),
|
|
Hook(
|
|
name="stats_dialog_old_will_show",
|
|
args=["dialog: aqt.stats.DeckStats"],
|
|
doc="""Allows changing the old stats dialog before it is shown.""",
|
|
),
|
|
# Other
|
|
###################
|
|
Hook(
|
|
name="current_note_type_did_change",
|
|
args=["notetype: NotetypeDict"],
|
|
legacy_hook="currentModelChanged",
|
|
legacy_no_args=True,
|
|
),
|
|
Hook(name="sidebar_should_refresh_decks", doc="Legacy, do not use."),
|
|
Hook(name="sidebar_should_refresh_notetypes", doc="Legacy, do not use."),
|
|
Hook(
|
|
name="deck_browser_will_show_options_menu",
|
|
args=["menu: QMenu", "deck_id: int"],
|
|
legacy_hook="showDeckOptions",
|
|
),
|
|
Hook(
|
|
name="flag_label_did_change",
|
|
args=[],
|
|
doc="Used to update the GUI when a new flag label is assigned.",
|
|
),
|
|
]
|
|
|
|
suffix = ""
|
|
|
|
if __name__ == "__main__":
|
|
path = sys.argv[1]
|
|
write_file(path, hooks, prefix, suffix)
|