0

我需要检查在执行某些非 GUI 代码期间是否按下了 Escape 键。(代码在 Python 中,但必要时可以轻松调用 C。)代码从 GUI 接收一个函数,它偶尔调用该函数来检查它是否已被中断。问题是如何实施这项检查。

通过查看文档,gdk_event_peek这似乎是一个很好的选择:

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'

但是,这不起作用:gtk.gdk.event_peek()当主循环未运行时,从返回的事件始终为 None 。将其更改为gtk.gdk.display_get_default().peek_event()也无济于事。我假设这些事件在 X 事件队列中并且尚未移动到 GDK 事件队列中。文档说:

请注意,此函数不会从窗口系统获取更多事件。它只检查已经移动到 GDK 事件队列的事件。

那么,如何将事件转移到 GDK 事件队列中呢?换句话说,什么gtk.gdk.peek_event()时候返回一个事件?调用gtk.events_pending()没有任何效果。

这是一个测试它的最小程序:

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()

运行代码时,打印的事件始终为 None,即使您按下 Escape 或移动鼠标。

我还考虑过为 Escape 安装一个处理程序,并让检查器使用while gtk.events_pending(): gtk.main_iteration()成语处理事件。这将导致所有待处理事件(包括键盘和鼠标事件)的出队和分派。效果是在代码运行时 GUI 启用了响应式,这看起来不太好,并且会严重干扰代码的执行。执行期间处理的唯一事件应该是用于中断它的转义键。

4

1 回答 1

1

我想出了一个runner满足问题中提出的标准的实现:

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()

与基于 peek 的解决方案相比,它的优势在于它可以处理按键后另一个事件到达的情况。缺点是它需要摆弄全局事件处理程序。

于 2012-10-28T14:32:39.863 回答