From 2023cea60ca70f98855f5579d58f72e60df4df5d Mon Sep 17 00:00:00 2001 From: gugutu <59798823+gugutu@users.noreply.github.com> Date: Fri, 2 Feb 2024 07:10:47 +0800 Subject: [PATCH 1/8] Fix second refresh after loading note --- src/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/__init__.py b/src/__init__.py index 5fd4ec5..1742c7e 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -90,6 +90,10 @@ class EditorPreview(object): layout.insertWidget(web_index, split) def editor_note_hook(self, editor): + # The initial loading of notes will also trigger an editing event + # which will cause a second refresh + # It is disabled here and enabled after the first editing + editor.need_reload_on_edit = False self.onedit_hook(editor, editor.note) def editor_init_button_hook(self, buttons, editor): @@ -121,7 +125,10 @@ class EditorPreview(object): def onedit_hook(self, editor, origin): if editor.note == origin: - editor.editor_preview.eval(self._obtainCardText(editor.note)) + if editor.need_reload_on_edit: + editor.editor_preview.eval(self._obtainCardText(editor.note)) + else: + editor.need_reload_on_edit = True eprev = EditorPreview() From deca35908a7afe09c027a4b88c48865d14865ed6 Mon Sep 17 00:00:00 2001 From: gugutu <59798823+gugutu@users.noreply.github.com> Date: Fri, 2 Feb 2024 07:28:16 +0800 Subject: [PATCH 2/8] Improve compatibility with other plugins Wrap a widget on the outer layer of the webview so that other plugins can continue to modify the layout. --- src/__init__.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index 1742c7e..c314bf8 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -51,28 +51,27 @@ class EditorPreview(object): ) def _get_splitter(self, editor): - layout = editor.outerLayout mainR, editorR = [int(r) for r in config["splitRatio"].split(":")] location = config["location"] split = QSplitter() if location == "above": split.setOrientation(Qt.Orientation.Vertical) split.addWidget(editor.editor_preview) - split.addWidget(editor.web) + split.addWidget(editor.wrapped_web) sizes = [editorR, mainR] elif location == "below": split.setOrientation(Qt.Orientation.Vertical) - split.addWidget(editor.web) + split.addWidget(editor.wrapped_web) split.addWidget(editor.editor_preview) sizes = [mainR, editorR] elif location == "left": split.setOrientation(Qt.Orientation.Horizontal) split.addWidget(editor.editor_preview) - split.addWidget(editor.web) + split.addWidget(editor.wrapped_web) sizes = [editorR, mainR] elif location == "right": split.setOrientation(Qt.Orientation.Horizontal) - split.addWidget(editor.web) + split.addWidget(editor.wrapped_web) split.addWidget(editor.editor_preview) sizes = [mainR, editorR] else: @@ -82,10 +81,20 @@ class EditorPreview(object): return split def _inject_splitter(self, editor: editor.Editor): - layout = editor.outerLayout + layout = editor.web.parentWidget().layout() + if layout is None: + layout = QVBoxLayout() + editor.web.parentWidget().setLayout(layout) web_index = layout.indexOf(editor.web) layout.removeWidget(editor.web) + # Wrap a widget on the outer layer of the webview + # So that other plugins can continue to modify the layout + editor.wrapped_web = QWidget() + wrapLayout = QHBoxLayout() + editor.wrapped_web.setLayout(wrapLayout) + wrapLayout.addWidget(editor.web) + split = self._get_splitter(editor) layout.insertWidget(web_index, split) From 74d12576cd59c4007a04fc96f09c130e3c850be0 Mon Sep 17 00:00:00 2001 From: gugutu <59798823+gugutu@users.noreply.github.com> Date: Fri, 2 Feb 2024 08:50:17 +0800 Subject: [PATCH 3/8] Fix memory leakage caused by hook not releasing Due to Anki not providing a hook when closing the editor, refactoring to use set filter to check which editors are not active and avoid creating a large number of hooks. By traversing the set, it is determined whether the current editor needs to be refreshed. --- src/__init__.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index c314bf8..0dcc159 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,4 +1,5 @@ import json +from typing import Set from anki import hooks, buildinfo from aqt import editor, gui_hooks, mw @@ -10,6 +11,7 @@ config = mw.addonManager.getConfig(__name__) class EditorPreview(object): + editors: Set[editor.Editor] = set() js = [ "js/mathjax.js", "js/vendor/mathjax/tex-chtml.js", @@ -19,6 +21,8 @@ class EditorPreview(object): def __init__(self): gui_hooks.editor_did_init.append(self.editor_init_hook) gui_hooks.editor_did_init_buttons.append(self.editor_init_button_hook) + gui_hooks.editor_did_load_note.append(self.editor_note_hook) + gui_hooks.editor_did_fire_typing_timer.append(self.onedit_hook) buildversion = buildinfo.version.split(".") # Anki changed their versioning scheme in 2023 to year.month(.patch), causing things to explode here. @@ -45,12 +49,9 @@ class EditorPreview(object): ed.editor_preview.hide() self._inject_splitter(ed) - gui_hooks.editor_did_fire_typing_timer.append(lambda o: self.onedit_hook(ed, o)) - gui_hooks.editor_did_load_note.append( - lambda o: None if o != ed else self.editor_note_hook(o) - ) def _get_splitter(self, editor): + layout = editor.outerLayout mainR, editorR = [int(r) for r in config["splitRatio"].split(":")] location = config["location"] split = QSplitter() @@ -99,11 +100,13 @@ class EditorPreview(object): layout.insertWidget(web_index, split) def editor_note_hook(self, editor): + self.editors = set(filter(lambda it: it.note is not None, self.editors)) + self.editors.add(editor) # The initial loading of notes will also trigger an editing event # which will cause a second refresh # It is disabled here and enabled after the first editing editor.need_reload_on_edit = False - self.onedit_hook(editor, editor.note) + self.refresh(editor) def editor_init_button_hook(self, buttons, editor): addon_path = os.path.dirname(__file__) @@ -132,12 +135,15 @@ class EditorPreview(object): return f"_showAnswer({json.dumps(a)},'{bodyclass}');" - def onedit_hook(self, editor, origin): - if editor.note == origin: - if editor.need_reload_on_edit: - editor.editor_preview.eval(self._obtainCardText(editor.note)) - else: - editor.need_reload_on_edit = True + def onedit_hook(self, origin): + for editor in self.editors: + if editor.note == origin: + if editor.need_reload_on_edit: + self.refresh(editor) + else: + editor.need_reload_on_edit = True + def refresh(self, editor): + editor.editor_preview.eval(self._obtainCardText(editor.note)) eprev = EditorPreview() From 8487bb86d0944238368a514462877ef75c0c118f Mon Sep 17 00:00:00 2001 From: gugutu <59798823+gugutu@users.noreply.github.com> Date: Fri, 2 Feb 2024 09:12:49 +0800 Subject: [PATCH 4/8] Increase the setSizes parameter If the parameter given to setSizes is very small and smaller than QSplitter, the sub windows within QSplitter will be stretched. It is advisable to set a larger parameter for setSizes as much as possible. --- src/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index 0dcc159..7a1136d 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -51,8 +51,7 @@ class EditorPreview(object): self._inject_splitter(ed) def _get_splitter(self, editor): - layout = editor.outerLayout - mainR, editorR = [int(r) for r in config["splitRatio"].split(":")] + mainR, editorR = [int(r) * 10000 for r in config["splitRatio"].split(":")] location = config["location"] split = QSplitter() if location == "above": From 65cc6c0b12e0ca6b396eecdc22a3701c923a3494 Mon Sep 17 00:00:00 2001 From: gugutu <59798823+gugutu@users.noreply.github.com> Date: Sun, 25 Feb 2024 05:11:17 +0800 Subject: [PATCH 5/8] Modify the implementation method of "avoiding secondary refresh" The previous implementation may encounter issues in specific scenarios and could potentially become invalid after Anki updates. Now, the approach has been modified to check whether the note content has been modified, which is a simpler and more understandable method. --- src/__init__.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index 7a1136d..d8878e6 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -103,8 +103,8 @@ class EditorPreview(object): self.editors.add(editor) # The initial loading of notes will also trigger an editing event # which will cause a second refresh - # It is disabled here and enabled after the first editing - editor.need_reload_on_edit = False + # Caching the content of notes here will be used to determine if the content has changed + editor.cached_fields = list(editor.note.fields) self.refresh(editor) def editor_init_button_hook(self, buttons, editor): @@ -134,13 +134,11 @@ class EditorPreview(object): return f"_showAnswer({json.dumps(a)},'{bodyclass}');" - def onedit_hook(self, origin): + def onedit_hook(self, note): for editor in self.editors: - if editor.note == origin: - if editor.need_reload_on_edit: - self.refresh(editor) - else: - editor.need_reload_on_edit = True + if editor.note == note and editor.cached_fields != note.fields: + editor.cached_fields = list(note.fields) + self.refresh(editor) def refresh(self, editor): editor.editor_preview.eval(self._obtainCardText(editor.note)) From d6034c6dcf28ab3c9f2e12fd6ecc53815ddd66e8 Mon Sep 17 00:00:00 2001 From: gugutu <59798823+gugutu@users.noreply.github.com> Date: Fri, 1 Mar 2024 16:19:38 +0800 Subject: [PATCH 6/8] Fix for Anki version 24.04+ --- src/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/__init__.py b/src/__init__.py index d8878e6..c743fb4 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -26,7 +26,13 @@ class EditorPreview(object): buildversion = buildinfo.version.split(".") # Anki changed their versioning scheme in 2023 to year.month(.patch), causing things to explode here. - if not int(buildversion[0]) >= 23 and int(buildversion[2]) < 45: # < 2.1.45 + if int(buildversion[0]) >= 24: + self.js = [ + "js/mathjax.js", + "js/vendor/mathjax/tex-chtml-full.js", + "js/reviewer.js", + ] + if not int(buildversion[0]) == 23 and int(buildversion[2]) < 45: # < 2.1.45 self.js = [ "js/vendor/jquery.min.js", "js/vendor/css_browser_selector.min.js", From 6acf2d5bb5f1b7b4a4ba41243469fe87b0acf2b4 Mon Sep 17 00:00:00 2001 From: gugutu <59798823+gugutu@users.noreply.github.com> Date: Fri, 1 Mar 2024 16:30:31 +0800 Subject: [PATCH 7/8] Fix for Anki version 24.04+ --- src/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__init__.py b/src/__init__.py index c743fb4..a3ed3e5 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -32,7 +32,7 @@ class EditorPreview(object): "js/vendor/mathjax/tex-chtml-full.js", "js/reviewer.js", ] - if not int(buildversion[0]) == 23 and int(buildversion[2]) < 45: # < 2.1.45 + elif int(buildversion[0]) < 23 and int(buildversion[2]) < 45: # < 2.1.45 self.js = [ "js/vendor/jquery.min.js", "js/vendor/css_browser_selector.min.js", From def0045819bac681f10895a76f3f9c8462a15613 Mon Sep 17 00:00:00 2001 From: gugutu <59798823+gugutu@users.noreply.github.com> Date: Tue, 5 Mar 2024 03:47:12 +0800 Subject: [PATCH 8/8] Fix the issue of incorrect JS file names introduced under Anki 23.12.1 --- src/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__init__.py b/src/__init__.py index a3ed3e5..bebae28 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -26,7 +26,7 @@ class EditorPreview(object): buildversion = buildinfo.version.split(".") # Anki changed their versioning scheme in 2023 to year.month(.patch), causing things to explode here. - if int(buildversion[0]) >= 24: + if int(buildversion[0]) >= 24 or (int(buildversion[0]) == 23 and int(buildversion[1]) == 12 and 2 < len(buildversion) and int(buildversion[2])) >= 1: # >= 23.12.1 self.js = [ "js/mathjax.js", "js/vendor/mathjax/tex-chtml-full.js",