diff --git a/qt/aqt/browser/find_duplicates.py b/qt/aqt/browser/find_duplicates.py index 5ffb97ba5..d8454fec5 100644 --- a/qt/aqt/browser/find_duplicates.py +++ b/qt/aqt/browser/find_duplicates.py @@ -14,7 +14,6 @@ from anki.collection import SearchNode from anki.notes import NoteId from aqt.qt import * from aqt.qt import sip -from aqt.webview import AnkiWebViewKind from ..operations import QueryOp from ..operations.tag import add_tags_to_notes @@ -52,7 +51,6 @@ class FindDuplicatesDialog(QDialog): self._dupes: list[tuple[str, list[NoteId]]] = [] # links - form.webView.set_kind(AnkiWebViewKind.FIND_DUPLICATES) form.webView.set_bridge_command(self._on_duplicate_clicked, context=self) form.webView.stdHtml("", context=self) diff --git a/qt/aqt/emptycards.py b/qt/aqt/emptycards.py index 78436239e..cf8bbd4ba 100644 --- a/qt/aqt/emptycards.py +++ b/qt/aqt/emptycards.py @@ -15,7 +15,6 @@ from anki.collection import EmptyCardsReport from aqt import gui_hooks from aqt.qt import QDialog, QDialogButtonBox, qconnect from aqt.utils import disable_help_button, restoreGeom, saveGeom, tooltip, tr -from aqt.webview import AnkiWebViewKind def show_empty_cards(mw: aqt.main.AnkiQt) -> None: @@ -47,7 +46,6 @@ class EmptyCardsDialog(QDialog): self.setWindowTitle(tr.empty_cards_window_title()) disable_help_button(self) self.form.keep_notes.setText(tr.empty_cards_preserve_notes_checkbox()) - self.form.webview.set_kind(AnkiWebViewKind.EMPTY_CARDS) self.form.webview.set_bridge_command(self._on_note_link_clicked, self) gui_hooks.empty_cards_will_show(self) diff --git a/qt/aqt/forms/emptycards.ui b/qt/aqt/forms/emptycards.ui index 70049a47f..ad47dead3 100644 --- a/qt/aqt/forms/emptycards.ui +++ b/qt/aqt/forms/emptycards.ui @@ -30,7 +30,7 @@ 0 - + about:blank @@ -81,7 +81,7 @@ - AnkiWebView + EmptyCardsWebView QWidget
aqt/webview
1 diff --git a/qt/aqt/forms/finddupes.ui b/qt/aqt/forms/finddupes.ui index d47f8b635..9a7c44c06 100644 --- a/qt/aqt/forms/finddupes.ui +++ b/qt/aqt/forms/finddupes.ui @@ -73,7 +73,7 @@ 0 - + about:blank @@ -98,7 +98,7 @@ - AnkiWebView + FindDupesWebView QWidget
aqt/webview
1 diff --git a/qt/aqt/forms/stats.ui b/qt/aqt/forms/stats.ui index 0d0edaa53..838e1da5f 100644 --- a/qt/aqt/forms/stats.ui +++ b/qt/aqt/forms/stats.ui @@ -30,7 +30,7 @@ 0 - + about:blank @@ -146,7 +146,7 @@ - AnkiWebView + StatsWebView QWidget
aqt/webview
1 diff --git a/qt/aqt/stats.py b/qt/aqt/stats.py index 3c9952155..56fb7ccfd 100644 --- a/qt/aqt/stats.py +++ b/qt/aqt/stats.py @@ -25,7 +25,7 @@ from aqt.utils import ( tooltip, tr, ) -from aqt.webview import AnkiWebViewKind +from aqt.webview import LegacyStatsWebView class NewDeckStats(QDialog): @@ -71,7 +71,6 @@ class NewDeckStats(QDialog): maybeHideClose(self.form.buttonBox) addCloseShortcut(self) gui_hooks.stats_dialog_will_show(self) - self.form.web.set_kind(AnkiWebViewKind.DECK_STATS) self.form.web.hide_while_preserving_layout() self.show() self.refresh() @@ -154,6 +153,9 @@ class DeckStats(QDialog): self.name = "deckStats" self.period = 0 self.form = aqt.forms.stats.Ui_Dialog() + # Hack: Switch out web views dynamically to avoid maintaining multiple + # Qt forms for different versions of the stats dialog. + self.form.web = LegacyStatsWebView(self.mw) self.oldPos = None self.wholeCollection = False self.setMinimumWidth(700) @@ -232,7 +234,6 @@ class DeckStats(QDialog): stats = self.mw.col.stats() stats.wholeCollection = self.wholeCollection self.report = stats.report(type=self.period) - self.form.web.set_kind(AnkiWebViewKind.LEGACY_DECK_STATS) self.form.web.stdHtml( f"{self.report}", js=["js/vendor/jquery.min.js", "js/vendor/plot.js"], diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index 70bc5a25c..c6dc9db2d 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -10,7 +10,9 @@ import re import sys from collections.abc import Callable, Sequence from enum import Enum -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, Type, cast + +from typing_extensions import TypedDict, Unpack import anki import anki.lang @@ -360,7 +362,9 @@ class AnkiWebView(QWebEngineView): kind: AnkiWebViewKind = AnkiWebViewKind.DEFAULT, ) -> None: QWebEngineView.__init__(self, parent=parent) - self.set_kind(kind) + self._kind = kind + self.set_title(kind.value) + self.setPage(AnkiWebPage(self._onBridgeCmd, kind, self)) # reduce flicker self.page().setBackgroundColor(theme_manager.qcolor(colors.CANVAS)) @@ -390,17 +394,6 @@ class AnkiWebView(QWebEngineView): """ ) - def set_kind(self, kind: AnkiWebViewKind) -> None: - self._kind = kind - self.set_title(kind.value) - # this is an ugly hack to avoid breakages caused by - # creating a default webview then immediately calling set_kind, which results - # in the creation of two pages, and the second fails as the domDone - # signal from the first one is received - if kind != AnkiWebViewKind.DEFAULT: - self.setPage(AnkiWebPage(self._onBridgeCmd, kind, self)) - self.page().setBackgroundColor(theme_manager.qcolor(colors.CANVAS)) - def page(self) -> AnkiWebPage: return cast(AnkiWebPage, super().page()) @@ -965,3 +958,53 @@ html {{ {font} }} @deprecated(info="use theme_manager.qcolor() instead") def get_window_bg_color(self, night_mode: bool | None = None) -> QColor: return theme_manager.qcolor(colors.CANVAS) + + +# Pre-configured classes for use in Qt Designer +########################################################################## + + +class _AnkiWebViewKwargs(TypedDict, total=False): + parent: QWidget | None + title: str + kind: AnkiWebViewKind + + +def _create_ankiwebview_subclass( + name: str, + /, + **fixed_kwargs: Unpack[_AnkiWebViewKwargs], +) -> Type[AnkiWebView]: + + def __init__(self, *args: Any, **kwargs: _AnkiWebViewKwargs) -> None: + # user‑supplied kwargs override fixed kwargs + merged = cast(_AnkiWebViewKwargs, {**fixed_kwargs, **kwargs}) + AnkiWebView.__init__(self, *args, **merged) + + __init__.__qualname__ = f"{name}.__init__" + if fixed_kwargs: + __init__.__doc__ = ( + f"Auto‑generated wrapper that pre‑sets " + f"{', '.join(f'{k}={v!r}' for k, v in fixed_kwargs.items())}." + ) + + cls: Type[AnkiWebView] = type(name, (AnkiWebView,), {"__init__": __init__}) + + return cls + + +# These subclasses are used in Qt Designer UI files to allow for configuring +# web views at initialization time (custom widgets can otherwise only be +# initialized with the default constructor) +StatsWebView = _create_ankiwebview_subclass( + "StatsWebView", kind=AnkiWebViewKind.DECK_STATS +) +LegacyStatsWebView = _create_ankiwebview_subclass( + "LegacyStatsWebView", kind=AnkiWebViewKind.LEGACY_DECK_STATS +) +EmptyCardsWebView = _create_ankiwebview_subclass( + "EmptyCardsWebView", kind=AnkiWebViewKind.EMPTY_CARDS +) +FindDupesWebView = _create_ankiwebview_subclass( + "FindDupesWebView", kind=AnkiWebViewKind.FIND_DUPLICATES +)