WebKit WebView-Formular mit einem Python-Rückruf verbinden?

31

Ich schreibe eine kleine Python- und WebKit-App. Ich verwende WebKit als Benutzeroberfläche für meine App.

Ich möchte ein interaktives Element in WebKit erstellen (dh ein Kombinationsfeld oder eine Reihe anklickbarer Bereiche). Wenn der Benutzer mit diesen Elementen interagiert, kann ich einen Python-Rückruf aufrufen, um einige Verarbeitungsschritte auszuführen und dann die WebKit-Ansicht mit zu aktualisieren neue Informationen.

Hier ist ein Beispiel dafür, was ich tun möchte:

  1. Dem Benutzer wird ein Kombinationsfeld mit Optionen angezeigt (das Kombinationsfeld wird vermutlich mithilfe eines HTML-Formulars in WebKit angezeigt).
  2. Wenn die Kombinationsfeldoption ausgewählt ist, on_combo_selected()wird sie in meinem Python-Skript aufgerufen, das dann einige Daten erfasst.
  3. Die Daten werden an WebKit übergeben und die Ansicht aktualisiert.

Wie kann ich das machen?

Jonobacon
quelle

Antworten:

31

Kommunikationsmöglichkeiten zwischen einem eingebetteten WebKit-Widget und dem steuernden Python-Programm

Gtk oder Qt:

  • Set window.statusvon JavaScript; Überfüllen Sie das entsprechende Ereignis in Python
  • Set document.titlevon JavaScript; Überfüllen Sie das entsprechende Ereignis in Python
  • Leiten Sie die Seite zu einer benutzerdefinierten URL um (z. B. x-my-app:thing1/thing2/thing3). Überfüllen Sie das navigation-policy-decision-requestedEreignis in Python, und überprüfen Sie die URL, zu der navigiert wird. Behandeln Sie es, wenn es sich um Ihr benutzerdefiniertes URL-Schema handelt

Nur Qt:

Methoden, die theoretisch funktionieren sollten, aber in der Praxis nicht gut funktionieren

  • Auslösen eines benutzerdefinierten DOM-Ereignisses für ein HTML-Element (das das Dokumentobjekt enthalten würde) aus JavaScript; Fangen Sie dieses Ereignis in Python ab, indem Sie in Python im WebKit-DOM navigieren und auf Ereignisse warten. Dies funktioniert, aber ich kann keine Möglichkeit finden, dass Python benutzerdefinierte Daten aus dem Ereignis lesen kann, was bedeutet, dass Sie keine anderen Informationen als die ausgelösten Ereignisse weitergeben können.

Kommunikationsmöglichkeiten zwischen Python und JavaScript

  • verwenden webview.execute_script(js_code). Beachten Sie, dass es eine gute Idee ist, Variablenwerte mit JSON zu codieren, wenn Sie sie mit JS übergeben. Auf diese Weise können Sie sich ein bisschen weniger Gedanken über das Entkommen machen, und JS kann JSON nativ lesen

Hier ist ein Beispiel für einen Gtk-Code:

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()
sil
quelle
18

Es ist schon eine Weile her, dass ich damit gespielt habe, aber hier ist, was ich getan habe:

Verwenden Sie so etwas wie

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

in Ihrem HTML. Wenn der Benutzer ein Element auswählt, wird ein mycombo-URI mit dem ausgewählten Wert aufgerufen. Um dies abzufangen, verbinden Sie einen Handler mit dem navigation-requestedSignal Ihres WebView :

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

In Ihrem Signalhandler prüfen Sie, ob es sich bei der Anforderung um eine mycomboURI handelt. Wenn ja, rufen Sie an 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

Um Ihr WebView zu aktualisieren, verwenden Sie seine execute_scriptFunktion, um einen JavaScript-Code auszuführen, der die Aktualisierungen ausführt , wie zum Beispiel:

self.webview.execute_script("document.title='Updated!';")
Florian Diesch
quelle
1
Hast reqdu eine Idee, wie man Post-Header abruft ?
Flimm