Многопоточность с Pygame - PullRequest
       11

Многопоточность с Pygame

3 голосов
/ 22 августа 2011

У меня возникли проблемы при написании простого приложения Pygame, использующего потоки.Помните, что это первый многопоточный код, который я когда-либо написал.

Вот ситуация.Я пишу простое приложение, которое нарисует на экране несколько прикольных линий.Моя проблема в том, что пока я рисую линии, приложение не может обрабатывать ввод, поэтому я не могу (например) закрыть окно, пока линии не будут закончены.Вот как выглядел мой исходный код:

import time
import pygame
from pygame.locals import *

SIZE = 800

def main():
    screen = pygame.display.set_mode((SIZE, SIZE))
    for interval in xrange(50, 1, -5):
        screen.fill((0, 0, 0))
        for i in xrange(0, SIZE, interval):
            pygame.draw.aaline(screen, (255, 255, 255), (i+interval, 0), (0, SIZE-i))
            pygame.draw.aaline(screen, (255, 255, 255), (i, 0), (SIZE, i+interval))
            pygame.draw.aaline(screen, (255, 255, 255), (SIZE, i), (SIZE-i-interval, SIZE))
            pygame.draw.aaline(screen, (255, 255, 255), (SIZE-i, SIZE), (0, SIZE-i-interval))
            pygame.display.update()
            time.sleep(0.03)
        time.sleep(3)
    while True:
        for evt in pygame.event.get():
            if evt.type == QUIT:
                return

if __name__ == '__main__':
    pygame.init()
    main()
    pygame.quit()

Как вы можете видеть, цикл обработки событий запускается только после завершения рисования, поэтому до тех пор кнопка закрытия окна не реагировала.Я подумал, что помещение кода рисования в собственный поток может помочь, поэтому я изменил код так:

import threading, time
import pygame
from pygame.locals import *

SIZE = 800

def draw():
    screen = pygame.display.set_mode((SIZE, SIZE))
    for interval in xrange(50, 1, -5):
        screen.fill((0, 0, 0))
        for i in xrange(0, SIZE, interval):
            pygame.draw.aaline(screen, (255, 255, 255), (i+interval, 0), (0, SIZE-i))
            pygame.draw.aaline(screen, (255, 255, 255), (i, 0), (SIZE, i+interval))
            pygame.draw.aaline(screen, (255, 255, 255), (SIZE, i), (SIZE-i-interval, SIZE))
            pygame.draw.aaline(screen, (255, 255, 255), (SIZE-i, SIZE), (0, SIZE-i-interval))
            pygame.display.update()
            time.sleep(0.03)
        time.sleep(3)

def main():
    threading.Thread(target=draw).start()
    while True:
        for evt in pygame.event.get():
            if evt.type == QUIT:
                return

if __name__ == '__main__':
    pygame.init()
    main()
    pygame.quit()

Но все, что я получаю, это черный экран, который также не реагирует на ввод.Что я тут не так делаю?

1 Ответ

9 голосов
/ 22 августа 2011

Хотя я никогда не использовал pygame, я сомневаюсь, что вы можете (или должны) вызывать его API из разных потоков. Все ваше рисование должно быть сделано в главном цикле событий.

Полагаю, вам нужно изменить способ мышления при разработке игр. Вместо использования time.sleep() для приостановки рисования создайте объект, который можно обновлять через равные промежутки времени. Как правило, это делается в два прохода - update() для улучшения состояния объекта во времени и draw() для визуализации текущего состояния объекта. На каждой итерации вашего основного цикла все объекты обновляются, а затем все отрисовываются. Обратите внимание, что draw() должен предполагать, что на экране ничего нет, и рисовать каждую нужную строку до текущего времени.

В вашем простом случае вы могли бы избежать неприятностей с чем-то более простым. Замените time.sleep() в вашей функции draw() на yield. Таким образом, вы получите итератор, который даст рекомендуемое количество времени для ожидания следующей итерации. Перед основным циклом создайте итератор, вызвав draw() и инициализируя время, когда должно произойти следующее рисование:

draw_iterator = draw()
next_draw_time = 0  # Draw immediately

Затем в главном цикле, после обработки пользовательского ввода, проверьте, пора ли рисовать:

current_time = time.time()
if current_time >= next_draw_time:

Если это так, запустите следующую итерацию и запланируйте следующее время отрисовки:

    try:
        timeout = next(draw_iterator)
    except StopIteration:
        # The drawing is finished, exit the main loop?
        break
    next_draw_time = current_time + timeout
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...