pygame requires keyboard interrupt to init display

2019-04-29 18:06发布

问题:

I'm trying to initialize pygame on a Raspberry Pi and its requiring a keyboard interrupt before it will do anything. Here's my code:

    os.putenv ( "SDL_VIDEODRIVER" , "fbcon" )
    pygame.display.init()    # It hangs here
    screen = pygame.display.set_mode ( ( 1024 , 768 ) )

    pygame.draw.rect ( screen , ( 0 , 255 , 0 ) , ( 15 , 15 , 15 , 15 ) )
    pygame.display.flip()

    keyLoop = True
    while keyLoop:
       for event in pygame.event.get():
          if event.type == pygame.KEYDOWN:
             if event.key == pygame.K_DOWN:
                print ( "Down arrow pressed, exiting" )
                keyLoop = False
                pygame.quit()

I found a similar question here Python program won't respond until keyboard interrupt but it wouldn't let me add a comment and I've tried all of their suggestions and still have the problem. If I press CTRL + C then my graphics appear but then the keyboard doesn't work.

Thanks

EDIT

I got it to work by removing os.putenv completely. The problem was actually in the Pi setup in config.txt. I was trying to init a pygame display bigger than the Pi's framebuffer. Making sure the two match (framebuffer and display.set_mode) made it start up just fine.

回答1:

I had exactly the same problem but it would only happen on second running of my pygame code (i.e. first run on boot, everything is OK but if you stop the program and then try to restart it there is then a hang at pygame.init or pygame.display.set_mode).

The solution from someone-or-other did not work for me (the raising of the keyboard interrupt did not seem to hit the main thread, just the forked thread) but thanks for the idea! A resulting working code fragment is as follows.

from signal import alarm, signal, SIGALRM, SIGKILL

def init_Pygame():

    # this section is an unbelievable nasty hack - for some reason Pygame
    # needs a keyboardinterrupt to initialise in some limited circs (second time running)
    class Alarm(Exception):
        pass
    def alarm_handler(signum, frame):
        raise Alarm
    signal(SIGALRM, alarm_handler)
    alarm(3)
    try:
        pygame.init()
        DISPLAYSURFACE = pygame.display.set_mode((DISPLAYWIDTH, DISPLAYHEIGHT)) 
        alarm(0)
    except Alarm:
        raise KeyboardInterrupt

    pygame.display.set_caption('Drawing')
    [...rest of initialisation...]

Although this is a solution, I don't know what causes the behaviour.



回答2:

I think this happens because pygame is still connected to the display. I get the same thing if I kill a process that is using pygame to display objects on an Adafruit PiTFT. After a reboot it runs fine, but if I kill it and try to start it again it hangs on this line:

screen = pygame.display.set_mode(size)

So I tried catching the SIGTERM and doing a pygame.quit():

import sys, signal

def signal_handler(signal, frame):
  print 'Signal: {}'.format(signal)
  sleep(1)
  pygame.quit()
  sys.exit(0)

[...]

signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
while ...

Now I kill the process or interrupt it with ctrl-c, I can run it again without having to hit Ctrl-C to get it going.

William



回答3:

Pygame.init() will never cause this (unless you actually found an error in python, very unlikely), however os.putenv() well could cause this. This may not solve yout problem, but it is better to set your change in os.environ than in os.putenv() It also doesn't work on OSX, so if you are using it on OSX there could be errors. One last possible solution is to just work in a patch to your problem. This is a messy solution and shouldn't be implemented unless you can't find anything else, but it will work, provided that the program works fine after your keyboard interrupt. You could make the program automatically cause a keyboard interrupt event. A way to do this is to start a timer, which will raise a keyboard interrupt. The only difficult bit is that it will have to run in the background to cause the interrupt when it is necessary. You can use the threading module to do this. Here is a patch. (remember try not to use this it is not ideal)

import threading
import time

def timer():
    time.sleep(0.5)
    raise KeyboardInterrupt

interrupter = threading.Thread(target=timer)
interrupter.start()
os.putenv ( "SDL_VIDEODRIVER" , "fbcon" )
pygame.display.init()    # It hangs here
screen = pygame.display.set_mode ( ( 1024 , 768 ) )

pygame.draw.rect ( screen , ( 0 , 255 , 0 ) , ( 15 , 15 , 15 , 15 ) )
pygame.display.flip()

keyLoop = True
while keyLoop:
   for event in pygame.event.get():
      if event.type == pygame.KEYDOWN:
         if event.key == pygame.K_DOWN:
            print ( "Down arrow pressed, exiting" )
            keyLoop = False
            pygame.quit()

this should make it work.