pyGame in a thread
I want to use a pyGame program as a part of another process. Using the following code, pyGame doesn't seem to be processing events; it doesn't respond to the 'q' key nor does it draw the titlebar for the window. If go()
is not run as a thread, it works fine. This is under OSX; I'm unsure if that's the problem or not.
import pygame, threading, random
def go():
pygame.init()
surf = pygame.display.set_mode((640,480))
pygame.fastevent.init()
while True:
e = pygame.fastevent.poll()
if e.type == pygame.KEYDOWN and e.unicode == 'q':
return
surf.set_at((random.randint(0,6开发者_开发百科40), random.randint(0,480)), (255,255,255))
pygame.display.flip()
t = threading.Thread(target=go)
t.start()
t.join()
Possibly a long shot, but try making sure you don't import Pygame until you're in the thread. It's just about possible that the problem is that you're importing Pygame on one thread and then using it on another. However, importing on multiple threads can have other issues. In particular, make sure that once you start the Pygame thread you wait for it to finish importing before you do anything that might cause the Python process to shut-down, or you might get a deadlock.
Pygame is not threadsafe and the eventloop is required to run on the main thread! Otherwise, the problem you describe can occur.
One solution is to call pygame.mainloop()
from the main thread.
However,maybe you are using other modules that also require running from the main thread. There is in this case one pythonic solution. you have the possibility to run pygame mainloop with an argument. This argument means: run the mainloop for only a few seconds. Hence what you can do is create a generator that runs mainloop for a 0.1 second that you call periodically from main thread. For example:
def continue_pygame_loop():
pygame.mainloop(0.1)
yield
then just call continue_pygame_loop()
periodically from main thread
Tkinter suffers from the same problem, but has no way to specify runloop()
with a timeout. For me, this is why pygame is great!
It's best to do the event handling and graphics in the main thread. Some environments really don't like you trying to render from other threads, and some don't like you trying to drain the event queue from them either.
It may not even be possible to do what you're hoping to do, since the process you're running within may well have its own ideas about who owns the message queue and the window you're rendering to.
Not really an answer, just an affirmation of the fact that this problem does indeed occur on Mac OS X and not on Linux. I developed my program on Ubuntu and this sort of code worked fine there, but it failed as you described when I tried to run it on a Mac. Running the drawing code in the main thread instead works fine.
If this is really a limitation of either the threading module or pygame (on Mac), then I guess the only way is to restructure your program so that all drawing is handled by the main thread. Alternatively, file a bug report and see what happens.
EDIT: Another, much better, option would be to use the multiprocessing
module. You could spawn a separate python process just for rendering to screen. Processes can then communicate information to each other. I'm looking into using this myself.
This was solved for me by calling pygame.init() in the thread being started rather than at the top. For example my class which initializes a thread and defines a function which will be the main loop:
class Thing():
# other class stuff
def __init__(self):
# other init stuff
self.thread = threading.Thread(target=self.gogo)
self.thread.start()
def gogo(self):
print('gogo')
pg.init() # <-THIS!
self.screen = pg.display.set_mode(SIZE)
while self.get_working():
for event in pg.event.get():
if event.type == pg.QUIT:
# etc
Before, the screen and init were done at the start, it completely worked on Ubuntu 18.04, just not on a window 10 machine.
精彩评论