I have been trying to set up a PS3 controller and be able to read analog input values from it, but whenever I press down or move any of the joysticks it doesn't read anything and returns false for everything.
I have been using various test codes I have found online for the controller and none of them seem to work. I'm starting to think it may be a hardware issue, but I'm still unsure.
import os
import pprint
import pygame
class PS3Controller(object):
controller = None
name = 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(1)
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:
#D - Pad
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__":
ps3 = PS3Controller()
ps3.init()
ps3.listen()
The code works fine. Apparently, I had to download a specific set of drivers to make the PS3 controller compatible with windows so it could be read as an XBOX 360 controller.
There were some tutorials online that used SCP ToolKit driver installer to make the controller compatible, however, it did make it so I couldn't use my Bluetooth mouse for some reason.
Related
I'm trying to get input from a joystick I have (specifically the Logitech Extreme 3D Pro) with a Python program. Unfortunately, I do not know how to do this well.
I currently have a working prototype using PyGame, but I do not want to use PyGame because I already want another Tkinter window open at the same time.
I've tried the inputs library, but I keep getting an inputs.UnpluggedError every time I plug the joystick in.
Are there any other methods of getting joystick input, other than PyGame?
I am using an MacBook Air running Big Sur.
Working code:
import os
import pprint
import pygame
import threading
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)
def controller_main():
ps4 = PS4Controller()
ps4.init()
ps4.listen()
controller_main()
Try this py-joystick module:
https://pypi.org/project/pyjoystick/
Hope it helps!😊
from pyjoystick.sdl2 import Key, Joystick, run_event_loop
def print_add(joy):
print('Added', joy)
def print_remove(joy):
print('Removed', joy)
def key_received(key):
print('received', key)
if key.value == Key.HAT_UP:
#do something
elif key.value == Key.HAT_DOWN:
#do something
if key.value == Key.HAT_LEFT:
#do something
elif key.value == Key.HAT_UPLEFT:
#do something
elif key.value == Key.HAT_DOWNLEFT:
#do something
elif key.value == Key.HAT_RIGHT:
#do something
elif key.value == Key.HAT_UPRIGHT:
#do something
elif key.value == Key.HAT_DOWNRIGHT:
#do something
run_event_loop(print_add, print_remove, key_received)
I need to to build an application that has multiple windows. In one of these windows, I need to be able to play a simple game and another window has to display questions and get response from a user that influences the game.
(1) I was wanting to use pygame in order to make the game. Is there a simple way to have pygame operate with multiple windows?
(2) If there is no easy way to solve (1), is there a simple way to use some other python GUI structure that would allow for me to run pygame and another window simultaneously?
The short answer is no, creating two pygame windows in the same process is not possible (as of April 2015). If you want to run two windows with one process, you should look into pyglet or cocos2d.
An alternative, if you must use pygame, is to use inter-process communication. You can have two processes, each with a window. They will relay messages to each other using sockets. If you want to go this route, check out the socket tutorial here.
Internally set_mode() probably sets a pointer that represents the memory of a unique display. So if we write:
screenA = pygame.display.set_mode((500,480), 0, 32)
screenB = pygame.display.set_mode((500,480), 0, 32)
For instance we can do something like that later:
screenA.blit(background, (0,0))
screenB.blit(player, (100,100))
both blit() calls will blit on the same surface. screenA and screenB are pointing to the same memory address. Working with 2 windows is quite hard to achieve in pygame.
Yes, that is possible. SDL2 is able to open multiple windows. In the example folder you can take a look at "video.py".
https://github.com/pygame/pygame/blob/main/examples/video.py
"This example requires pygame 2 and SDL2. _sdl2 is experimental and will change."
I've been trying to do this for a few days now, and I'm finally making progress. Does this count as simple? Or even as "being in" pygame. In this exampe I never even call pygame.init() That just seems to get in the way. The event pump is running (for mouse and keyboard) but not all the normal events seem to be coming thru (FOCUSGAINED and LOST in particular). In this example each window renders it status (size, position, etc) to it's self. I also have versions where I mix SDL windows with the pygame display. But those involve encapsulating a Window rather than extending it.
In order to draw on these windows you can draw on a vanilla surface as usually and then use the Renderer associated with the window to create a texture that will update the the window. (texture.draw(), renderer.present). You dont't use display.update() or flip() because you you aren't using the pygame display surface.
The X11 package is just my experimental windowing stuff and has nothing to do with X11. I think all my imports are explicit so it should be easy to figure out what the missing pieces are.
from typing import List
from pygame import Rect, Surface, Color
import pygame.event
from pygame.event import Event
from pygame.freetype import Font
from pygame._sdl2.video import Window, Renderer, Texture
from X11.windows import DEFAULT_PAD, default_font, window_info
from X11.text import prt
class MyWindow(Window):
def __init__(self, font: Font=None):
super().__init__()
self._font = font if font else default_font()
self.resizable = True
self._renderer = None
def destroy(self) -> None:
super().destroy()
def update(self):
r = self.renderer
r.draw_color = Color('grey')
r.clear()
#self.render_line(f"TICKS: {pg.time.get_ticks()}", 5, size=16.0)
txt: List[str] = window_info(self)
self.render_text(txt, lineno=0)
r.present()
#property
def renderer(self):
if self._renderer is None:
try:
self._renderer = Renderer.from_window(self)
except:
self._renderer = Renderer(self)
return self._renderer
def render_text(self, txt: List[str], lineno: int=0):
for line in txt:
self.render_line(line, lineno, size=16.0)
lineno += 1
def render_line(self, txt: str, lineno: int = 0, size: float = 0.0):
font = self._font
line_spacing = font.get_sized_height(size) + DEFAULT_PAD
x = DEFAULT_PAD
y = DEFAULT_PAD + lineno * line_spacing
# compute the size of the message
src_rect = font.get_rect(txt, size=size)
# create a new surface (image) of text
l_surf = Surface((src_rect.width, src_rect.height))
src_rect = font.render_to(l_surf, (0, 0), txt, size=size)
# get ready to draw
texture = Texture.from_surface(self.renderer, l_surf)
dst = Rect(x, y, src_rect.width, src_rect.height)
texture.draw(None, dst)
_running: bool = False
def test():
global _running
win1 = MyWindow()
win2 = MyWindow()
my_windows = {win1.id: win1, win2.id: win2}
win = win1
rnd = win1.renderer
print("pygame.get_init():", pygame.get_init())
print("pygame.display.get_init():", pygame.display.get_init())
print("pygame.mouse.get_pos():", pygame.mouse.get_pos())
clock = pygame.time.Clock()
_running = True
while _running:
events = pygame.event.get()
for event in events:
if event.type != pygame.MOUSEMOTION:
print(event)
if event.type == pygame.QUIT:
_running = False
elif event.type == pygame.WINDOWENTER:
win = my_windows[event.window.id]
print(f"Enter Window ({event.window.id}")
elif event.type == pygame.WINDOWLEAVE:
pass
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
_running = False
if event.key == pygame.K_1:
win = my_windows[1]
rnd = win.renderer
if event.key == pygame.K_2:
win = my_windows[2]
rnd = win.renderer
elif event.key == pygame.K_b:
rnd.draw_color = Color('blue')
rnd.clear()
elif event.key == pygame.K_g:
rnd.draw_color = Color('grey')
rnd.clear()
elif event.key == pygame.K_t:
win.render_line("Hello, world")
elif event.key == pygame.K_s:
surface = pygame.display.get_surface()
print("surface: ", surface)
elif event.key == pygame.K_f:
pygame.display.flip()
# pygame.error: Display mode not set
elif event.key == pygame.K_u:
pygame.display.update()
# pygame.error: Display mode not set
for win in my_windows.values():
win.update()
clock.tick(40)
if __name__ == '__main__':
test()
I am using a Raspberry Pi 3 to control a robotic vehicle. I have successfully linked my PS4 controller to the RPi using ds4drv. I have the following code working and outputting "Button Pressed"/"Button Released" when a button is pressed/released on the PS4 controller using pygame. I am wondering how to identify which button is exactly being pressed.
ps4_controller.py
import pygame
pygame.init()
j = pygame.joystick.Joystick(0)
j.init()
try:
while True:
events = pygame.event.get()
for event in events:
if event.type == pygame.JOYBUTTONDOWN:
print("Button Pressed")
elif event.type == pygame.JOYBUTTONUP:
print("Button Released")
except KeyboardInterrupt:
print("EXITING NOW")
j.quit()
Figured out a hack.
The PS4 buttons are numbered as the following:
0 = SQUARE
1 = X
2 = CIRCLE
3 = TRIANGLE
4 = L1
5 = R1
6 = L2
7 = R2
8 = SHARE
9 = OPTIONS
10 = LEFT ANALOG PRESS
11 = RIGHT ANALOG PRESS
12 = PS4 ON BUTTON
13 = TOUCHPAD PRESS
To figure out which button is being pressed I used j.get_button(int), passing in the matching button integer.
Example:
import pygame
pygame.init()
j = pygame.joystick.Joystick(0)
j.init()
try:
while True:
events = pygame.event.get()
for event in events:
if event.type == pygame.JOYBUTTONDOWN:
print("Button Pressed")
if j.get_button(6):
# Control Left Motor using L2
elif j.get_button(7):
# Control Right Motor using R2
elif event.type == pygame.JOYBUTTONUP:
print("Button Released")
except KeyboardInterrupt:
print("EXITING NOW")
j.quit()
You are really close! With a few tweaks, you code becomes this instead:
import pygame
pygame.init()
j = pygame.joystick.Joystick(0)
j.init()
try:
while True:
events = pygame.event.get()
for event in events:
if event.type == pygame.JOYAXISMOTION:
print(event.dict, event.joy, event.axis, event.value)
elif event.type == pygame.JOYBALLMOTION:
print(event.dict, event.joy, event.ball, event.rel)
elif event.type == pygame.JOYBUTTONDOWN:
print(event.dict, event.joy, event.button, 'pressed')
elif event.type == pygame.JOYBUTTONUP:
print(event.dict, event.joy, event.button, 'released')
elif event.type == pygame.JOYHATMOTION:
print(event.dict, event.joy, event.hat, event.value)
except KeyboardInterrupt:
print("EXITING NOW")
j.quit()
Some resources that I found helpful in writing the up included pygame's event documentation, the use of python's dir function to see what properties a python object has, and the documentation for pygame's parent C library, SDL if you wanted a deeper explanation of what the property actually means. I included both the dictionary access version (using event.dict) as well as the property-access version (using just event.whatever_the_property_name_is). Note that event.button only gives you a number; it is up to you to manually create a mapping of what each button number means on your controller. Hope this clears it up!
A bit late, but if there are people looking for a solution on this still, I have created a module: pyPS4Controller that has all the button events on the controller already mapped and they can be overwritten like so:
from pyPS4Controller.controller import Controller
class MyController(Controller):
def __init__(self, **kwargs):
Controller.__init__(self, **kwargs)
def on_x_press(self):
print("Hello world")
def on_x_release(self):
print("Goodbye world")
controller = MyController(interface="/dev/input/js0", connecting_using_ds4drv=False)
# you can start listening before controller is paired, as long as you pair it within the timeout window
controller.listen(timeout=60)
This is just an example for 1 event. There are more events that can be overwritten such as:
on_x_press
on_x_release
on_triangle_press
on_triangle_release
on_circle_press
on_circle_release
on_square_press
on_square_release
on_L1_press
on_L1_release
on_L2_press
on_L2_release
on_R1_press
on_R1_release
on_R2_press
on_R2_release
on_up_arrow_press
on_up_down_arrow_release
on_down_arrow_press
on_left_arrow_press
on_left_right_arrow_release
on_right_arrow_press
on_L3_up
on_L3_down
on_L3_left
on_L3_right
on_L3_at_rest # L3 joystick is at rest after the joystick was moved and let go off
on_L3_press # L3 joystick is clicked. This event is only detected when connecting without ds4drv
on_L3_release # L3 joystick is released after the click. This event is only detected when connecting without ds4drv
on_R3_up
on_R3_down
on_R3_left
on_R3_right
on_R3_at_rest # R3 joystick is at rest after the joystick was moved and let go off
on_R3_press # R3 joystick is clicked. This event is only detected when connecting without ds4drv
on_R3_release # R3 joystick is released after the click. This event is only detected when connecting without ds4drv
on_options_press
on_options_release
on_share_press # this event is only detected when connecting without ds4drv
on_share_release # this event is only detected when connecting without ds4drv
on_playstation_button_press # this event is only detected when connecting without ds4drv
on_playstation_button_release # this event is only detected when connecting without ds4drv
Full documentation is available # https://github.com/ArturSpirin/pyPS4Controller
if event.type == pygame.JOYBUTTONDOWN:
if j.get_button(0):
PXV = -0.1
if j.get_button(2):
PXV = 0.1
if event.type == pygame.JOYBUTTONUP:
if j.get_button(0) or j.get_button(2):
PXV = 0
joy button down works but PXV(player x velocity)
does not get reset back to zero when i release my controller
Working on a video project for raspberry pi. Trying to get video to fill entire screen. When I call omxplayer videofile.mp4 --aspect-mode fill it plays fine. However when I call it in my program the argument for the aspect ratio is not working.
import pygame
import sys
from time import sleep
from omxplayer import OMXPlayer
pygame.joystick.init()
_joystick_right = pygame.joystick.Joystick(0)
_joystick_right.init()
_joystick_left = pygame.joystick.Joystick(1)
_joystick_left.init()
pygame.init()
done = False
button = ''
controller = ''
player = ''
path = 'hankvids/'
movies = [
'vid1.mp4',
'vid2.mp4',
]
quit_video = False
while done==False:
for event in pygame.event.get():
if event.type == pygame.JOYBUTTONDOWN:
button = event.button
controller = event.joy
if quit_video == True:
if button == 0 and controller == 0:
player.quit()
quit()
else:
if player.is_playing():
player.load(path + movies[controller], pause=False)
else:
quit_video = True
player = OMXPlayer(path + movies[controller], args=["-b --aspect-mode fill"], pause=False)
You need to split the command into words: args=["-b", "--aspect-mode", "fill"],. Alternatively you can pass your CLI string to shlex to split the args string for you.
So I got raspi 3 and simple 8x8 LED matrix. After some playing with it I decided to make a simple snake game (displaying on that matrix) with pygame's events, I have no prior experience with pygame. There is no screen/display connected besides the led matrix.
So the problem at first was "pygame.error: video system not initialized", though I think i got it fixed by setting an env variable:
os.putenv('DISPLAY', ':0.0')
Now that I got it working I run it...and nothing happens, like no keystrokes are registered. Just this "junk", I don't know how to call it The dot on LED matrix is not moving. If i alter the snake's x or y position somewhere in the loop it moves as intended.
My code:
#!/usr/bin/python2
import pygame
import max7219.led as led
from max7219.font import proportional, SINCLAIR_FONT, TINY_FONT, CP437_FONT
import numpy as nqp
import os
SIZE = (8, 8)
class Board:
def __init__(self, size, snake):
"Board object for snake game"
self.matrix = np.zeros(size, dtype=np.int8)
self.device = led.matrix()
self.snake = snake
def draw(self):
#add snake
self.matrix = np.zeros(SIZE, dtype=np.int8)
self.matrix[self.snake.x][self.snake.y] = 1
for x in range(8):
for y in range(8):
self.device.pixel(x, y, self.matrix[x][y], redraw=False)
self.device.flush()
def light(self, x, y):
"light specified pixel"
self.matrix[x][y] = 1
def dim(self, x, y):
"off specified pixel"
self.matrix[x][y] = 0
class Snake:
def __init__(self):
"Object representing an ingame snake"
self.length = 1
self.x = 3
self.y = 3
if __name__=="__main__":
os.putenv('DISPLAY', ':0.0')
pygame.init()
snake = Snake()
board = Board(SIZE, snake)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
snake.y -= 1
elif event.key == pygame.K_DOWN:
snake.y += 1
elif event.key == pygame.K_LEFT:
snake.x -= 1
elif event.key == pygame.K_RIGHT:
snake.x += 1
board.draw()
I'm using pygame because I don't know anything else (Well I can't use pygame either but I just don't know of any alternatives). If it can be done simpler I will be happy to do it. Thank You in advance!
You should be able to use curses. Here's a simple example:
import curses
def main(screen):
key = ''
while key != 'q':
key = screen.getkey()
screen.addstr(0, 0, 'key: {:<10}'.format(key))
if __name__ == '__main__':
curses.wrapper(main)
You'll see that your key presses are registered - they're just strings.
However, this runs in blocking mode. Assuming that your code needs to do other things, you can turn nodelay on:
def main(screen):
screen.nodelay(True)
key = ''
while key != 'q':
try:
key = screen.getkey()
except curses.error:
pass # no keypress was ready
else:
screen.addstr(0, 0, 'key: {:<10}'.format(key))
In your scenario you probably would put this inside your game loop that's drawing out to your 8x8 display, so it would look something like this:
game = SnakeGame()
while game.not_done:
try:
key = screen.getkey()
except curses.error:
key = None
if key == 'KEY_UP':
game.turn_up()
elif key == 'KEY_DOWN':
game.turn_down()
elif key == 'KEY_LEFT':
game.turn_left()
elif key == 'KEY_RIGHT':
game.turn_right()
game.tick()
One thing to note - this approach will take 100% of your CPU, so if you don't have some other way to limit what your app is doing it can cause you some problems. You could extend this approach using threading/multiprocessing, if you find that to be something that you need.