2

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?

umpirsky
  • 9,902
  • 13
  • 71
  • 96

2 Answers2

3

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()
jcollado
  • 39,419
  • 8
  • 102
  • 133
  • Thanks, that did the trick. Just one thing to note, I tried to move window.show_all() to show_html() method, but that fails with Segmentation fault, strange, isn't it? I used self.window ofc. – umpirsky Dec 26 '11 at 09:31
  • Do you mean directly in the `show_html` method? If you need that, you can define another method that does it and the `webView.load_html_string` and schedule it with `glib.idle_add`. – jcollado Dec 26 '11 at 09:38
  • Why can't I schedule it directly in show_html() right after load_html_string? – umpirsky Dec 26 '11 at 09:45
  • pygtk explicitly states it's not really thread-safe, you have to use idle_add with threads_enter/leave wrapper for all operations from other threads – Dima Tisnek Oct 27 '12 at 16:30
0

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

XORcist
  • 4,288
  • 24
  • 32