I'm trying to run pygame on my Raspberry Pi Zero W, which has a PS4 controller hooked up to it. I found some code that should work but I get this error when I try to python3 game.py:
Traceback (most recent call last):
File "controller.py", line 74, in
ps4.listen()
File "controller.py", line 52, in listen
for event in pygame.event.get():
pygame.error: video system not initialized
The same code from someone else on Stackoverflow got it to work (at least that's what I assume), but he had a different problem, yet the same code. I did try to run that code instead, but I got the same error. I tried all suggestions I could find from Stackoverflow, but none of them worked. Here's the code I found:
import os
import pprint
import pygame
class PS4Controller(object):
"""Class representing the PS4 controller. Pretty straightforward functionality."""
controller = None
axis_data = None
button_data = None
hat_data = None
def init(self):
"""Initialize the joystick components"""
pygame.init()
pygame.joystick.init()
self.controller = pygame.joystick.Joystick(0)
self.controller.init()
def listen(self):
"""Listen for events to happen"""
if not self.axis_data:
self.axis_data = {}
if not self.button_data:
self.button_data = {}
for i in range(self.controller.get_numbuttons()):
self.button_data[i] = False
if not self.hat_data:
self.hat_data = {}
for i in range(self.controller.get_numhats()):
self.hat_data[i] = (0, 0)
while True:
for event in pygame.event.get():
if event.type == pygame.JOYAXISMOTION:
self.axis_data[event.axis] = round(event.value,2)
elif event.type == pygame.JOYBUTTONDOWN:
self.button_data[event.button] = True
elif event.type == pygame.JOYBUTTONUP:
self.button_data[event.button] = False
elif event.type == pygame.JOYHATMOTION:
self.hat_data[event.hat] = event.value
# Insert your code on what you would like to happen for each event here!
# In the current setup, I have the state simply printing out to the screen.
os.system('clear')
pprint.pprint(self.button_data)
pprint.pprint(self.axis_data)
pprint.pprint(self.hat_data)
if __name__ == "__main__":
ps4 = PS4Controller()
ps4.init()
ps4.listen()
Any clue what to do and why it's not working? I run this on Jessie Lite, so there is no desktop or anything like that.
pygame.init fails silently when a module cannot be initialized:
No exceptions will be raised if a module fails, but the total number if successful and failed inits will be returned as a tuple. You can always initialize individual modules manually, but pygame.init()initialize all imported pygame modules is a convenient way to get everything started. The init() functions for individual modules will raise exceptions when they fail.
In your case, it didn't initialize the display. To have it fail loudly, call pygame.display.init explicitly:
import pygame.display
pygame.display.init()
Related
I have constructed some code to load assets into a character class. This works fine in a single thread, but I want to have a loading screen when the assets are loading.
I put the asset loading into a function, and tried to make it run in a separate thread. The issue is that it now hangs, and seemingly does not even execute the main thread.
I've kept an eye on the memory usage, and the memory is well-behaved (never goes above 500M out of 1976M, bits or bytes I'm not sure, it's whatever PyCharm reports).
import threading
import os
import pygame
from pygame.locals import *
def load_assets(screen: pygame.Surface, results: List):
print("thread: started to load assets in thread")
appearances1 = {} # some dictionary, Dict[str, Dict[str, str]]
default_outfit = "office_wear"
print("thread: instance of game class to be created")
game = Game("lmao", screen) # crashes somewhere here when running multithreaded
print("thread: game initialized") # this is never achieved
# rest of function not relevant
results.append(game)
def main():
# ====== INITIALIZE PYGAME ======
pygame.init()
pygame.font.init()
screen = pygame.display.set_mode((1024, 768))
clock = pygame.time.Clock()
loading = create_loading_screen(screen)
loading.display(screen)
pygame.display.flip()
# ====== START OTHER THREAD FOR LOADING ASSETS ======
results = []
x = threading.Thread(
target=load_assets, args=(screen, results), daemon=True
)
x.start()
while x.is_alive():
# never see this print statement!
print("x is fine") # want to blit loading screen here
# if I comment out the threading, and run the following instead, my game boots up just fine
# in under 3-4 seconds of waiting for the assets to load
# load_assets(screen, results)
game = results[0]
# other code here to render the game, no more threading after this point
while 1:
for event in pygame.event.get():
if event.type == QUIT:
return -1
game.main()
pygame.display.flip()
clock.tick(game.fps)
if __name__ == '__main__':
main()
You can see the print statements here (everything hangs and we don't even make it to the main thread!)
I figured out the issue. I feel really dumb now... The issue was my Game initializer was touching the pygame.display() which I already knew it shouldn't... I need a nap...
I'm developping a game using pygame and I want to create a loading screen while the assets are loaded. The loading screen have animations, so loading screen and assets loading should be occurring at the same time.
Consider the code below:
class Game:
def __init__(self):
self.loading_screen()
self.load_assets()
def loading_screen(self):
# do something while load_assets() is running
def load_assets(self):
# load all assets needed
I've tried Process from multiprocessing, but I dont know how to keep loading_screen() running without freezes while load_assets() are running.
Also, I've tried threads, but python doesn't run threads simultaneously, so, in some moment, the loading_screen() will freeze. (This could be wrong, but this was observed in the game)
Some help about this?
Thanks for all
Threads are suitable for this. Nothing runs exactly simultaneously on a multi user system anyway.
import threading
from time import sleep
class Game:
def __init__(self):
self.loading_screen()
def loading_screen(self):
t = threading.Thread(target=self.load_assets)
t.daemon = True
t.start()
while t.isAlive():
sleep(0.1)
print("loading screen")
print ("assets done, ready to proceed now")
def load_assets(self):
i = 0
while i < 10:
print("loading assets")
i += 1
sleep(1)
g = Game()
print("done")
This puts your asset loading to a thread and then enters loading_screen() in the main thread. This just prints messages to demonstrate they both run in parallel.
I can understand what problems the OP is having, a preferred solution is to use multiprocessing rather than multithreading. The given demo from the first answer uses time.sleep for simulating 'heavy loading' in the loading function, but when loading is actually going to take place, it will take a major amount of performance out of the CPU.
So it might happen that your loading animation is playing at 5-10 FPS when you expected 100+ FPS from it.
In this case, it is best to use multiprocessing to achieve the same. From what I have heard, it is a bit complicated to implement it, but it is quite possible. The loading function will then run as a separate process, not a separate thread from the same process.
Here is a small demo program to illustrate a loading screen using multiprocessing:
import multiprocessing
from multiprocessing.connection import Connection
def loading_screen(conn: Connection, stage):
clock = pygame.time.Clock()
fps = 60
c = 0
font = pygame.font.SysFont('consolas', 25)
while True:
screen.fill((0, 0, 50))
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_ESCAPE:
return
if conn.poll(): # check if any object is available to be received or not
# this line is used to prevent waiting until the next object is received
c = conn.recv() # if available, receive the object
if c == 'end':
# process end condition checking
return
pygame.draw.rect(screen, 'white', (50, 50, c, 50))
screen.blit(font.render(f'{c} / {stage * 100} % [stage {stage}]', True, 'white'), (50, 150))
screen.blit(font.render('Check console for status', True, 'white'), (50, 200))
pygame.display.update()
clock.tick(fps)
pygame.display.set_caption(f'Multiprocessing Loading Screen [FPS = {int(clock.get_fps())}]')
def resource_load(conn: Connection, items):
# load resources
# for now it increments a counter and sends it via the connection object
# you can replace it for CPU intensive processes
# and pass the loaded objects to the connection object
c = 0
while True:
c += 1
if c > items:
conn.send('end') # signal end of loading current stage
return # returning will end the process automatically
conn.send(c) # send the counter value via the connection object
if __name__ == '__main__':
import pygame
pygame.init()
screen = pygame.display.set_mode((500, 300)) # initialize display
receiver, sender = multiprocessing.Pipe(duplex=False) # initialize sender and receiver connection objects [unidirectional]
print('Program to demonstrate a simple loading screen using multiprocessing')
print()
print('starting stages...')
multiprocessing.Process(target=resource_load, args=(sender, 100)).start()
print('loading stage 1')
loading_screen(receiver, 1)
multiprocessing.Process(target=resource_load, args=(sender, 200)).start()
print('loading stage 2')
loading_screen(receiver, 2)
multiprocessing.Process(target=resource_load, args=(sender, 300)).start()
print('loading stage 3')
loading_screen(receiver, 3)
input('finished loading all stages... press Enter to exit console') # press Enter inside the console to exit program
EDIT:
I have made a video on this if interested: https://www.youtube.com/watch?v=KWGDgPldPVo
If I attempt to black out the screen using PyGame, PyGame seems to intercept SIGINT and I cannot use control-C to exit my program. If I simplify my code to the most basic pieces, it looks like this:
import signal
import time
import pygame
class Foo:
def __init__(self):
self.bgcolor = [0,0,0]
pygame.display.init()
pygame.font.init()
pygame.mouse.set_visible(False)
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
self.screen = pygame.display.set_mode(size, pygame.FULLSCREEN)
self.blank_screen()
def blank_screen(self):
self.screen.fill(self.bgcolor)
pygame.display.update()
#pygame.quit()
def sighandle(self, signal, frame):
print('I got a ctrl-c')
self.close()
def close(self):
print('calling close')
pygame.quit()
def run(self):
print('I am running')
time.sleep(10)
if __name__=='__main__':
bar = None
try:
bar = Foo()
signal.signal(signal.SIGINT, bar.sighandle)
signal.signal(signal.SIGTERM, bar.sighandle)
bar.run()
finally:
print('in finally block')
if bar is not None:
bar.close()
If I remove the pygame parts, ctrl-c works as expected, and sighandle()'s message is printed. When I put the pygame parts in, the screen blanks as expected, but ctrl-c is ignored, and sighandle()'s message is never printed. The code just runs to completion. Furthermore, if I uncomment the commented out pygame.quit() in blank_screen(), ctrl-c works again, but of course the blacked out screen goes away.
Why is the code ignoring ctrl-c when I invoke PyGame, and how do I make it work as I expect, so that ctrl-c kills the program through sighandle?
After some research, I found that this is indeed the case by design. I don't see it explicitly mentioned in PyGame's documentation, though.
Ctrl-C can be enabled in the code above by handling it as an event. I can modify Foo.run() as such:
def run(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.sighandle()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_c and pygame.key.get_mods() & pygame.KMOD_CTRL:
print "pressed CTRL-C as an event"
self.sighandle()
print('I am running')
time.sleep(10)
I took the PyGame based screen blanking code from Adafruit's VideoLooper, which also included the signal library based handling of SIGINT. But I don't think their code will handle Ctrl-C as-is either.
I am working on a small project including Raspberry Pi camera. I am using multiprocessing on the PC to perform various tasks and one of them is displaying images in a Pygame window. I have manager to minimize the code suffering the problem. This is the main function, which just creates a Game process, runs it and waits for it to end:
# main.py
from multiprocessing import Manager
import game
with Manager() as manager:
namespace = manager.Namespace()
namespace.done = False
jobs = [
game.Game(namespace),
]
for job in jobs:
job.start()
for job in jobs:
job.join()
Below is the code of the Game class. The state is shared because there will be other processes checking if the program has finished.
# game.py
import multiprocessing
import pygame
import time
import sys
class Game(multiprocessing.Process):
def __init__(self, the_state):
super(Game, self).__init__()
pygame.init()
self.screen = pygame.display.set_mode((100, 100))
self.state = the_state
def handle_key_events(self):
""" Wrapper for handling key events """
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.state.done = True
return
def run(self):
while True:
self.handle_key_events()
if self.state.done:
print('exiting PyGame thread')
pygame.display.quit()
pygame.quit()
return
The behaviour right now is that it doesn't end on clicking the Pygame's exit button. However, if I comment out the pygame.quit() line from Game's run method, it gives me the following message:
exiting PyGame thread
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 120 requests (120 known processed) with 1 events remaining.
I tried googling that error but little success. I can't shake off the feeling that I'm attempting something I shouldn't be, considering that probably the Pygame's window is a process on its own.
I would appreciate some insight and recommendations on where to read up
As Blckknght suggested, the issue was that the parent process (main.py) initiated PyGame's window, while the child process (game.py) attempted to quit it. It can be resolved by moving this part:
pygame.init()
self.screen = pygame.display.set_mode((100, 100))
into Game's run method
I am trying to make the most simple pythong code that will respond when a button is pressed on a joystick. I used code from several different examples and I still cannot get it to work. The following code will not dispatch the event when I press the trigger (or any button for that matter)
import pygame
joy = []
def handleJoyEvent(e):
if e.type == pygame.JOYBUTTONDOWN:
str = "Button: %d" % (e.dict['button'])
if (e.dict['button'] == 0):
print ("Pressed!\n")
else:
pass
def joystickControl():
while True:
e = pygame.event.wait()
if (e.type == pygame.JOYBUTTONDOWN):
handleJoyEvent(e)
# main method
def main():
pygame.joystick.init()
pygame.display.init()
for i in range(pygame.joystick.get_count()):
myjoy = pygame.joystick.Joystick(i)
myjoy.init()
joy.append(myjoy)
# run joystick listener loop
joystickControl()
# allow use as a module or standalone script
if __name__ == "__main__":
main()
I assume you've tried leaving off the if and just printing str?
Your joystick might also not be working properly. Does it work in other programs?
If you are using linux you might need to install a joystick driver. For Windows, check the device manager.