Connecter le formulaire WebKit WebView à un rappel Python?

31

J'écris une petite application Python et WebKit; J'utilise WebKit comme interface utilisateur pour mon application.

Ce que je veux faire est de créer un élément interactif dans WebKit (à savoir une zone de liste déroulante ou un ensemble de régions cliquables) et lorsque l'utilisateur interagit avec ces éléments, je peux appeler un rappel Python pour effectuer un traitement, puis mettre à jour la vue WebKit avec nouvelle information.

Voici un exemple de ce que je veux faire:

  1. L'utilisateur est présenté avec une zone de liste déroulante d'options (zone de liste déroulante affichée, je suppose, en utilisant un formulaire HTML dans WebKit).
  2. Lorsque l'option de zone de liste déroulante est sélectionnée, on_combo_selected()est appelée dans mon script Python qui récupère ensuite certaines données.
  3. Les données sont transmises à WebKit et la vue est mise à jour.

Comment puis-je faire ceci?

jonobacon
la source

Réponses:

31

Façons de communiquer à partir d'un widget WebKit intégré vers le programme Python de contrôle

Gtk ou Qt:

  • défini à window.statuspartir de JavaScript; intercepter l'événement correspondant en Python
  • défini à document.titlepartir de JavaScript; intercepter l'événement correspondant en Python
  • rediriger la page vers une URL personnalisée (par exemple x-my-app:thing1/thing2/thing3); piège navigation-policy-decision-requestedévénement en Python et regardez l'URL vers laquelle vous naviguez et traitez-le s'il s'agit de votre schéma d'URL personnalisé

Qt uniquement:

Des méthodes qui devraient théoriquement fonctionner mais qui ne fonctionnent pas très bien dans la pratique

  • Déclenchez un événement DOM personnalisé sur un élément HTML (qui inclurait l'objet document) à partir de JavaScript; intercepter cet événement dans Python en naviguant dans le DOM WebKit à partir de Python et en écoutant les événements. Cela fonctionne, mais je ne trouve pas de moyen pour que Python puisse lire les données personnalisées de l'événement, ce qui signifie que vous ne pouvez pas transmettre d'informations autres que l'événement ayant déclenché.

Façons de communiquer de Python à JavaScript

  • utiliser webview.execute_script(js_code). Notez que si vous transmettez des valeurs variables avec le JS, c'est une bonne idée de les coder en JSON; de cette façon, vous pouvez vous soucier un peu moins de s'échapper, et JS peut lire JSON en natif

Voici un exemple de code 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
la source
18

Cela fait un moment que je n'ai pas joué avec ça mais voici ce que j'ai fait:

Utilisez quelque chose comme

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

dans votre HTML. Ainsi, lorsque l'utilisateur sélectionne un élément, un mycombo-URI avec la valeur sélectionnée est appelé. Pour intercepter cela, connectez un gestionnaire au navigation-requestedsignal de votre WebView :

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

Dans votre gestionnaire de signaux, vous vérifiez si la demande concerne un mycomboURI. Si oui, appelez 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

Pour mettre à jour votre WebView, utilisez sa execute_scriptfonction pour exécuter du code JavaScript qui effectue les mises à jour, comme:

self.webview.execute_script("document.title='Updated!';")
Florian Diesch
la source
1
Une idée de comment récupérer les en-têtes de messages req?
Flimm