PyGTK, Threads and WebKit

2019-04-13 23:39发布

问题:

In my PyGTK app, on button click I need to:

  1. Fetch some html (can take some time)
  2. Show it in new window

While fetching html, I want to keep GUI responsive, so I decided to do it in separate thread. I use WebKit to render html.

The problem is I get empty page in WebView when it is in separated thread.

This works:

import gtk
import webkit

webView = webkit.WebView()
webView.load_html_string('<h1>Hello Mars</h1>', 'file:///')
window = gtk.Window()
window.add(webView)
window.show_all()
gtk.mainloop()

This does not work, produces empty window:

import gtk
import webkit
import threading

def show_html():
    webView = webkit.WebView()
    webView.load_html_string('<h1>Hello Mars</h1>', 'file:///')
    window = gtk.Window()
    window.add(webView)
    window.show_all()

thread = threading.Thread(target=show_html)
thread.setDaemon(True)
thread.start()
gtk.mainloop()

Is it because webkit is not thread-safe. Is there any workaround for this?

回答1:

According to my experience, one of the things that sometimes doesn't work as you expect with gtk is the update of widgets in separate threads.

To workaround this problem, you can work with the data in threads, and use glib.idle_add to schedule the update of the widget in the main thread once the data has been processed.

The following code is an updated version of your example that works for me (the time.sleep is used to simulate the delay in getting the html in a real scenario):

import gtk, glib
import webkit
import threading
import time

# Use threads                                       
gtk.gdk.threads_init()

class App(object):
    def __init__(self):
        window = gtk.Window()
        webView = webkit.WebView()
        window.add(webView)
        window.show_all()

        self.window = window
        self.webView = webView

    def run(self):
        gtk.main()

    def show_html(self):
        # Get your html string                     
        time.sleep(3)
        html_str = '<h1>Hello Mars</h1>'

        # Update widget in main thread             
        glib.idle_add(self.webView.load_html_string,
                      html_str, 'file:///')

app = App()

thread = threading.Thread(target=app.show_html)
thread.start()

app.run()
gtk.main()


回答2:

I don't know anything about webkit inner workings, but maybe you can try it with multiple processes.