2016-02-29 2 views
0

Ниже код попадает после объявления og gtkLoopTHread, даже если я не вызываю вызов функции gtkMainLoop.start(). Я хочу запустить таймер в фоновом режиме и сделать некоторые вещи в этом пользовательском интерфейсе и уничтожить пользовательский интерфейс, как только таймер достигнет «0».PyGtk: Запустить цикл gtk.main() в отдельной нити

Код

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 

Текущее поведение: Line 'После gtkThread декларации' печать не видел с gtkLoopThread начинается сразу после инициализации gtkLoopThread variabl e

+0

Нет, вы не можете запустить 'gtk.main()' в другом потоке из вашего другого кода GTK +. Вам нужно будет запустить другой материал в отдельном потоке. Вы также не можете запустить 'gtk.main_quit()' в отдельном потоке; вам нужно будет использовать 'glib.idle_add()' для очереди на функцию, которая будет запускаться в потоке GTK +. Сожалею. – andlabs

+0

Если вам нужен только таймер, вы также можете использовать 'glib.timeout_add()'. – elya5

ответ

3

Существует несколько проблем, препятствующих правильному запуску вашего кода.

Прежде всего, вам не позвоните в gobject.threads_init() (после import gobject) в начале вашей программы. Этот вызов заставляет PyGTK освобождать GIL при входе в основной цикл. Неспособность вызвать это сделает потоки бесполезными. (В последнем PyGObject с Python 3, это no longer necessary.)

Вторая проблема, и это одна тривиальна, является то, что вы неправильно строит один-элементный кортеж для использования в качестве args аргумента threading.Thread конструктора - это должен быть args=(maxSec,), а не args=(maxSec).

После этих двух изменений ваша программа отображает ожидаемый результат.

Наконец, as andlabs points out и documentation confirms, не разрешено вызывать код GTK из любого потока, кроме потока, который запускает основной цикл. Вместо вызова функций GTK рабочие потоки могут использовать gobject.idle_add для планирования кода, который будет выполняться в потоке графического интерфейса. (Если поток GUI простаивает, как и должно быть, это произойдет быстро). Другими словами, вы должны заменить gtk.main_quit() на gobject.idle_add(gtk.main_quit). Если функция или принимает аргументы, вместо этого используйте gobject.idle_add(lambda: gtk_call_here(...). Хотя совершенно правильно создавать несколько рабочих потоков в программе GTK, они должны избегать прямых вызовов в GTK.

Кроме того, согласно документации, поток, который запускает основной цикл, должен быть тем же самым потоком, который инициализирует GTK. Таким образом, правильная программа не должна 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. 
Смежные вопросы