Reputation: 154876
I need to check whether the Escape key has been pressed during execution of some non-GUI code. (The code is in Python, but can easily call into C if necessary.) The code received a function from the GUI that it occasionally calls to check whether it has been interrupted. The question is how to implement this check.
By looking at the documentation, gdk_event_peek
seems like an excellent choice for this:
def _check_esc(self):
event = gtk.gdk.event_peek()
if event is None or event.type not in (gtk.gdk.KEY_PRESS, gtk.gdk.KEY_RELEASE):
return False
return gtk.gdk.keyval_name(event.keyval) == 'Escape'
This doesn't work, however: the event returned from gtk.gdk.event_peek()
is always None when the main loop is not running. Changing it to gtk.gdk.display_get_default().peek_event()
doesn't help either. I assume the events are in the X event queue and are not yet moved to the GDK event queue. The documentation says:
Note that this function will not get more events from the windowing system. It only checks the events that have already been moved to the GDK event queue.
So, how does one transfer the event to the GDK event queue or? In other words, when does gtk.gdk.peek_event()
ever return an event? Calling gtk.events_pending()
doesn't have any effect.
Here is a minimal program to test it:
import gtk, gobject
import time
def code(check):
while 1:
time.sleep(.1)
if check():
print 'interrupted'
return
def _check_esc():
event = gtk.gdk.event_peek()
print 'event:', event
if event is None or event.type not in (gtk.gdk.KEY_PRESS, gtk.gdk.KEY_RELEASE):
return False
return gtk.gdk.keyval_name(event.keyval) == 'Escape'
def runner():
code(_check_esc)
gtk.main_quit()
w = gtk.Window()
w.show()
gobject.idle_add(runner)
gtk.main()
When running the code, the event printed is always None, even if you press Escape or move the mouse.
I also considered installing a handler for Escape and having the checker process events with the while gtk.events_pending(): gtk.main_iteration()
idiom. This results in unqueuing and dispatch of all pending events, including keyboard and mouse events. The effect is that the GUI is responsive enabled while the code runs, which doesn't look well and can severely interfere with the execution of the code. The only event processed during execution should be the escape key to interrupt it.
Upvotes: 0
Views: 1148
Reputation: 154876
I came up with a runner
implementation that satisfies the criteria put forward in the question:
def runner():
# _check_esc searches for Escape in our queue
def _check_esc():
oldpos = len(queue)
while gtk.events_pending():
gtk.main_iteration()
new = itertools.islice(queue, oldpos, None)
return any(event.type == gtk.gdk.KEY_PRESS \
and gtk.gdk.keyval_name(event.keyval) == 'Escape'
for event in new)
queue = []
# temporarily set the global event handler to queue
# the events
gtk.gdk.event_handler_set(queue.append)
try:
code(_check_esc)
finally:
# restore the handler and replay the events
handler = gtk.main_do_event
gtk.gdk.event_handler_set(gtk.main_do_event)
for event in queue:
handler(event)
gtk.main_quit()
Compared to a peek-based solution, its advantage is that it handles the case when another event arrives after the keypress. The disadvantage is that it requires fiddling with the global event handler.
Upvotes: 1