Use result of JavaScript call in Python (Add-on development)

The add-on docs state the following:

  • Javascript is evaluated asynchronously, so if you need the result of a JS expression you can use ankiwebview’s evalWithCallback().

Although the communication between Python and JS works as intended, I’m clueless how to use the result of a JS call effectively within Python - not with another editor.web.eval(), but as a return value of the Python function.

Scenario

I would like to edit field content before it is saved by Anki (editor_will_munge_html) with a JavaScript function, because it is a lot easier to do with DOM-manipulation rather than raw text editing.

Basic idea (flawed)

def on_munge(txt, editor):
    replacement = txt

    def callback(value) -> None: # <- asynchronous
        replacement = value

    editor.web.evalWithCallback(f"SomeObject.munge('{txt}')", callback)

    # is there a way to await evalWithCallback
    # before returning?
    return replacement

I reckon making on_munge async won’t work because it will not be awaited by Anki. Is there an API-solution for this kind of scenario, where you want to await the result of a JS call / assign it to a variable?


Or is there an altogether different way to solve my problem?

1 Like

For now, I resorted to Beautifulsoup to edit the string. Still interested whether this would have been possible with JS :v:

I’m also remember breaking my head on something like this.
I came to the conclusion that it’s not possible (which is due to the Qt implementation).

One hacky alternative that would probably work, is getting the prototype of an Editable object, and then overwrite the Editable.prototype.fieldHTML getter. But I think you might already have the better solution.

1 Like

No big deal. I think doing it without async back- and forth is more performant anyway. In the end I’m glad my laziness didn’t win and I had to step out of my comfort zone.

Beautifulsoup is seriously impressive and covers all my needs. As a bonus, it can pretty-print the output, so it looks nice in the HTML editor (but I guess you’ll soon implement this natively with Prettier?).

2 Likes

It’s not really practical to implement async/await with Qt at the moment. You can’t mutate local function state with a callback, so that state either needs to be captured in a closure that you pass as the callback, or the state needs to be stored in an instance variable, with processing continuing in the callback (eg deckbrowser.py:_renderPage())

2 Likes