Połączyć formularz WebView WebKit z wywołaniem zwrotnym Pythona?

31

Piszę małą aplikację Python i WebKit; Używam WebKit jako interfejsu użytkownika mojej aplikacji.

Chcę utworzyć element interaktywny w WebKit (mianowicie pole kombi lub zestaw klikalnych regionów), a kiedy użytkownik wchodzi w interakcję z tymi elementami, mogę wywołać wywołanie zwrotne Pythona, aby wykonać pewne przetwarzanie, a następnie zaktualizować widok WebKit za pomocą Nowa informacja.

Oto przykład tego, co chcę zrobić:

  1. Użytkownikowi wyświetla się pole kombi z opcjami (zakładam, że wyświetlane jest pole kombi przy użyciu formularza HTML w WebKit).
  2. Gdy wybrana jest opcja pola kombi, on_combo_selected()jest wywoływana w moim skrypcie Python, który następnie pobiera niektóre dane.
  3. Dane są przekazywane do WebKit, a widok jest aktualizowany.

W jaki sposób mogę to zrobić?

jonobacon
źródło

Odpowiedzi:

31

Sposoby komunikacji z osadzonego widgetu WebKit do kontrolującego programu Python

Gtk lub Qt:

  • ustawiony window.statusz JavaScript; przechwytuje odpowiednie zdarzenie w Pythonie
  • ustawiony document.titlez JavaScript; przechwytuje odpowiednie zdarzenie w Pythonie
  • przekieruj stronę na niestandardowy adres URL (powiedzmy x-my-app:thing1/thing2/thing3); przechwytuj navigation-policy-decision-requestedzdarzenia w Pythonie i spójrz na adres URL, do którego prowadzi nawigacja, i radz sobie z tym, jeśli jest to Twój niestandardowy schemat adresów URL

Tylko Qt:

Metody, które teoretycznie powinny działać, ale w praktyce nie, bardzo dobrze

  • Uruchom niestandardowe zdarzenie DOM na elemencie HTML (który obejmowałby obiekt dokumentu) z JavaScript; przechwytuj to zdarzenie w Pythonie, nawigując DOM WebKit z Pythona i nasłuchując zdarzeń. To działa, ale nie mogę znaleźć sposobu, aby Python mógł odczytać niestandardowe dane ze zdarzenia, co oznacza, że ​​nie możesz przekazać informacji innych niż zdarzenie, które zostało uruchomione.

Sposoby komunikacji z Python do JavaScript

  • użyć webview.execute_script(js_code). Zauważ, że jeśli przekazujesz wartości zmiennych za pomocą JS, dobrym pomysłem jest ich kodowanie; w ten sposób możesz mniej martwić się ucieczką, a JS może czytać JSON natywnie

Oto przykładowy kod 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()
sil
źródło
18

Minęło trochę czasu, odkąd grałem z tym, ale oto co zrobiłem:

Użyj czegoś takiego

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

w swoim HTML. Kiedy więc użytkownik wybierze element, mycombowywoływana jest opcja -URI o wybranej wartości. Aby to złapać, podłącz moduł obsługi do navigation-requestedsygnału swojego WebView :

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

W module obsługi sygnału sprawdzasz, czy żądanie dotyczy mycomboidentyfikatora URI. Jeśli tak, zadzwoń 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

Aby zaktualizować WebView, użyj jego execute_scriptfunkcji, aby uruchomić kod JavaScript, który wykonuje aktualizacje, na przykład:

self.webview.execute_script("document.title='Updated!';")
Florian Diesch
źródło
1
Masz pomysł, jak pobrać nagłówki postów req?
Flimm