เชื่อมต่อแบบฟอร์ม WebKit WebView กับ Python callback หรือไม่


31

ฉันกำลังเขียนแอพ Python และ WebKit ขนาดเล็ก ฉันใช้ WebKit เป็น UI สำหรับแอปของฉัน

สิ่งที่ฉันต้องการทำคือการสร้างองค์ประกอบแบบโต้ตอบใน WebKit (เช่นกล่องคำสั่งผสมหรือชุดของภูมิภาคที่คลิกได้) และเมื่อผู้ใช้โต้ตอบกับองค์ประกอบเหล่านี้ฉันสามารถเรียก Python โทรกลับเพื่อทำการประมวลผลบางส่วนแล้วปรับปรุงมุมมอง WebKit ด้วย ข้อมูลใหม่.

นี่คือตัวอย่างของสิ่งที่ฉันต้องการจะทำ:

  1. ผู้ใช้จะถูกนำเสนอด้วยกล่องตัวเลือกคำสั่งผสม (กล่องคำสั่งผสมที่แสดงฉันถือว่าโดยใช้รูปแบบ HTML ใน WebKit)
  2. เมื่อเลือกตัวเลือกคอมโบบ็อกซ์จะon_combo_selected()ถูกเรียกในสคริปต์ Python ของฉันซึ่งจะจับข้อมูลบางอย่าง
  3. ข้อมูลถูกส่งผ่านไปยัง WebKit และมุมมองได้รับการอัปเดต

ฉันจะทำสิ่งนี้ได้อย่างไร

คำตอบ:


31

วิธีในการสื่อสารจากวิดเจ็ต WebKit ในตัวไปยังโปรแกรม Python ที่ควบคุม

Gtk หรือ Qt:

  • ตั้งwindow.statusจาก JavaScript; ดักเหตุการณ์ที่เกี่ยวข้องใน Python
  • ตั้งdocument.titleจาก JavaScript; ดักเหตุการณ์ที่เกี่ยวข้องใน Python
  • เปลี่ยนเส้นทางหน้าไปยัง URL ที่กำหนดเอง (พูด, x-my-app:thing1/thing2/thing3); navigation-policy-decision-requestedเหตุการณ์การดักจับใน Python และดู URL ที่ถูกนำทางไปและจัดการกับมันถ้ามันเป็นรูปแบบ URL ที่กำหนดเองของคุณ

Qt เท่านั้น:

  • ใช้frame.addToJavaScriptWindowObjectเพื่อเพิ่มวัตถุหลาม (ออกแบบมาโดยเฉพาะ) ไปยังเนมสเปซส่วนกลางของ JavaScript วิธีการโทรจาก JavaScript (ดูhttp://pysnippet.blogspot.com/2010/01/calling-python-from-javascript-in-pyqts.htmlเพื่อดูตัวอย่าง)

วิธีการในทางทฤษฎีควรใช้งานได้ แต่ในทางปฏิบัติแล้วก็ทำได้ไม่ดีนัก

  • ดำเนินการเหตุการณ์ DOM ที่กำหนดเองในองค์ประกอบ HTML (ซึ่งจะรวมถึงวัตถุเอกสาร) จาก JavaScript ดักเหตุการณ์นั้นใน Python โดยการนำทาง WebKit DOM จาก Python และฟังเหตุการณ์ วิธีนี้ใช้ได้ผล แต่ฉันไม่สามารถหาวิธีให้ Python อ่านข้อมูลที่กำหนดเองจากเหตุการณ์ซึ่งหมายความว่าคุณไม่สามารถส่งผ่านข้อมูลอื่นนอกเหนือจากเหตุการณ์ที่เกิดขึ้น

วิธีสื่อสารจาก Python ไปยัง JavaScript

  • webview.execute_script(js_code)ใช้ โปรดทราบว่าถ้าคุณส่งค่าตัวแปรด้วย JS คุณควรเข้ารหัส JSON วิธีนี้จะทำให้คุณกังวลน้อยลงเกี่ยวกับการหลบหนีและ JS สามารถอ่าน JSON ได้อย่างเป็นธรรมชาติ

นี่คือตัวอย่างโค้ด 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()

18

ไม่นานมานี้ที่ฉันเล่นด้วย แต่นี่คือสิ่งที่ฉันทำ:

ใช้สิ่งที่ชอบ

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

ใน HTML ของคุณ ดังนั้นเมื่อผู้ใช้เลือกรายการmycombo-URI ด้วยค่าที่เลือกจะถูกเรียกว่า ในการจับสิ่งนี้เชื่อมต่อตัวจัดการกับnavigation-requestedสัญญาณของ WebView ของคุณ:

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

ในตัวจัดการสัญญาณของคุณคุณตรวจสอบว่าคำขอนั้นเป็นmycomboURI หรือไม่ ถ้าเป็นเช่นนั้นโทร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

ในการอัปเดต WebView ของคุณใช้execute_scriptฟังก์ชั่นเพื่อเรียกใช้โค้ด JavaScript บางตัวที่ทำหน้าที่อัปเดตเช่น:

self.webview.execute_script("document.title='Updated!';")

1
มีความคิดวิธีการดึงส่วนหัวของโพสต์มาจากreqไหน?
Flimm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.