在 PyGObject 内省中,GTK 在 Python 中的线程处理方式是否有所改变?

27
我正在将一个程序从PyGTK转换为PyGObject内省,第一次遇到线程问题。我的一个进程需要花费一些时间才能完成,所以我弹出一个带有进度条的对话框,并使用一个线程来执行该进程并更新进度条。在PyGTK中,这可以正常运行,但是在转换为PyGObject后,程序会出现所有常见的线程问题:程序会挂起,但似乎是在进程的不同部分挂起等。因此,我认为某些东西已经改变了,但我无法确定是什么。
这里有一个简单的PyGTK进度条示例:http://aruiz.typepad.com/siliconisland/2006/04/threads_on_pygt.html。按照该页面提供的代码可以正常工作。我已将其转换为PyGObject内省,但我得到了与我的程序相同的问题:程序会挂起,无法正确更新进度条等。
import threading
import random, time
from gi.repository import Gtk, Gdk
#Initializing the gtk's thread engine
Gdk.threads_init()


class FractionSetter(threading.Thread):
    """This class sets the fraction of the progressbar"""

    #Thread event, stops the thread if it is set.
    stopthread = threading.Event()

    def run(self):
        """Run method, this is the code that runs while thread is alive."""

        #Importing the progressbar widget from the global scope
        global progressbar 

        #While the stopthread event isn't setted, the thread keeps going on
        while not self.stopthread.isSet() :
            # Acquiring the gtk global mutex
            Gdk.threads_enter()
            #Setting a random value for the fraction
            progressbar.set_fraction(random.random())
            # Releasing the gtk global mutex
            Gdk.threads_leave()

            #Delaying 100ms until the next iteration
            time.sleep(0.1)

    def stop(self):
        """Stop method, sets the event to terminate the thread's main loop"""
        self.stopthread.set()

def main_quit(obj):
    """main_quit function, it stops the thread and the gtk's main loop"""
    #Importing the fs object from the global scope
    global fs
    #Stopping the thread and the gtk's main loop
    fs.stop()
    Gtk.main_quit()

#Gui bootstrap: window and progressbar
window = Gtk.Window()
progressbar = Gtk.ProgressBar()
window.add(progressbar)
window.show_all()
#Connecting the 'destroy' event to the main_quit function
window.connect('destroy', main_quit)

#Creating and starting the thread
fs = FractionSetter()
fs.start()

Gtk.main()

在Gdk的线程能力文档中,强调在运行gdk_threads_init()之前必须先运行g_thread_init(NULL)。但是为了运行它,您需要链接一些额外的库。如果我尝试通过内省导入GLib,然后尝试运行GLib.thread_init(),我会得到以下错误:
>>> from gi.repository import GLib
>>> GLib.thread_init(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/site-packages/gi/types.py", line 44, in function
    return info.invoke(*args)
glib.GError: Could not locate g_thread_init: `g_thread_init': /usr/lib/libglib-2.0.so.0: undefined symbol: g_thread_init

我猜这是因为额外的线程库没有链接。如果这是我线程问题的原因,那么我该如何使用GLib并像链接了那些库一样工作呢?

1
你应该在调用Gtk.main()时使用Gdk.threads_enter()Gdk.threads_leave()进行包装,不是吗?我看到的所有C代码示例都是这样做的。例如:http://blogs.operationaldynamics.com/andrew/software/gnome-desktop/gtk-thread-awareness - Marius Gedminas
1个回答

27

我通过查看一些用Python编写的Gnome程序(例如Gnome数独),最终解决了自己的问题,这个程序在某些情况下确实帮助了我。

诀窍在于,在您的代码开头调用GObject.threads_init()而不是C文档所示的GLib.thread_init()


4
我只是想说谢谢。伙计,处理这些东西90%的时间都在用谷歌搜索。 - miracle2k
1
非常感谢!我遇到了这个确切的问题,但不知道哪里出错了。我希望PyGObject有一些具体的文档,而不仅仅是“查看C文档”。 - alldayremix
3
这是对该回答的现代更新:GObject.threads_init()和GLib.threads_init()是同义词,在PyGObject v3.10.2及以上版本中实际上都不需要。详情请见:https://wiki.gnome.org/Projects/PyGObject/Threading - Simon Feltman
2
@SimonFeltman 另外,在现代GTK中,人们会完全放弃使用Gdk.threads_enter()Gdk.threads_leave(),而是使用像idle_add(lambda: progressbar.set_fraction(random.random()))这样的调用来在实际运行主循环的线程中运行所有GUI代码。其他任何操作都会导致崩溃和头痛。 - user4815162342

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接