27

I'm in the process of converting a program from PyGTK to PyGObject introspection for the first time and I've hit a roadblock with threading. I have a process that takes some time to complete, so I pop up a dialog with a progress bar on it and I use a thread to do the process and to update the progress bar. This worked fine with PyGTK but after converting to PyGObject, I get all the usual improper threading weirdness: the program hangs, but it seems to hang in different parts of the process, etc. So I get the impression that something has changed but I can't figure out what.

Here's this simple PyGTK progressbar example: http://aruiz.typepad.com/siliconisland/2006/04/threads_on_pygt.html As presented on that page, the code works. I've converted it to PyGObject introspection and I get the same problems as in my program: it hangs, it doesn't properly update the progress bar, etc.

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

In the documentation for Gdk's threading capabilities, it stresses that you first must run g_thread_init(NULL) before running gdk_threads_init(). But to run that, you need to link in some extra libraries. If I try to import GLib through introspection and then I try to run GLib.thread_init(), I get the following error:

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

I assume this is because the extra threading libraries weren't linked. If this is the cause of my threading problems, how can I work with GLib as if those libraries have been linked?

1

1 Answer 1

27

I managed to answer my own question by poking through some Gnome programs written in Python (Gnome Sudoku, in this case, which actually has helped me a couple of times).

The trick is that you have to call GObject.threads_init() at the beginning of your code, not GLib.thread_init() as the C documentation implies.

Sign up to request clarification or add additional context in comments.

4 Comments

I just want to say thanks. Man, working with this stuff is 90% Googling.
Thanks so much! I was having this exact problem and had no clue where I was going wrong. I wish PyGObject had some specific documentation instead of just "check the C docs"
A modern update to this answer: GObject.threads_init() and GLib.threads_init() are synonymous and neither are actually needed in PyGObject v3.10.2 and up. See also: wiki.gnome.org/Projects/PyGObject/Threading
@SimonFeltman Also, in modern GTK, one would forego the use of Gdk.threads_enter() and Gdk.threads_leave() altogether, and use calls like idle_add(lambda: progressbar.set_fraction(random.random())) to run all GUI code in the thread that actually runs the main loop. Anything else leads to crashes and headaches.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.