Hi everyone and thank you in advance for your help. I've just discovered pygame (a library for python) and I wanted to play a bit with it but I'm facing a problem. I tried to use threads in my code but the program keeps crashing each time I launch it.
I have isolated the problem and I know that it is thread_1 that causes the crash because when I comment it out everything works again. I have tried to change the code of the function of thread_1, but it still crashes. I'm pretty sure it's not the content of the function animateTitle that causes the crash but the way I'm using threads.
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()
Threading causes many problems and a general rule of thumb is to avoid them if they aren't necessary. They make your program non-deterministic, harder to debug, harder to test, harder to maintain and slower (most of the time). In your program, there are no reasons to use threads. Instead, you should do things sequentially. Pygame will implicitly create threads whenever necessary (for example when dealing with pygame.mixer)
Why it doesn't work, is because pygame expects all event handling to happen in the thread that set the video mode (pygame is using SDL2, hence that link). You cannot handle them in another thread, and as you don't handle them (correctly), the operating system will think your program has crashed.
I made an example to show one way you could do the animation. The concept is that you tell pygame that after a certain amount of time, you want an event to be posted. When that event appears in your event loop, you do something.
In your case, you tell pygame to post DRAW_TEXT_EVENT after 500 ms. When this event appears in your event loop, you first tell pygame to don't post DRAW_TEXT_EVENT anymore, but to post CLEAR_TEXT_EVENT after 1000ms. Then you draw the text.
After 1000ms, the CLEAR_TEXT_EVENT will appear in your event loop. Now you do basically the same thing, but disable CLEAR_TEXT_EVENT and tell pygame to post DRAW_TEXT_EVENT after 500ms.
I haven't changed much in your code. I added 2 definitions for the events at the top. I've removed your functions loop and animateTitle, and put them into the game loop. Lastly, I've added the game loop in the main function.
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With