Connetti il ​​modulo WebKit WebView a un callback Python?


31

Sto scrivendo una piccola app Python e WebKit; Sto usando WebKit come interfaccia utente per la mia app.

Quello che voglio fare è creare un elemento interattivo in WebKit (ovvero una casella combinata o un insieme di aree cliccabili) e quando l'utente interagisce con questi elementi posso chiamare un callback Python per fare un po 'di elaborazione e quindi aggiornare la vista WebKit con nuova informazione.

Ecco un esempio di ciò che voglio fare:

  1. All'utente viene presentata una casella combinata di opzioni (casella combinata visualizzata, presumo, utilizzando un modulo HTML in WebKit).
  2. Quando l'opzione casella combinata è selezionata, on_combo_selected()viene chiamata nel mio script Python che quindi acquisisce alcuni dati.
  3. I dati vengono passati a WebKit e la vista viene aggiornata.

Come posso fare questo?

Risposte:


31

Modi per comunicare da un widget WebKit incorporato al programma Python di controllo

Gtk o Qt:

  • impostato window.statusda JavaScript; intercettare l'evento corrispondente in Python
  • impostato document.titleda JavaScript; intercettare l'evento corrispondente in Python
  • reindirizzare la pagina a un URL personalizzato (ad esempio, x-my-app:thing1/thing2/thing3); trappola l' navigation-policy-decision-requestedevento in Python e osserva l'URL a cui stai navigando, e gestiscilo se è il tuo schema URL personalizzato

Solo Qt:

Metodi che teoricamente dovrebbero funzionare ma in pratica non funzionano molto bene

  • Attiva un evento DOM personalizzato su un elemento HTML (che includerebbe l'oggetto documento) da JavaScript; intercettare quell'evento in Python navigando nel DOM WebKit da Python e ascoltando gli eventi. Funziona, ma non riesco a trovare un modo per far sì che Python sia in grado di leggere i dati personalizzati dall'evento, il che significa che non è possibile trasmettere informazioni diverse dall'evento che è stato generato.

Modi per comunicare da Python a JavaScript

  • usare webview.execute_script(js_code). Nota che se stai passando valori variabili con JS, è una buona idea codificarli JSON; in questo modo puoi preoccuparti un po 'meno dell'evasione e JS può leggere JSON in modo nativo

Ecco alcuni esempi di codice Gtk:

from gi.repository import Gtk,WebKit
import json

w = Gtk.Window()
v = WebKit.WebView()
sw = Gtk.ScrolledWindow()
w.add(sw)
sw.add(v)
w.set_size_request(400,300)
w.connect("destroy", lambda q: Gtk.main_quit())
def window_title_change(v, param):
    if not v.get_title():
        return
    if v.get_title().startswith("msgtopython:::"):
        message = v.get_title().split(":::",1)[1]
        # Now, send a message back to JavaScript
        return_message = "You chose '%s'. How interesting." % message
        v.execute_script("jscallback(%s)" % json.dumps(return_message))
v.connect("notify::title", window_title_change)
v.load_html_string("""<!doctype html>
<html>
<head>
<title>A demo</title>
<style>
body { font-family: Ubuntu, sans-serif; }
h1 { font-size: 1.3em; }
</style>
<body>
<h1>A tiny JavaScript demonstration</h1>
<form>
<p>What's your favourite thing about Jono? <select>
    <option>------choose one------</option>
    <option>his beard</option>
    <option>his infectious sense of humour</option>
    <option>his infectious diseases</option>
    <option>his guitar ability</option>
    <option>his wife</option>
</select></p>
</form>
<p id="out"></p>

<script>
document.querySelector("select").addEventListener("change", function() {
    var chosenOption = this.options[this.selectedIndex].text;
    // Now send that text back to Python by setting the title
    document.title = "msgtopython:::" + chosenOption;
}, false);
function jscallback(msg) {
    document.getElementById("out").innerHTML = msg;
}
</script>

</body>
</html>
""", "file:///")
w.show_all()
Gtk.main()

18

È da un po 'che non ci gioco, ma ecco cosa ho fatto:

Usa qualcosa di simile

<form>
    <select  onchange="location.href='mycombo://'+this.value">
      <option>foo</option>
      <option>bar</option>
      <option>baz</option>
    </select>
</form>

nel tuo HTML. Quindi quando l'utente seleziona un elemento mycomboviene chiamato un -URI con il valore selezionato. Per catturare questo collegare un gestore al navigation-requestedsegnale del tuo WebView :

self.webview.connect("navigation-requested", self.on_navigation_requested)

Nel gestore del segnale controlli se la richiesta riguarda un mycomboURI. In tal caso chiama on_combo_selected:

def on_navigation_requested(self, view, frame, req, data=None):
    uri = req.get_uri()
    scheme, path=uri.split(':', 1)
    if scheme == 'mycombo':
        self.on_combo_selected(path) 
        return True
    else:
        return False

Per aggiornare il tuo WebView usa la sua execute_scriptfunzione per eseguire del codice JavaScript che esegue gli aggiornamenti, come:

self.webview.execute_script("document.title='Updated!';")

1
Qualche idea su come recuperare le intestazioni dei post req?
Flimm,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.