¿Conectar el formulario WebKit WebView a una devolución de llamada de Python?

31

Estoy escribiendo una pequeña aplicación Python y WebKit; Estoy usando WebKit como la interfaz de usuario para mi aplicación.

Lo que quiero hacer es crear un elemento interactivo en WebKit (es decir, un cuadro combinado o un conjunto de regiones en las que se puede hacer clic) y cuando el usuario interactúa con estos elementos, puedo llamar a una devolución de llamada de Python para hacer un procesamiento y luego actualizar la vista de WebKit con nueva información.

Aquí hay un ejemplo de lo que quiero hacer:

  1. Al usuario se le presenta un cuadro combinado de opciones (supongo que se muestra un cuadro combinado usando un formulario HTML en WebKit).
  2. Cuando se selecciona la opción del cuadro combinado, on_combo_selected()se invoca en mi script de Python que luego toma algunos datos.
  3. Los datos se pasan a WebKit y la vista se actualiza.

¿Cómo puedo hacer esto?

jonobacon
fuente

Respuestas:

31

Formas de comunicarse desde un widget WebKit incorporado al programa Python controlador

Gtk o Qt:

  • establecer window.statusdesde JavaScript; atrapar el evento correspondiente en Python
  • establecer document.titledesde JavaScript; atrapar el evento correspondiente en Python
  • redirigir la página a una URL personalizada (por ejemplo, x-my-app:thing1/thing2/thing3); atrape el navigation-policy-decision-requestedevento en Python y mire la URL a la que se está navegando, y trátelo si es su esquema de URL personalizado

Solo Qt:

Métodos que teóricamente deberían funcionar pero en la práctica no funcionan muy bien

  • Activa un evento DOM personalizado en un elemento HTML (que incluiría el objeto del documento) desde JavaScript; atrape ese evento en Python navegando el WebKit DOM desde Python y escuchando eventos. Esto funciona, pero no puedo encontrar una manera de que Python pueda leer datos personalizados del evento, lo que significa que no puede pasar información que no sea el evento que se disparó.

Formas de comunicarse desde Python a JavaScript

  • uso webview.execute_script(js_code). Tenga en cuenta que si pasa valores variables con JS, es una buena idea que JSON los codifique; de esa manera puede preocuparse un poco menos por escapar, y JS puede leer JSON de forma nativa

Aquí hay un ejemplo de código 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
fuente
18

Ha pasado un tiempo desde que jugué con eso, pero esto es lo que hice:

Usa algo como

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

en tu HTML. Entonces, cuando el usuario selecciona un elemento, se mycombollama a -URI con el valor seleccionado. Para captar esto, conecte un controlador a la navigation-requestedseñal de su WebView :

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

En su controlador de señal, verifica si la solicitud es para un mycomboURI. Si es así, llame 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

Para actualizar su WebView, use su execute_scriptfunción para ejecutar algún código JavaScript que realice las actualizaciones, como:

self.webview.execute_script("document.title='Updated!';")
Florian Diesch
fuente
1
¿Alguna idea de cómo recuperar encabezados de publicación req?
Flimm