MultiThreading с Pygame, сбой программы - PullRequest
0 голосов
/ 01 февраля 2019

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

Я выделил проблему, и я знаю, что именно thread_1 вызывает сбой, потому что, когда я его комментирую, все снова работает.Я попытался изменить код функции thread_1, но все равно вылетает.Я почти уверен, что не содержимое функции animateTitle вызывает сбой, а то, как я использую потоки.

import pygame

from pygame.locals import *

from threading import Thread


def encadre(screen):  
    pygame.draw.line(screen, (250, 250, 250), (230, 140), (520, 140), 3)
    pygame.draw.line(screen, (250, 250, 250), (230, 190), (520, 190), 3)
    pygame.draw.line(screen, (250, 250, 250), (230, 140), (230, 190), 3)
    pygame.draw.line(screen, (250, 250, 250), (520, 140), (520, 190), 3)


def initRoad(screen):  
    pygame.draw.line(screen, (250, 250, 250), (30, 0), (30, 500))
    pygame.draw.line(screen, (250, 250, 250), (100, 0), (100, 500))
    pygame.draw.line(screen, (250, 250, 250), (650, 0), (650, 500))
    pygame.draw.line(screen, (250, 250, 250), (720, 0), (720, 500))
    drawLines(screen)


def drawLines(screen): 
    i = 0
    while i <= 49:
        pygame.draw.line(screen, (250, 250, 250), (65, i * 10), (65, (i + 1) * 10))
        pygame.draw.line(screen, (250, 250, 250), (685, i * 10), (685, (i + 1) * 10))
        i = i + 3


def initText(screen, text1):  
    text1pos = text1.get_rect()
    text1pos.x = 235
    text1pos.y = 150
    screen.blit(text1, text1pos)

    font1 = pygame.font.Font(None, 30)
    text1 = font1.render("PLAY", 1, (10, 10, 10))
    text1pos = text1.get_rect()
    text1pos.x = 210
    text1pos.y = 310
    screen.blit(text1, text1pos)

    font1 = pygame.font.Font(None, 30)
    text1 = font1.render("QUIT", 1, (10, 10, 10))
    text1pos = text1.get_rect()
    text1pos.x = 490
    text1pos.y = 310
    screen.blit(text1, text1pos)


def animateRoad(screen):  # not done
    pygame.draw.line(screen, (130, 130, 130), (65, 0), (65, 500))
    pygame.draw.line(screen, (130, 130, 130), (685, 0), (685, 500))


def animateTitle(screen, text1): 
    text1pos = text1.get_rect()
    while True:
        pygame.draw.rect(screen, (130, 130, 130), (235, 150, 283, 35))
        pygame.display.flip()
        pygame.time.wait(500)
        text1pos.x = 235
        text1pos.y = 150
        screen.blit(text1, text1pos)
        pygame.display.flip()
        pygame.time.wait(1000)


def loop(surface1, surface2):
    while True:
        for event in pygame.event.get():

            if event.type == QUIT:
                return

            if event.type == pygame.MOUSEBUTTONDOWN:
                if surface1.topleft[0] <= pygame.mouse.get_pos()[0] <= surface1.topright[0]:
                    if surface1.topleft[1] <= pygame.mouse.get_pos()[1] <= surface1.bottomleft[1]:
                    print('play')

                if surface2.topleft[0] <= pygame.mouse.get_pos()[0] <= surface2.topright[0]:
                    if surface2.topleft[1] <= pygame.mouse.get_pos()[1] <= surface2.bottomleft[1]:
                    return

    pygame.display.flip()
    pygame.time.wait(10)


def main():
    pygame.init()
    screen = pygame.display.set_mode((750, 500))
    pygame.display.set_caption('Infinite circle run')

    background = pygame.Surface(screen.get_size())  
    background = background.convert()
    background.fill((130, 130, 130))
    screen.blit(background, (0, 0))

    encadre(screen)
    initRoad(screen)

    surface1 = pygame.Rect(193, 290, 85, 50) 
    button1 = pygame.draw.rect(screen, (0, 0, 240), surface1)
    surface2 = pygame.Rect(472, 290, 85, 50)  
    button2 = pygame.draw.rect(screen, (240, 0, 0), surface2)

    font1 = pygame.font.Font(None, 50)
    text1 = font1.render("Infinite circle run", 1, (0, 240, 0))
    initText(screen, text1)  

    pygame.display.flip()

    thread_1 = Thread(target=animateTitle(screen, text1), daemon=True)
    thread_2 = Thread(target=loop(surface1, surface2))
    # thread_3 = Thread(target=animateRoad(screen))

    thread_1.start()
    thread_2.start()
    # thread_3.start()


if __name__ == '__main__':
    main()

1 Ответ

0 голосов
/ 01 февраля 2019

Многопоточность вызывает много проблем, и общее правило - избегать их, если они не нужны.Они делают вашу программу недетерминированной, труднее отлаживать, труднее тестировать, труднее поддерживать и медленнее (большую часть времени).В вашей программе нет причин использовать потоки.Вместо этого вы должны делать вещи последовательно.Pygame будет неявно создавать потоки всякий раз, когда это необходимо (например, при работе с pygame.mixer)

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


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

В вашем случае вы указываете pygame публиковать DRAW_TEXT_EVENT через 500 мс.Когда это событие появляется в вашем цикле событий, вы сначала указываете pygame больше не публиковать DRAW_TEXT_EVENT, а публиковать CLEAR_TEXT_EVENT через 1000 мс.Затем вы рисуете текст.

Через 1000 мсек CLEAR_TEXT_EVENT появится в вашем цикле событий.Теперь вы делаете в основном то же самое, но отключаете CLEAR_TEXT_EVENT и говорите pygame публиковать DRAW_TEXT_EVENT через 500 мс.

Я не сильно изменился в вашем коде.Я добавил 2 определения событий вверху.Я удалил ваши функции loop и animateTitle и поместил их в игровой цикл.Наконец, я добавил игровой цикл в функцию main.

import pygame
from pygame.locals import *

# Events that we're going to post.
DRAW_TEXT_EVENT  = pygame.USEREVENT + 1
CLEAR_TEXT_EVENT = pygame.USEREVENT + 2


def encadre(screen):
    pygame.draw.line(screen, (250, 250, 250), (230, 140), (520, 140), 3)
    pygame.draw.line(screen, (250, 250, 250), (230, 190), (520, 190), 3)
    pygame.draw.line(screen, (250, 250, 250), (230, 140), (230, 190), 3)
    pygame.draw.line(screen, (250, 250, 250), (520, 140), (520, 190), 3)


def initRoad(screen):
    pygame.draw.line(screen, (250, 250, 250), (30, 0), (30, 500))
    pygame.draw.line(screen, (250, 250, 250), (100, 0), (100, 500))
    pygame.draw.line(screen, (250, 250, 250), (650, 0), (650, 500))
    pygame.draw.line(screen, (250, 250, 250), (720, 0), (720, 500))
    drawLines(screen)


def drawLines(screen):
    i = 0
    while i <= 49:
        pygame.draw.line(screen, (250, 250, 250), (65, i * 10), (65, (i + 1) * 10))
        pygame.draw.line(screen, (250, 250, 250), (685, i * 10), (685, (i + 1) * 10))
        i = i + 3


def initText(screen, text1):
    text1pos = text1.get_rect()
    text1pos.x = 235
    text1pos.y = 150
    screen.blit(text1, text1pos)

    font1 = pygame.font.Font(None, 30)
    text1 = font1.render("PLAY", 1, (10, 10, 10))
    text1pos = text1.get_rect()
    text1pos.x = 210
    text1pos.y = 310
    screen.blit(text1, text1pos)

    font1 = pygame.font.Font(None, 30)
    text1 = font1.render("QUIT", 1, (10, 10, 10))
    text1pos = text1.get_rect()
    text1pos.x = 490
    text1pos.y = 310
    screen.blit(text1, text1pos)


def animateRoad(screen):  # not done
    pygame.draw.line(screen, (130, 130, 130), (65, 0), (65, 500))
    pygame.draw.line(screen, (130, 130, 130), (685, 0), (685, 500))



def main():
    pygame.init()
    screen = pygame.display.set_mode((750, 500))
    pygame.display.set_caption('Infinite circle run')

    background = pygame.Surface(screen.get_size())
    background = background.convert()
    background.fill((130, 130, 130))
    screen.blit(background, (0, 0))

    encadre(screen)
    initRoad(screen)

    surface1 = pygame.Rect(193, 290, 85, 50)
    button1 = pygame.draw.rect(screen, (0, 0, 240), surface1)
    surface2 = pygame.Rect(472, 290, 85, 50)
    button2 = pygame.draw.rect(screen, (240, 0, 0), surface2)

    font1 = pygame.font.Font(None, 50)
    text1 = font1.render("Infinite circle run", 1, (0, 240, 0))
    initText(screen, text1)

    pygame.display.flip()

    pygame.time.set_timer(DRAW_TEXT_EVENT, 500)

    text1pos = text1.get_rect()

    # GAME LOOP
    while True:

        for event in pygame.event.get():
            if event.type == QUIT:
                return
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if surface1.topleft[0] <= pygame.mouse.get_pos()[0] <= surface1.topright[0]:
                    if surface1.topleft[1] <= pygame.mouse.get_pos()[1] <= surface1.bottomleft[1]:
                        print('play')
                elif surface2.topleft[0] <= pygame.mouse.get_pos()[0] <= surface2.topright[0]:
                    if surface2.topleft[1] <= pygame.mouse.get_pos()[1] <= surface2.bottomleft[1]:
                        return

            elif event.type == DRAW_TEXT_EVENT:
                pygame.draw.rect(screen, (130, 130, 130), (235, 150, 283, 35))
                pygame.time.set_timer(DRAW_TEXT_EVENT,  0)     # Disable the event.
                pygame.time.set_timer(CLEAR_TEXT_EVENT, 1000)  # Post event after 1000ms.
            elif event.type == CLEAR_TEXT_EVENT:
                text1pos.x = 235
                text1pos.y = 150
                screen.blit(text1, text1pos)
                pygame.time.set_timer(CLEAR_TEXT_EVENT, 0)    # Disable the event.
                pygame.time.set_timer(DRAW_TEXT_EVENT,  500)  # Post event after 500ms.

        # Only call once each frame!
        pygame.display.flip()


if __name__ == '__main__':
    main()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...