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

Ash*_*win 3 gtk multithreading pygtk python-2.7

即使我没有给出 gtkMainLoop.start() 函数调用,下面的代码也会在声明 og gtkLoopTHread 本身后受到攻击。我想在后台运行计时器,并在该 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();
Run Code Online (Sandbox Code Playgroud)

预期输出:

After gtkThread declaration
5
4
3
2
1
Run Code Online (Sandbox Code Playgroud)

当前行为: 行“在 gtkThread 声明之后”没有看到打印,因为 gtkLoopThread 在初始化 gtkLoopThread 变量后立即启动

use*_*342 5

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

首先也是最重要的,您错过了程序开头对gobject.threads_init()(after ) 的调用。此调用会导致 PyGTK在进入主循环时import gobject释放GIL 。不调用它将使线程变得无用。(在最新的 PyGObject with Python 3 中,这不再是必要的。

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

经过上述两项更改后,您的程序将显示预期的输出。

最后,正如 andlabs 指出的文档确认的那样,不允许从运行主循环的线程以外的任何线程调用 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 的线程相同。因此,正确的程序不应该import gtk在 GUI 线程启动之前(对其进行初始化)。这是实现此功能的代码版本:

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.
Run Code Online (Sandbox Code Playgroud)