Попробуйте заменить эти строки:
random_index = random.randrange(len(enemies))
x = random.randint(0, 5)
Поточная обработка с PyGTK является битом, хитрым, если Вы хотите сделать его правильно. В основном Вы не должны обновлять GUI ни из какого другого потока, чем основной поток (общее ограничение в GUI освобождает). Обычно это сделано в PyGTK с помощью механизма сообщений с очередями (для коммуникации между рабочими и GUI), которые периодически читаются с помощью функции тайм-аута. После того как у меня была презентация моего локального LUG по этой теме, можно захватить пример кода для этой презентации от Репозитория кода Google. Взгляните на MainWindow
класс в forms/frmmain.py
, особенно для метода _pulse()
и в чем выполняют on_entry_activate()
(поток запускается там плюс неактивный таймер, создается).
def on_entry_activate(self, entry):
text = entry.get_text().strip()
if text:
store = entry.get_completion().get_model()
if text not in [row[0] for row in store]:
store.append((text, ))
thread = threads.RecommendationsFetcher(text, self.queue)# <- 1
self.idle_timer = gobject.idle_add(self._pulse)# <- 2
tv_results = self.widgets.get_widget('tv_results')
model = tv_results.get_model()
model.clear()
thread.setDaemon(True)# <- 3
progress_update = self.widgets.get_widget('progress_update')
progress_update.show()
thread.start()# <- 4
Таким образом, обновления приложения GUI, когда "неактивно" (средствами GTK) порождение никаких замораживаний.
Обычно лучше избежать потоков, когда Вы можете. Очень трудно записать потоковое приложение правильно, и еще более трудный знать, что Вы разобрались в нем. Так как Вы пишете приложение GUI, для Вас легче визуализировать, как сделать так, так как уже необходимо записать приложение в асинхронной платформе.
Важная вещь понять состоит в том, что приложение GUI делает многое ни из чего. Это проводит большую часть своего времени, ожидая ОС, чтобы сказать этому, что что-то произошло. Можно сделать много материала в это время простоя, пока Вы знаете, как написать продолжительный код, таким образом, он не блокируется.
Можно решить исходную проблему при помощи тайм-аута; сообщение платформы GUI отозвать некоторую функцию после задержки, и затем сброс той задержки или запуск другого отложенного вызова.
Другой общий вопрос состоит в том, как связаться по сети в приложении GUI. Сетевые приложения похожи на приложения для GUI в этом, они делают большое ожидание. Используя платформу сети IO (как Скрученный) помогает иметь обе части Вашего приложения, ожидают совместно вместо соревновательно, и снова облегчает потребность в дополнительных потоках.
Продолжительные вычисления могут быть записаны многократно вместо синхронно, и можно сделать обработку, в то время как GUI неактивен. Можно использовать генератор, чтобы сделать это довольно легко в Python.
def long_calculation(param, callback):
result = None
while True:
result = calculate_next_part(param, result)
if calculation_is_done(result):
break
else:
yield
callback(result)
Вызов long_calculation
даст Вам объект генератора и вызов .next()
на объекте генератора выполнит генератор, пока он не достигнет также yield
или return
. Вы просто сказали бы платформе GUI звонить long_calculation(some_param, some_callback).next
когда это будет иметь время, и в конечном счете Ваш обратный вызов назовут с результатом.
Я не знаю GTK очень хорошо, таким образом, я не могу сказать Вам, какой gobject функционирует, необходимо звонить. С этим объяснением, тем не менее, необходимо смочь найти необходимые функции в документации, или в худшем случае, спросить относительно соответствующего канала IRC.
К сожалению, нет никакого хорошего ответа общего случая. Если Вы разъясняете с точно, что Вы пытаетесь сделать, было бы легче объяснить, почему Вам не нужны потоки в той ситуации.
Вы не можете перезапустить остановленный объект потока; не пробовать. Вместо этого создайте новый экземпляр объекта, если Вы хотите перезапустить его после того, как его действительно останавливают и присоединяются.
Я не посмотрел подробно на Вашем коде. Но я вижу два решения Вашей проблемы:
Не используйте потоки вообще. Вместо этого используйте тайм-аут, как это:
import gobject
i = 0
def do_print():
global i
print i
i += 1
if i == 10:
main_loop.quit()
return False
return True
main_loop = gobject.MainLoop()
gobject.timeout_add(250, do_print)
main_loop.run()
При использовании потоков необходимо удостовериться, что код GUI только называют от одного потока одновременно путем защиты его как это:
import threading
import time
import gobject
import gtk
gtk.gdk.threads_init()
def run_thread():
for i in xrange(10):
time.sleep(0.25)
gtk.gdk.threads_enter()
# update the view here
gtk.gdk.threads_leave()
gtk.gdk.threads_enter()
main_loop.quit()
gtk.gdk.threads_leave()
t = threading.Thread(target=run_thread)
t.start()
main_loop = gobject.MainLoop()
main_loop.run()
Я играл с различными инструментами, чтобы помочь очистить работу с потоками, неактивную обработку, и т.д.
make_idle является функциональным декоратором, который позволяет Вам выполнять задачу в фоновом режиме совместно. Это - хороший компромисс между чем-то достаточно коротким, чтобы работать однажды в потоке UI и не влиять на скорость отклика приложения, и выполнение полного распараллеливают в специальной синхронизации. В украшенной функции Вы используете "урожай" для возвращения обработки GUI, таким образом, это может остаться быстро реагирующим и в следующий раз, когда UI неактивен, это возьмет в функции, где Вы кончили. Таким образом для запущения этого Вы просто называете idle_add к украшенной функции.
def make_idler(func):
"""
Decorator that makes a generator-function into a function that will
continue execution on next call
"""
a = []
@functools.wraps(func)
def decorated_func(*args, **kwds):
if not a:
a.append(func(*args, **kwds))
try:
a[0].next()
return True
except StopIteration:
del a[:]
return False
return decorated_func
Если необходимо сделать немного больше обработки, можно использовать менеджера по контексту для блокировки потока UI при необходимости, чтобы помочь сделать код немного более безопасным
@contextlib.contextmanager
def gtk_critical_section():
gtk.gdk.threads_enter()
try:
yield
finally:
gtk.gdk.threads_leave()
с этим Вы можете просто
with gtk_critical_section():
... processing ...
Я еще не закончил с ним, но в объединении выполнения вещей просто в неактивном и просто в потоке, у меня есть декоратор (не протестированный все же так не отправленный), что можно сказать это, должен ли следующий раздел после урожая быть выполнен во время простоя UI или в потоке. Это позволило бы делать некоторую установку в потоке UI, переключаться на новый поток для того, чтобы делать фоновый материал и затем переключиться на время простоя UI, чтобы сделать очистку, минимизировав потребность в блокировках.