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
a JavaScript dialog, e.g. 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
quit.
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()
Where prompt.html
looks like this:
<html>
<body>
<script type="text/javascript" charset="utf-8">
var name = window.prompt('whats your name?');
window.alert('have name ' + name);
</script>
</body>
</html>
It turns out that’s all there is to it. Enjoy!