PyGtk:运行 gtk.main() 在单独的线程中循环

PyGtk: Run gtk.main() loop in a seperate thread

下面的代码在声明 og gtkLoopTHread 本身之后被击中,即使我没有给出 gtkMainLoop.start() 函数调用。 我想在后台 运行 计时器并在 UI 中做一些事情,并在计时器达到“0”后销毁 UI。

代码

def createCountDown(maxSec):
  while maxSec > 0:
    print maxSec;
    maxSec = maxSec - 1;

  gtk.main_quit();
  return;

maxSec = 5;
gtkLoopThread = threading.Thread(group=None, target=gtk.main, name=None, args=(), kwargs={});
print "After gtkThread declaration"
myThread = threading.Thread(group=None, target=createCountDown, name=None, args=(maxSec), kwargs={});
gtkLoopThread.start();
myThread.start();

预期输出:

After gtkThread declaration
5
4
3
2
1

当前行为: 由于 gtkLoopThread 在初始化 gtkLoopThread 变量

后立即启动,所以看不到行 'After gtkThread declaration'

有几个问题会阻止您的代码 运行 正确。

首先,您在程序开头缺少对 gobject.threads_init()(在 import gobject 之后)的调用。此调用导致 PyGTK 释放 GIL when entering the main loop. Failure to call it will make threads useless. (In the latest PyGObject with Python 3, this is no longer necessary.)

第二个问题,也是一个微不足道的问题,是您错误地构造了一个单元素元组用作 threading.Thread 构造函数的 args 参数 - 它应该是 args=(maxSec,),不是 args=(maxSec).

经过以上两处修改后,您的程序显示了预期的输出。

最后, and the documentation confirms,不允许从运行主循环的线程以外的任何线程调用GTK代码。工作线程可以使用 gobject.idle_add 来安排要在 GUI 线程中执行的代码,而不是调用 GTK 函数。 (如果 GUI 线程空闲,这应该是空闲的,这会立即发生。)换句话说,您应该将 gtk.main_quit() 替换为 gobject.idle_add(gtk.main_quit)。如果 or 函数接受参数,请改用 gobject.idle_add(lambda: gtk_call_here(...)。虽然在 GTK 程序中创建多个工作线程是完全合法的,但它们必须避免直接调用 GTK。

此外,根据文档,运行主循环的线程应该与初始化 GTK 的线程相同。因此,一个正确的程序不应该在 GUI 线程启动之前 import gtk(初始化它)。这是实现此功能的代码版本:

import threading

# Event marking the GUI thread as having imported GTK.  GTK must not
# be imported before this event's flag is set.
gui_ready = threading.Event()

def run_gui_thread():
    import gobject
    gobject.threads_init()
    import gtk
    gui_ready.set()
    gtk.main()

gui_thread = threading.Thread(target=run_gui_thread)
gui_thread.start()

# wait for the GUI thread to initialize GTK
gui_ready.wait()

# it is now safe to import GTK-related stuff
import gobject, gtk

def countdown(maxSec):
    while maxSec > 0:
        print maxSec
        maxSec -= 1

    gobject.idle_add(gtk.main_quit)

worker = threading.Thread(target=countdown, args=(5,))
print 'starting work...'
worker.start()

# When both the worker and gui_thread finish, the main thread
# will exit as well.