Who consumes AnkiWebView's events after finishing a deck?

I’m developing an addon that acts on a left click during review. Finishing a deck causes this to stop working.

The addon wraps the web view’s event filter:

AnkiWebView.eventFilter = wrap(AnkiWebView.eventFilter, eventFilter, “around”)
or
AnkiWebView.eventFilter = eventFilter

with

def eventFilter(self, obj, evt):
# code here is still executed
if evt.type() == QEvent.MouseButtonRelease and evt.button() == Qt.LeftButton:
# code here doesn’t work anymore

Everything works fine until a deck is finished, i.e. all cards in the deck are completed. Afterwards, eventFilter is still called, but it doesn’t receive mouse events anymore. I digged around in the source code, but couldn’t find an answer myself. Another addon written by someone else is also affected by this.

Can someone shed light on what’s happening here? The issue is easily reproduced by adding an empty deck, choosing it in the deck brower, then switching to another deck.

Instead of monkey-patching that routine, perhaps you’d have more success injecting an event handler into the DOM using .eval(). The congrats screen is a separate webpage, so you’d need to do it there too.

Thanks for pointing me into this direction. Since I have close to zero knowledge in web development, this is very hard to use and debug. Can you point me at some example or other resource? Even after googling and trying other addons for a while now I’m almost clueless.

Try passing something like this to .eval():

document.addEventListener('mouseup', e => {
  if(e.button == 0) {
    // left button released
  }
});

See MouseEvent.button - Web APIs | MDN for more info

2 Likes

Thanks for your hint, I could solve it. However, document.addEventListener didn’t work for two reasons:

  1. .eval() needs to be evoked multiple times, because every time the content of the web view changes, the event handler is lost.
  2. depending on the hook that triggers injection of the javascript code, addEventListener accumulates handlers. These are all executed in a row afterwards.

So I ended up using the global event handler:

.eval("""document.onclick = () => { pycmd("clicked"); };""")

I think you can work around both issues by injecting your code in the webview_will_set_content hook instead.

from aqt import gui_hooks

def on_webview_will_set_content(web_content, context):

	if not isinstance(context, (aqt.reviewer.Reviewer, aqt.overview.Overview)):
		return

	js = """
	document.addEventListener('mouseup', e => {
		if(e.button == 0) {
			// left button released
		}
	});
	"""

	web_content.head += f"<script>{js}</script>"


gui_hooks.webview_will_set_content.append(on_webview_will_set_content)

See anki/genhooks_gui.py at 2265bfe71da0dff6756c8d4d9109aaaae259e645 · ankitects/anki · GitHub

1 Like
  • The dynamic_style hook can be used for the congrats screen
  • A global handler is likely to cause conflicts down the line

I faced similar problems developing Review Hotmouse addon. (addon for mouse shortcuts/gestures)

To solve this purely on qt side, you need to listen for AnkiWebView’s child event for child creation, then install event filters on the newly created child as well.

As long as it’s only mouse press/release event you are listening to, you won’t need to inject a script to the web. But you can find the code to do both in my addon code which needed to inject a js script for the mouse wheel event.

Thanks for your response! However, the other addon with the same problem I mentioned in my initial post is actually yours. I searched Ankiweb for mouse and keyboard-related addons and checked your code for a possible solution. In Anki 2.1.44, it has the same problem I described in my post and in 2.1.46, it’s not loading anymore. If you’re interested interested in my solution, I can share some code with you.

I recently updated the addon code, but haven’t uploaded it yet. The new version should work in 2.1.46 and fix the above issue. Release Review Hotmouse v2.0 · BlueGreenMagick/Review-Hotmouse · GitHub

I think the problem was because I didn’t recursively add event filters to all its children when they were created.