update to latest black

This commit is contained in:
Damien Elmes 2020-08-31 13:29:28 +10:00
parent 603210149c
commit a517accee3
24 changed files with 395 additions and 320 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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]] = []

View File

@ -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
) )

View File

@ -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())

View File

@ -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"""

View File

@ -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 = {}

View File

@ -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

View File

@ -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.",
), ),
] ]

View File

@ -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)

View File

@ -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):

View File

@ -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(

View File

@ -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,

View File

@ -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
) )

View File

@ -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]] = []

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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}"

View File

@ -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)

View File

@ -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(