31

I am writing a small Python and WebKit app; I am using WebKit as the UI for my app.

What I want to do is to create an interactive element in WebKit (namely a combo box or a set of clickable regions) and when the user interacts with these elements I can call a Python callback to do some processing and then update the WebKit view with new information.

Here is an example of what I want to do:

  1. User is presented with a combo box of options (combo box displayed, I assume, using a HTML form in WebKit).
  2. When the combo box option is selected, on_combo_selected() is called in my Python script which then grabs some data.
  3. The data is passed to WebKit and the view is updated.

How can I do this?

0

2 Answers 2

31
+50

Ways to communicate from an embedded WebKit widget to the controlling Python program

Gtk or Qt:

  • set window.status from JavaScript; trap the corresponding event in Python
  • set document.title from JavaScript; trap the corresponding event in Python
  • redirect the page to a custom URL (say, x-my-app:thing1/thing2/thing3); trap navigation-policy-decision-requested event in Python and look at the URL that's being navigated to, and deal with it if it's your custom URL scheme

Qt only:

Methods which theoretically should work but in practice don't, very well

  • Fire a custom DOM event on an HTML element (which would include the document object) from JavaScript; trap that event in Python by navigating the WebKit DOM from Python and listening for events. This works, but I can't find a way of having Python be able to read custom data from the event, which means that you can't pass information other than the event having fired.

Ways to communicate from Python to JavaScript

  • use webview.execute_script(js_code). Note that if you're passing variable values in with the JS, it's a good idea to JSON encode them; that way you can worry a bit less about escaping, and JS can read JSON natively

Here's some example 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()
18

It's been a while since I played with that but here's what I did:

Use something like

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

in your HTML. So when the user selects an item a mycombo-URI with the selected value is called. To catch this connect a handler to your WebView's navigation-requested signal:

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

In your signal handler you check if the request is for a mycombo URI. If so call 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

To update your WebView use its execute_script function to run some JavaScript code that does the updates, like:

self.webview.execute_script("document.title='Updated!';")
1
  • 1
    Any idea how to retrieve post headers from req?
    – Flimm
    Mar 5, 2013 at 19:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .