Изменилась ли многопоточность в GTK с Python в самоанализе PyGObject?

Я впервые нахожусь в процессе преобразования программы из PyGTK в интроспекцию PyGObject и столкнулся с проблемой многопоточности. У меня есть процесс, для завершения которого требуется некоторое время, поэтому я открываю диалоговое окно с индикатором выполнения и использую поток для выполнения процесса и обновления индикатора выполнения. Это отлично работало с PyGTK, но после преобразования в PyGObject я получаю все обычные странности неправильной работы с потоками: программа зависает, но кажется, что она зависает в разных частях процесса и т. д. Таким образом, у меня создается впечатление, что что-то изменилось, но я могу не пойму что.

Вот этот простой пример индикатора прогресса PyGTK: 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 подчеркивается, что вы должны сначала запустить g_thread_init(NULL) перед запуском gdk_threads_init(). Но для этого вам нужно связать некоторые дополнительные библиотеки. Если я попытаюсь импортировать 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, как если бы эти библиотеки были связаны?


person Community    schedule 04.08.2011    source источник
comment
Разве вы не должны заключать вызов Gtk.main() в Gdk.threads_enter() и Gdk.threads_leave()? Все примеры кода C, которые я видел, делают это. Например: blogs.operationaldynamics.com/andrew/software/gnome- рабочий стол/   -  person Marius Gedminas    schedule 17.12.2013


Ответы (1)


Мне удалось ответить на мой собственный вопрос, покопавшись в некоторых программах Gnome, написанных на Python (в данном случае Gnome Sudoku, что на самом деле помогло мне пару раз).

Хитрость в том, что вы должны вызывать GObject.threads_init() в начале вашего кода, а не GLib.thread_init(), как подразумевает документация C.

person Community    schedule 04.08.2011
comment
Я просто хочу сказать спасибо. Чувак, работа с этим материалом на 90% состоит из гугления. - person miracle2k; 06.08.2011
comment
Большое спасибо! У меня была именно эта проблема, и я понятия не имел, где я ошибаюсь. Я бы хотел, чтобы у PyGObject была какая-то конкретная документация, а не просто проверка документов C - person alldayremix; 11.05.2013
comment
Современное обновление этого ответа: GObject.threads_init() и GLib.threads_init() являются синонимами, и ни один из них на самом деле не нужен в PyGObject v3.10.2 и выше. См. также: wiki.gnome.org/Projects/PyGObject/Threading - person Simon Feltman; 08.07.2014
comment
@SimonFeltman Кроме того, в современном GTK можно вообще отказаться от использования Gdk.threads_enter() и Gdk.threads_leave() и использовать такие вызовы, как idle_add(lambda: progressbar.set_fraction(random.random())), для запуска всего кода графического интерфейса в потоке, который фактически запускает основной цикл. Все остальное приводит к сбоям и головным болям. - person user4815162342; 14.02.2016