Pygame display init on headless Raspberry Pi Zero (Raspbian Buster Lite) - python

I have Pi Zero running Raspbian Buster Lite, there's no display attached. It runs a python program at startup (crontab #reboot entry), something like this:
#reboot sudo su username /home/username/launcher.sh >> /home/username/crontab.log 2>&1
I have to initialize pygame display, because I need to process the Sound object end events. Without initializing the display, the events are not triggered.
The code breaks at this line, with error "pygame.error: Unable to open a console terminal"
os.putenv('SDL_VIDEODRIVER', 'dummy')
pygame.display.init() # error at this line
My code used to work in the previous Raspbian (Stretch) version. I have experimented with all kinds of drivers, added/removed pygame.display.set_mode((1, 1)), but no luck.
I am aware of the other posts that tackle with the same question, but I have exhausted all those approaches - changing the driver to fbcon, skipping the set_mode line, etc. The only thing that works is running the program as root:
#reboot /home/username/launcher.sh >> /home/username/crontab.log 2>&1
But it's a bad idea, from the security perspective. I'd rather run it as unpriviliged user.
Is there a new kind of hack to get it past the display.init()?

pygame.init initializes all Pygame modules. You do not need to initialize all Pygame modules, only the ones you need. For playing music it is sufficient to initialize pygame.mixer. Some examples
import pygame
pygame.mixer.init()
pygame.mixer.music.load('music.mp3')
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.delay(10)
pygame.mixer.quit()
import pygame
pygame.mixer.init()
my_sound = pygame.mixer.Sound('music.mp3')
my_sound.play(0)
while pygame.mixer.get_busy():
pygame.time.delay(10)
pygame.quit()
import pygame.mixer
pygame.mixer.init()
my_sound = pygame.mixer.Sound('sound.wav')
my_sound.play(0)
pygame.time.wait(int(my_sound.get_length() * 1000))
pygame.mixer.quit()
However, you cannot use the event module because it is tied to the display module.

Related

Python 3.9 Pygame mixer frequency change not working

I am using pygame (version = 2.0.1) in python==3.9.1 in windows OS laptop. I use the pygame.mixer module to play my mp3 file. My code:
from time import sleep
from pygame import mixer
audio_length = 5.184
mixer.init(frequency=26500)
# mixer.init(frequency=44100)
mixer.music.load("Test.mp3")
mixer.music.play()
sleep(audio_length)
mixer.music.stop()
mixer.quit()
The above code works without errors.. BUT the audio is played at the same default frequency no matter what value I set in mixer.init().
This seems to be quite strange because the above code works perfectly fine (with frequency change) in python 3.7 and pygame 1.9.6
I realised this only after i thought about upgrading my python version.
Is this a bug which I have to report? In that case how do I proceed so?
Or... is it just that I'm not following the new way in python 3.9?
In case, the file I used for testing is required, you can download from https://drive.google.com/file/d/1vd7aO5ahFBHfoT97_0RSBU4reNAWseB6/view?usp=sharing
This is a known issue that hasn't been resolved as of today.
https://github.com/pygame/pygame/issues/2435
Mixer init expects to also get the size and the channels, so you will have to do it like this:
mixer.init(frequency=26500, size=-16, channels=2)
For more info, see pygame docs: https://www.pygame.org/docs/ref/mixer.html#pygame.mixer.init

Python 3 Pygame programs will not quit

When the pygame window closes, I want to immediately stop execution; that's the end of the program. I have looked up and down the internet and every problem I come across is something like "Did you try pygame.quit()" or "You never break out of your infinite loop". That isn't my problem. Here's my code:
#!/usr/bin/env python3
import pygame
from sys import exit as sysExit
pygame.init()
screen = pygame.display.set_mode((512,512))
screen.fill((255,255,255))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.display.update()
pygame.display.quit()
pygame.quit()
exit(0)
sysExit(0)
#None of these work, code under them is unreachable but the process hangs
As the comment and question title say, this process hangs. I honestly don't what else I could do to tell python I'm done, and I've tried every combination of these quit functions but nothing works. The window will close, but the program never ends. In my searching I've found people have issues with something called "IDLE" which I do not have.
Here's what I do have (my system): Linux Mint Cinnamon 18, 32-bit (i686), using python version 3.5.2 (no IDE, just running from terminal)
EDIT - Apparently there's some confusion about what I'm talking about when I say the program "hangs", so here's some screenshots of the issue in action. This first one shows you that the above code is, in fact, usable to reproduce the problem.
Now all I do is run this script, and it just gives me a blank window like you'd expect.
Now I click the "x" button on the window, and the window disappears, but my terminal is left like this. The screen capture won't show you the blinking cursor, but you'll notice there's no new prompt.
I've so far been resorting to doing this to get the thing to stop:
throw it in the background, force kill the job (SIGTERM gets ignored btw) and foreground it again and now I have a prompt.
check pygame.init() -> (numpass, numfails), then loading each module manually (yes, each module has an init() and quit() method). I would suspect the mixed or music module to be the problem (due to os/driver/hardware issues).
The pygame irc channel helped me figure this out. Specifically, this answer comes from user DR0ID_ who walked me through some debugging I should've thought of in the first place.
First of all, once I'm done with this answer I'll edit the question, but I have to apologize for leaving something out initially: I'm doing all this on a Virtualbox VM. By walking through the different modules initialized by pygame.init(), I discovered that the one that actually hangs it is pygame.mixer. No word yet from the pygame devs, but the working theory is that this is related to Virtualbox sound drivers and/or their interface with the host drivers. Basically, if you're on a virtual machine and don't need to play sounds, the workaround is to only initialize the modules you need. Since in my example I only need pygame.display, the solution is easy:
#!/usr/bin/env python3
import pygame
pygame.display.init()
screen = pygame.display.set_mode((512, 512))
screen.fill((255, 255, 255))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.display.quit()
exit()

Screen tearing using pygame on Raspberry Pi

I am stuck on a screen tearing problem for about a week now.
Here is my problem: I would like to make a program that can display a sequence of PNG pictures really fast (at a rate of 30 fps or more). To do this I use the pygame library, especially pygame.display.blit and pygame.display.flip.
Here is an example of the code (with a homemade delay function):
import time
import pygame
screen=pygame.display.set_mode(size, pygame.FULLSCREEN)
nbPicturesToLoad=12
pictures=range(nbPicturesToLoad)
for i in range(nbPicturesToLoad):
pictures[i]=pygame.image.load(pictureName).convert() //Here pictureName depends on the value of i
(...)
for i in range(nbPicturesToLoad):
timer1=time.time()
screen.blit(pictures[i], (0,0))
pygame.display.flip()
timer2=time.time()
timer=timer2-timer1
while(timer<0.03333):
timer2=time.time()
timer=timer2-timer1
The timer allows me to have the same refresh rate on the screen.
My problem is that one can see with their own eyes that there is a screen tearing problem (https://en.wikipedia.org/wiki/Screen_tearing).
On the pygame documentation they recommend to use other flags for the screen init (pygame.HWSURFACE and pygame.DOUBLEBUF, http://www.pygame.org/docs/ref/display, y.set_mode). Apparently these could help to correct the screen tearing problems. But when I use these flags I see no screen tearing correction.
I've seen somewhere that pygame can't handle vsync on X11, which would explain why I see no difference with the HWSURFACE and DOUBLEBUF flags.
Has anyone ever encountered this vsync issue on the Raspberry Pi? Is there a parameter to put in the "config.txt" file?
Does anyone have a way to get real vsync information to avoid these screen tearing effects?
I've seen that pyglet could handle vsync in the created windows, but for unknown reasons I can't display images in the pyglet window and I'm really not sure if it would fix the problem, especially if a Raspberry can't handle vsync right now.
Try using the Pygame clock to handle timing, it is much easier to implement and understand and it may solve the issue.
shown below:
import pygame as py
py.init()
WINDOW = (400,400)
screen = py.display.set_mode(WINDOW)
nbPicturesToLoad=12
pictures = range(nbPicturesToLoad)
for i in range(nbPicturesToLoad):
pictures[i]=py.image.load(pictureName).convert()
FPS = 30
clock = py.time.Clock() # Create a clock object
done = False
while not done:
for event in py.event.get():
if event.type == py.QUIT:
done = True
for i in range(nbPicturesToLoad):
screen.fill((255,255,255))
screen.blit(pictures[i],(0,0))
py.display.flip()
clock.tick(FPS) # use pygame.time.Clock to wait 1/30th of a second
py.quit()
However, since this is a screen tearing problem the solution may be to stop using Pygame altogether. How to avoid tearing with pygame on Linux/X11
This question seems to cover what you may be looking for.
Good luck.

pygame sound with Python 3.5 on Windows

im currently having trouble with the python 3rd party library pygame.
Other posts on stackoverflow didn't help me so i opened a new post.
I want to load a *.mp3 file into the program and play it. My code so far (copied from other posts):
import pygame,time,sys
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=4096)
print ("Mixer settings", pygame.mixer.get_init())
print ("Mixer channels", pygame.mixer.get_num_channels())
pygame.mixer.music.set_volume(1.0)
pygame.mixer.music.load("test.mp3")
pygame.mixer.music.play()
clock = pygame.time.Clock()
while pygame.mixer.music.get_busy():
clock.tick(30)
Here is a link to a screenshot from the Windows Sound panel: http://i.imgur.com/fUvJXof.png
I see that there is some output, but i cant hear anything on my headphones.
I hope some has an idea what the problem is.
Thanks
Check your hardware/audio drivers. It doesn't sound like (pardon the pun!) an issue with python or pygame. Does your audio work outside python? Also, if you've got other programs open, they can sometimes take control of your audio drivers and not allow other programs (in this case python) to output.

Read console input using pygame

Is there anyway to use pygame to get input from the console, rather than having to display a separate window to get input? I'm using pygame to track how long the keys on the keyboard are pressed.
The following code doesn't work (this is just a minimal example, it doesn't actually keep track of time elapsed):
pygame.init()
while 1:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
print event.key, 'pressed'
It doesn't look like any pygame event is being raised. If I add
screen = pygame.display.set_mode((640, 480))
After pygame.init()
then the event is raised, but I have that ghastly window I don't want to deal with.
To explain why I don't want the window, I envision this application being a command-line utility, so I can't have that. Is there any functional reason preventing pygame from running on the command-line?
Thanks!
EDIT: I speculated that the problem was pygame.init(), and that I only needed to initialize the key and event modules. According to http://www.pygame.org/docs/tut/ImportInit.html I should have been able to call pygame.key.init()pygame.event.init() but it didn't work.
Pygame is designed for making (graphical) games, so it only captures key presses when there is a window displayed. As Ignacio said in his answer, reading from the command line and from another window are very different things.
If you want to create a command line application, try curses:
http://docs.python.org/library/curses.html
Unfortunately, it only works on Linux and Mac OS X.
Console input comes in via stdin, which pygame is not prepared to handle. There is no reliable way to get press/release events via stdin since it's dependent on the terminal sending it keypresses.
If you just make the window really small using
screen = pygame.display.set_mode((1, 1))
you can't see it. So you are clicked into the window but you don't notice.
If you click anywhere of course it stops working. You have to click on the pygame window icon for it to work again.
If you just plain don't want a window of any kind at all, you can use PyHook. If you just want a console application, get user input with the built-in Python command "raw_input(...)".
Try pygame.display.iconify().
This will hide the pygame screen, and you will still be able to detect keypresses.

Categories

Resources