From d02de28f218873e48eecf6ab3a3e0b9d031822f2 Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Sun, 16 Feb 2020 19:29:01 +0100 Subject: [PATCH 1/4] Add deck_browser_will_render_section hook Allows add-on authors to specifcally target and modify individual sections of the deck browser HTML body at string composition time. --- qt/aqt/deckbrowser.py | 20 ++++++++++-- qt/aqt/gui_hooks.py | 69 ++++++++++++++++++++++++++++++++++++++++ qt/tools/genhooks_gui.py | 30 +++++++++++++++++ 3 files changed, 116 insertions(+), 3 deletions(-) diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index a57647d62..356d616fc 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -5,6 +5,7 @@ from __future__ import annotations from copy import deepcopy +from enum import Enum from typing import Any import aqt @@ -23,6 +24,12 @@ class DeckBrowserBottomBar: self.deck_browser = deck_browser +class DeckBrowserSection(Enum): + TREE = 0 + STATS = 1 + WARN = 2 + + class DeckBrowser: _dueTree: Any @@ -103,10 +110,17 @@ class DeckBrowser: gui_hooks.deck_browser_did_render(self) def __renderPage(self, offset): - tree = self._renderDeckTree(self._dueTree) - stats = self._renderStats() + tree = gui_hooks.deck_browser_will_render_section( + self._renderDeckTree(self._dueTree), DeckBrowserSection.TREE, self + ) + stats = gui_hooks.deck_browser_will_render_section( + self._renderStats(), DeckBrowserSection.STATS, self + ) + warn = gui_hooks.deck_browser_will_render_section( + self._countWarn(), DeckBrowserSection.WARN, self + ) self.web.stdHtml( - self._body % dict(tree=tree, stats=stats, countwarn=self._countWarn()), + self._body % dict(tree=tree, stats=stats, countwarn=warn), css=["deckbrowser.css"], js=["jquery.js", "jquery-ui.js", "deckbrowser.js"], context=self, diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index 026f0bc2f..a4922de36 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -435,6 +435,75 @@ class _DeckBrowserDidRenderHook: deck_browser_did_render = _DeckBrowserDidRenderHook() +class _DeckBrowserWillRenderSectionFilter: + """Used to modify HTML content sections in the deck browser body + + 'html' is the content a particular section will be populated with + + 'section' is an enum describing the current section. For an overview + of all the possible values please see aqt.deckbrowser.DeckBrowserSection. + + If you do not want to modify the content of a particular section, + return 'html' unmodified, e.g.: + + def on_deck_browser_will_render_section(html, section, deck_browser): + + if section != DeckBrowserSection.TREE: + # not the tree section we want to modify, return unchanged + return html + + # tree section, perform changes to html + html += "
my code
" + + return html + """ + + _hooks: List[ + Callable[ + [str, "aqt.deckbrowser.DeckBrowserSection", "aqt.deckbrowser.DeckBrowser"], + str, + ] + ] = [] + + def append( + self, + cb: Callable[ + [str, "aqt.deckbrowser.DeckBrowserSection", "aqt.deckbrowser.DeckBrowser"], + str, + ], + ) -> None: + """(html: str, section: aqt.deckbrowser.DeckBrowserSection, deck_browser: aqt.deckbrowser.DeckBrowser)""" + self._hooks.append(cb) + + def remove( + self, + cb: Callable[ + [str, "aqt.deckbrowser.DeckBrowserSection", "aqt.deckbrowser.DeckBrowser"], + str, + ], + ) -> None: + if cb in self._hooks: + self._hooks.remove(cb) + + def __call__( + self, + html: str, + section: aqt.deckbrowser.DeckBrowserSection, + deck_browser: aqt.deckbrowser.DeckBrowser, + ) -> str: + for filter in self._hooks: + try: + html = filter(html, section, deck_browser) + except: + # if the hook fails, remove it + self._hooks.remove(filter) + raise + return html + + +deck_browser_will_render_section = _DeckBrowserWillRenderSectionFilter() + + class _DeckBrowserWillShowOptionsMenuHook: _hooks: List[Callable[[QMenu, int], None]] = [] diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index bd8226132..b3efb16f3 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -30,6 +30,36 @@ hooks = [ 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_section", + args=[ + "html: str", + "section: aqt.deckbrowser.DeckBrowserSection", + "deck_browser: aqt.deckbrowser.DeckBrowser", + ], + return_type="str", + doc="""Used to modify HTML content sections in the deck browser body + + 'html' is the content a particular section will be populated with + + 'section' is an enum describing the current section. For an overview + of all the possible values please see aqt.deckbrowser.DeckBrowserSection. + + If you do not want to modify the content of a particular section, + return 'html' unmodified, e.g.: + + def on_deck_browser_will_render_section(html, section, deck_browser): + + if section != DeckBrowserSection.TREE: + # not the tree section we want to modify, return unchanged + return html + + # tree section, perform changes to html + html += "
my code
" + + return html + """, + ), Hook( name="reviewer_did_show_question", args=["card: Card"], From 8ff1a2e770114818494b13e222acd47a24ed945a Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Mon, 17 Feb 2020 16:26:21 +0100 Subject: [PATCH 2/4] Bundle individual section hooks together into one Uses new dataclass 'DeckBrowserContent' --- qt/aqt/deckbrowser.py | 35 ++++++++++++++++----------- qt/aqt/gui_hooks.py | 52 ++++++++++++++++------------------------ qt/tools/genhooks_gui.py | 30 ++++++++--------------- 3 files changed, 51 insertions(+), 66 deletions(-) diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index 356d616fc..c19b0ef01 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -5,7 +5,7 @@ from __future__ import annotations from copy import deepcopy -from enum import Enum +from dataclasses import dataclass from typing import Any import aqt @@ -24,10 +24,20 @@ class DeckBrowserBottomBar: self.deck_browser = deck_browser -class DeckBrowserSection(Enum): - TREE = 0 - STATS = 1 - WARN = 2 +@dataclass +class DeckBrowserContent: + """Stores sections of HTML content that the deck browser will be + populated with. + + Attributes: + tree {str} -- HTML of the deck tree section + stats {str} -- HTML of the stats section + countwarn {str} -- HTML of the deck count warning section + """ + + tree: str + stats: str + countwarn: str class DeckBrowser: @@ -110,17 +120,14 @@ class DeckBrowser: gui_hooks.deck_browser_did_render(self) def __renderPage(self, offset): - tree = gui_hooks.deck_browser_will_render_section( - self._renderDeckTree(self._dueTree), DeckBrowserSection.TREE, self - ) - stats = gui_hooks.deck_browser_will_render_section( - self._renderStats(), DeckBrowserSection.STATS, self - ) - warn = gui_hooks.deck_browser_will_render_section( - self._countWarn(), DeckBrowserSection.WARN, self + content = DeckBrowserContent( + tree=self._renderDeckTree(self._dueTree), + stats=self._renderStats(), + countwarn=self._countWarn(), ) + gui_hooks.deck_browser_will_render_content(self, content) self.web.stdHtml( - self._body % dict(tree=tree, stats=stats, countwarn=warn), + self._body % content.__dict__, css=["deckbrowser.css"], js=["jquery.js", "jquery-ui.js", "deckbrowser.js"], context=self, diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index a4922de36..a6813d54c 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -435,51 +435,41 @@ class _DeckBrowserDidRenderHook: deck_browser_did_render = _DeckBrowserDidRenderHook() -class _DeckBrowserWillRenderSectionFilter: +class _DeckBrowserWillRenderContentHook: """Used to modify HTML content sections in the deck browser body - 'html' is the content a particular section will be populated with + 'content' contains the sections of HTML content the deck browser body + will be updated with. - 'section' is an enum describing the current section. For an overview - of all the possible values please see aqt.deckbrowser.DeckBrowserSection. - - If you do not want to modify the content of a particular section, - return 'html' unmodified, e.g.: + 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_section(html, section, deck_browser): - - if section != DeckBrowserSection.TREE: - # not the tree section we want to modify, return unchanged - return html - - # tree section, perform changes to html - html += "
my code
" - - return html + def on_deck_browser_will_render_content(deck_browser, content): + content.stats += " +
my html
" """ _hooks: List[ Callable[ - [str, "aqt.deckbrowser.DeckBrowserSection", "aqt.deckbrowser.DeckBrowser"], - str, + ["aqt.deckbrowser.DeckBrowser", "aqt.deckbrowser.DeckBrowserContent"], None ] ] = [] def append( self, cb: Callable[ - [str, "aqt.deckbrowser.DeckBrowserSection", "aqt.deckbrowser.DeckBrowser"], - str, + ["aqt.deckbrowser.DeckBrowser", "aqt.deckbrowser.DeckBrowserContent"], None ], ) -> None: - """(html: str, section: aqt.deckbrowser.DeckBrowserSection, deck_browser: aqt.deckbrowser.DeckBrowser)""" + """(deck_browser: aqt.deckbrowser.DeckBrowser, content: aqt.deckbrowser.DeckBrowserContent)""" self._hooks.append(cb) def remove( self, cb: Callable[ - [str, "aqt.deckbrowser.DeckBrowserSection", "aqt.deckbrowser.DeckBrowser"], - str, + ["aqt.deckbrowser.DeckBrowser", "aqt.deckbrowser.DeckBrowserContent"], None ], ) -> None: if cb in self._hooks: @@ -487,21 +477,19 @@ class _DeckBrowserWillRenderSectionFilter: def __call__( self, - html: str, - section: aqt.deckbrowser.DeckBrowserSection, deck_browser: aqt.deckbrowser.DeckBrowser, - ) -> str: - for filter in self._hooks: + content: aqt.deckbrowser.DeckBrowserContent, + ) -> None: + for hook in self._hooks: try: - html = filter(html, section, deck_browser) + hook(deck_browser, content) except: # if the hook fails, remove it - self._hooks.remove(filter) + self._hooks.remove(hook) raise - return html -deck_browser_will_render_section = _DeckBrowserWillRenderSectionFilter() +deck_browser_will_render_content = _DeckBrowserWillRenderContentHook() class _DeckBrowserWillShowOptionsMenuHook: diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index b3efb16f3..3d9b198bc 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -31,33 +31,23 @@ hooks = [ doc="""Allow to update the deck browser window. E.g. change its title.""", ), Hook( - name="deck_browser_will_render_section", + name="deck_browser_will_render_content", args=[ - "html: str", - "section: aqt.deckbrowser.DeckBrowserSection", "deck_browser: aqt.deckbrowser.DeckBrowser", + "content: aqt.deckbrowser.DeckBrowserContent", ], - return_type="str", doc="""Used to modify HTML content sections in the deck browser body - 'html' is the content a particular section will be populated with + 'content' contains the sections of HTML content the deck browser body + will be updated with. - 'section' is an enum describing the current section. For an overview - of all the possible values please see aqt.deckbrowser.DeckBrowserSection. - - If you do not want to modify the content of a particular section, - return 'html' unmodified, e.g.: + 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_section(html, section, deck_browser): - - if section != DeckBrowserSection.TREE: - # not the tree section we want to modify, return unchanged - return html - - # tree section, perform changes to html - html += "
my code
" - - return html + def on_deck_browser_will_render_content(deck_browser, content): + content.stats += "\n
my html
" """, ), Hook( From f7ae2fa1f78ecd35be44542718cf7f7529c1efa3 Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Mon, 17 Feb 2020 16:49:21 +0100 Subject: [PATCH 3/4] Add overview_will_render_content hook --- qt/aqt/gui_hooks.py | 49 ++++++++++++++++++++++++++++++++++++++++ qt/aqt/overview.py | 25 ++++++++++++++------ qt/tools/genhooks_gui.py | 20 ++++++++++++++++ 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index a6813d54c..3a85b50a5 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -781,6 +781,55 @@ class _OverviewDidRefreshHook: overview_did_refresh = _OverviewDidRefreshHook() +class _OverviewWillRenderContentHook: + """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 += " +
my html
" + """ + + _hooks: List[ + Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None] + ] = [] + + def append( + self, + cb: Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None], + ) -> None: + """(overview: aqt.overview.Overview, content: aqt.overview.OverviewContent)""" + self._hooks.append(cb) + + def remove( + self, + cb: Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None], + ) -> None: + if cb in self._hooks: + self._hooks.remove(cb) + + def __call__( + self, overview: aqt.overview.Overview, content: aqt.overview.OverviewContent + ) -> None: + for hook in self._hooks: + try: + hook(overview, content) + except: + # if the hook fails, remove it + self._hooks.remove(hook) + raise + + +overview_will_render_content = _OverviewWillRenderContentHook() + + class _ProfileDidOpenHook: _hooks: List[Callable[[], None]] = [] diff --git a/qt/aqt/overview.py b/qt/aqt/overview.py index e94b69953..0b06d27b8 100644 --- a/qt/aqt/overview.py +++ b/qt/aqt/overview.py @@ -4,6 +4,8 @@ from __future__ import annotations +from dataclasses import dataclass + import aqt from anki.lang import _ from aqt import gui_hooks @@ -17,6 +19,14 @@ class OverviewBottomBar: self.overview = overview +@dataclass +class OverviewContent: + deck: str + shareLink: str + desc: str + table: str + + class Overview: "Deck overview." @@ -141,14 +151,15 @@ class Overview: shareLink = 'Reviews and Updates' else: shareLink = "" + content = OverviewContent( + deck=deck["name"], + shareLink=shareLink, + desc=self._desc(deck), + table=self._table(), + ) + gui_hooks.overview_will_render_content(self, content) self.web.stdHtml( - self._body - % dict( - deck=deck["name"], - shareLink=shareLink, - desc=self._desc(deck), - table=self._table(), - ), + self._body % content.__dict__, css=["overview.css"], js=["jquery.js", "overview.js"], context=self, diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index 3d9b198bc..e377a727b 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -25,6 +25,26 @@ hooks = [ 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
my html
" + """, + ), Hook( name="deck_browser_did_render", args=["deck_browser: aqt.deckbrowser.DeckBrowser"], From 775765ff4f30ba7f16b8b0711be31da7eca5d4a8 Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Mon, 17 Feb 2020 16:53:47 +0100 Subject: [PATCH 4/4] Complete OverviewContent docs --- qt/aqt/overview.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qt/aqt/overview.py b/qt/aqt/overview.py index 0b06d27b8..4184660e1 100644 --- a/qt/aqt/overview.py +++ b/qt/aqt/overview.py @@ -21,6 +21,16 @@ class OverviewBottomBar: @dataclass class OverviewContent: + """Stores sections of HTML content that the overview will be + populated with. + + Attributes: + deck {str} -- Plain text deck name + shareLink {str} -- HTML of the share link section + desc {str} -- HTML of the deck description section + table {str} -- HTML of the deck stats table section + """ + deck: str shareLink: str desc: str