I’ve been working on a Gtk project a bit recently (A web browser called Roland, which I’ll talk about another day), and I recently came across an interaction problem.
WebKit2.WebView has a signal called
script-dialog, which is fired whenever
window.prompt, is called. There’s no way to handle
this asynchronously, and I couldn’t figure out a way to handle it synchronously
without freezing up the UI. I knew there had to be a way however, because,
well, the default implementation could certainly do it with a dialog box.
After looking up things on spinning up new Gtk mainloops, I found some documentation which states:
It is possible to create new instances of GMainLoop recursively. This is often used in GTK+ applications when showing modal dialog boxes.
“Could it really be that simple? Do I just call
Gtk.main() again?” I asked
myself. So I sat down and wrote out what I thought would theoretically work,
and behold! It just worked. Within your signal handler you simply start another
event loop, wait on the event you’re interested in, and when it occurs you
Because it’s recursive, it will stop the inner most loop and let the outer loop continue, with the UI never having frozen. What follows is a small example demonstrating how it works.
from gi.repository import WebKit2, Gtk def get_something_async(): def activate(*args): entry.disconnect(id) Gtk.main_quit() id = entry.connect('activate', activate) # This will block until the activate function above is called, which # stops the secondary loop. Gtk.main() return entry.get_text() def script_dialog(view, dialog): if dialog.get_dialog_type() == WebKit2.ScriptDialogType.PROMPT: result = get_something_async() dialog.prompt_set_text(result) return True elif dialog.get_dialog_type() == WebKit2.ScriptDialogType.ALERT: print('message', dialog.get_message()) return True return False view = WebKit2.WebView() view.connect('script-dialog', script_dialog) view.load_uri('file:///path/to/prompt.html') window = Gtk.Window() window.connect('destroy', Gtk.main_quit) entry = Gtk.Entry() vbox = Gtk.VBox() vbox.add(view) vbox.add(entry) window.add(vbox) window.show_all() Gtk.main()
prompt.html looks like this:
It turns out that’s all there is to it. Enjoy!