7

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,640), random.randint(0,480)), (255,255,255))
    pygame.display.flip()

t = threading.Thread(target=go)
t.start()
t.join()
4
  • Your code runs fine on my (linux) machine. Commented Jun 4, 2010 at 17:12
  • 1
    Also runs on my linux machine, seem to be OSX specific problem. Commented Aug 28, 2011 at 7:33
  • 1
    FYI, Python has a GIL (global interpreter lock) that prevents true parallelism with threads (i.e., if you use threads, it WILL NOT make your code faster). Commented Feb 29, 2012 at 5:52
  • Having the same problem on a Mac where the drawing doesn't happen if it isn't done in the main thread. Not really a solution and rather more of an observation is that if the display.flip() is called in the main thread while the drawing is done in the other the screen will update as properly required. Commented Jul 29, 2015 at 3:33

5 Answers 5

8

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!

Sign up to request clarification or add additional context in comments.

1 Comment

As far as I know and can find out, pygame has no mainloop() function. (I'm using v1.9.2), so it unclear what you're talking about. As you confusing it with tkinter (which does)?
6

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.

1 Comment

That certainly worked for me on Windows with Python 3.3.5
5

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.

Comments

4

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.

Comments

1

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.

1 Comment

This worked for me! Not sure about possible side effects of using it in a thread together with another application, but in my cases it is a very simple application (a splash screen), so it seems to do the trick. Thanks, @bobbins

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.