Conecta o formulário WebKit WebView a um retorno de chamada Python?

31

Estou escrevendo um pequeno aplicativo Python e WebKit; Estou usando o WebKit como a interface do usuário do meu aplicativo.

O que eu quero fazer é criar um elemento interativo no WebKit (ou seja, uma caixa de combinação ou um conjunto de regiões clicáveis) e quando o usuário interage com esses elementos eu posso chamar um callback Python para fazer algum processamento e atualizar o Visualização do WebKit com novas informações.

Aqui está um exemplo do que eu quero fazer:

  1. O usuário é apresentado com uma caixa de combinação de opções (caixa de combinação exibida, suponho, usando um formulário HTML no WebKit).
  2. Quando a opção da caixa de combinação é selecionada, on_combo_selected() é chamado no meu script Python, que então captura alguns dados.
  3. Os dados são transmitidos para o WebKit e a visualização é atualizada.

Como posso fazer isso?

    
por jonobacon 21.01.2012 / 21:28

2 respostas

30

Formas de comunicação de um widget incorporado do WebKit para o programa Python de controle

Gtk ou Qt:

  • defina window.status do JavaScript; interceptar o evento correspondente no Python
  • defina document.title do JavaScript; interceptar o evento correspondente no Python
  • redirecionar a página para um URL personalizado (digamos, x-my-app:thing1/thing2/thing3 ); trap navigation-policy-decision-requested event no Python, observe o URL que está sendo navegado e lide com ele se for seu esquema de URL personalizado

Somente Qt:

  • use frame.addToJavaScriptWindowObject para adicionar um objeto Python (projetado especificamente) ao namespace global JavaScript; chame métodos nele de JavaScript. (Veja link para um exemplo.)

Métodos que teoricamente deveriam funcionar, mas na prática não, muito bem

  • Dispara um evento DOM personalizado em um elemento HTML (que incluiria o objeto do documento) do JavaScript; interceptar esse evento no Python, navegando no WebKit DOM do Python e ouvindo os eventos. Isso funciona, mas não consigo encontrar uma maneira de o Python conseguir ler dados personalizados do evento, o que significa que você não pode passar informações além do evento que foi acionado.

Formas de comunicação do Python para JavaScript

  • use webview.execute_script(js_code) . Observe que, se você passar valores de variável com o JS, é uma boa ideia codificá-los no JSON. Dessa forma, você pode se preocupar um pouco menos com o escape, e o JS pode ler JSON nativamente

Veja alguns exemplos 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()
    
por sil 22.01.2012 / 22:04
18

Já faz um tempo desde que eu joguei com isso, mas aqui está o que eu fiz:

Use algo como

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

no seu HTML. Portanto, quando o usuário seleciona um item, um mycombo -URI com o valor selecionado é chamado. Para capturar isso, conecte um manipulador ao sinal navigation-requested do seu WebView:

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

No seu manipulador de sinal, você verifica se a solicitação é de mycombo URI. Se assim for, chame 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 atualizar seu WebView, use sua função execute_script para executar algum código JavaScript que atualize, como:

self.webview.execute_script("document.title='Updated!';")
    
por Florian Diesch 21.01.2012 / 22:37