how to print after button is up? - python

using pygame, when i execute the program, all i want to do is to keep printing "mode 1". If i press button number 7 on the joystick, i want it to print "mode 2" only after button 7 has been UP (meaning pressed then released). If i press button 7 again, i want it to print "mode 1" again. I tried using pygame.JOYBUTTONDOWN and pygame.JOYBUTTONUP but it just doesnt work and the best code i can come up with is the following:
Result: it prints "mode 1".....then it prints "mode 2" only if i keep holding button 7 :( if i let go of button 7, it just goes back to printing "mode 1"
import sys
import pygame
import time
from pygame.locals import *
pygame.init()
global data_of_axis
def main():
global data_of_axis
gui_screen = pygame.display.set_mode((500, 700))
pygame.display.set_caption("Joystick example")
joysticks = {}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True # Flag that we are done so we exit this loop.
# Handle hotplugging
if event.type == pygame.JOYDEVICEADDED:
# This event will be generated when the program starts for every
# joystick, filling up the list without needing to create them manually.
joy = pygame.joystick.Joystick(event.device_index)
joysticks[joy.get_instance_id()] = joy
#print("Joystick {} connencted".format(joy.get_instance_id()))
if event.type == pygame.JOYDEVICEREMOVED:
del joysticks[event.instance_id]
#print("Joystick {} disconnected".format(event.instance_id))
for joystick in joysticks.values():
butt7Down = joystick.get_button(7)
if butt7Down:
print("mode 2")
else:
print("mode 1")
time.sleep(0.1)
pygame.display.update()
if __name__ == "__main__":
main()
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit()

One way to do this is to watch for a Joystick button event, and then re-read the status of all joystick buttons. Whenever the new set of states is read, make a copy of the set, to notice changes between "before" and "now".
This allows the code to simply check if the previous state was pressed, but now it isn't, and then print the appropriate output string.
# Create a list of "released" button states
button_states = [ False for i in range( joystick.get_numbuttons() ) ]
prev_states = button_states.copy()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.JOYBUTTONDOWN or \
event.type == pygame.JOYBUTTONUP:
prev_states = button_states.copy() # make a copy of the previous states
# Whenever we see a joystick event, re-read the status of all buttons
button_states = [ joystick.get_button( i ) for i in range( joystick.get_numbuttons() ) ]
# [...]
# If there's enough button states
if len( button_states ) > 7 and \
len( prev_states ) > 7:
# If the button was pushed, but now is released
if prev_states[7] == True and button_states[7] == False:
print( "mode2" )
else:
print( "mode1" )

Related

Pygame: Adding an Escape key to exit whole game with stopwatch function

I'm making a game via Python on a Raspberry Pi. I'm using the GPIOs to light up an LED and detect a button switch.
I wanted to incorporate an ESC on the keyboard so we can exit at any time.
But whenever I add in the ESC key code into the main while loop. It doesn't work. The LED and Buttons work, but when I press on the ESC key, it doesn't do anything.
The loop runs to refresh/run a stopwatch and listen to an LED button via the GPIO.
I wanted some advice on how things like ESC key are handled in games. Especially with fast paced games where the loop and cycles are very fast.
Any tips or suggestions would be greatly appreciated. Thanks in advance.
Please see the code below:
# Importing all libraries
import RPi.GPIO as GPIO
import sys, time, atexit, pygame
# Setup GPIO and Pygame
GPIO.setmode(GPIO.BCM)
pygame.init()
# Define Tuples and Variables
leds = (16,17,22,9,5)
switches = (19,4,27,10,11)
button_pressed = False
taskcomplete = False
# Pygame visual variables
screen = pygame.display.set_mode( (1024,240) )
counterfont = pygame.font.Font('DSEG14Modern-Regular.ttf', 70)
# Set Pygame refresh rate variable = clock
clock = pygame.time.Clock()
# Clock variables
sec_val = 0
sec = 0
mins = 0
hours = 0
# Status variables
paused = False
running = True
# Start the clock
start_time = pygame.time.get_ticks()
# Defining Functions
# Function that renders segment display on screen
def time_convert(sec):
sec = sec % 60
sec_val = ("Timer: {0}".format(round((sec), 2)))
counting_text = counterfont.render(str(sec_val), 3, (134,145,255))
counting_rect = counting_text.get_rect(left = screen.get_rect().left)
screen.fill( (0,0,0) )
screen.blit(counting_text, (300,40))
pygame.display.update()
# Stopwatch function to compute for a SS:MS based stopwatch
def stop_Watch():
end_time = time.time()
time_lapsed = end_time - start_time
sec_val = time_convert(time_lapsed)
# Press Button 1 to start the game
def but_3():
while GPIO.input(switches[2]) == GPIO.LOW:
GPIO.output(leds[2],True)
time.sleep(0.01)
stop_Watch()
GPIO.output(leds[2],False)
print(" Button 3 is pressed! Exit")
start_time = time.time()
def buttonPress(channel):
# This function gets called every time a button is pressed, if the button pressed is the same as the button
# that is illuminated, then we set the "correct_button" variable to True,
# otherwise we set the "incorrect_button" variable to True.
# We need to set some variables to global so that this function can change their value.
button_pressed = True
def exit():
# This function gets called when we exit our script, using Ctrl+C
print("GPIO Clean Up!")
GPIO.cleanup()
pygame.quit()
# This tells our script to use the "exit()" without this, our "exit()" function would never be called.
atexit.register(exit)
#Loop through the leds to set them up
for led in leds:
# Set the led to be an ouput
GPIO.setup(led, GPIO.OUT)
# Turn the led off
GPIO.output(led,False)
# Loop through the switches to set them up
for switch in switches:
# Set the switch to be an input
GPIO.setup(switch, GPIO.IN)
# Add rising edge detection
GPIO.add_event_detect(switch, GPIO.RISING, bouncetime=300)
# Add the function "buttonPress" to be called when switch is pressed.
GPIO.add_event_callback(switch, buttonPress)
# Main sequence code
# Setup Pygame refresh rate to 120 fps
clock.tick(120)
# Start timer
start_time = time.time()
# Main loop
while running:
# Press Button 1 to start the game
while GPIO.input(switches[0]) == GPIO.LOW:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
print("escape pressed")
running = False
GPIO.output(leds[0],True)
time.sleep(0.01)
stop_Watch()
GPIO.output(leds[0],False)
print(" Button 1 is pressed! Exit")
running = False
exit()
It's because it's in another while loop I'm guessing, so it's not in the running while loop anymore. You can add pygame.quit() to make it quit that way though:
# Main loop
while running:
# Press Button 1 to start the game
while GPIO.input(switches[0]) == GPIO.LOW:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
print("escape pressed")
pygame.quit()
running = False
GPIO.output(leds[0],True)
time.sleep(0.01)
stop_Watch()
GPIO.output(leds[0],False)
print(" Button 1 is pressed! Exit")
running = False
exit()
Or, since you have a function named exit() that does the same thing, you can add exit() to those places instead.

PyGame - RaspberryPi 3b+ with a ps3 controller

I am trying to use pygame with the raspberry pi to use a PlayStation 3 controller as an input for a car.
I have tested the controller with a demo code, and everything works fine. Then when I try to use it in my program, it reads 0.0 as the input, when the joysticks are moved. attached is my current code:
import pygame
class controller:
def __init__(self):
pygame.init()
pygame.joystick.init()
global joystick
joystick = pygame.joystick.Joystick(0)
joystick.init()
def get_value(self, axis):
value = joystick.get_axis(axis)
return value
control = controller()
val = control.get_value(0)
while True:
print(val)
I am aware that this test is only for axis 0, but the output is still 0.0 for all axes.
Below, i have attached the demo code, where all the values are properly read.
import pygame, sys, time #Imports Modules
from pygame.locals import *
pygame.init()#Initializes Pygame
pygame.joystick.init()
joystick = pygame.joystick.Joystick(0)
joystick.init()#Initializes Joystick
# get count of joysticks=1, axes=27, buttons=19 for DualShock 3
joystick_count = pygame.joystick.get_count()
print("joystick_count")
print(joystick_count)
print("--------------")
numaxes = joystick.get_numaxes()
print("numaxes")
print(numaxes)
print("--------------")
numbuttons = joystick.get_numbuttons()
print("numbuttons")
print(numbuttons)
print("--------------")
loopQuit = False
while loopQuit == False:
# test joystick axes and prints values
outstr = ""
for i in range(0,4):
axis = joystick.get_axis(i)
outstr = outstr + str(i) + ":" + str(axis) + "|"
print(outstr)
# test controller buttons
outstr = ""
for i in range(0,numbuttons):
button = joystick.get_button(i)
outstr = outstr + str(i) + ":" + str(button) + "|"
print(outstr)
for event in pygame.event.get():
if event.type == QUIT:
loopQuit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
loopQuit = True
# Returns Joystick Button Motion
if event.type == pygame.JOYBUTTONDOWN:
print("joy button down")
if event.type == pygame.JOYBUTTONUP:
print("joy button up")
if event.type == pygame.JOYBALLMOTION:
print("joy ball motion")
# axis motion is movement of controller
# dominates events when used
if event.type == pygame.JOYAXISMOTION:
# print("joy axis motion")
time.sleep(0.01)
pygame.quit()
sys.exit()
any feedback will be much appreciated.
The code is losing the reference to the initialised joystick. It needs to maintain an internal link to it. Note the use of self. in the class below. This keeps the reference inside the class, making "self.joystick" a member variable of the class. Python classes need the self. notation (unlike lots of (all?) other object orientated languages). While editing I changed some of the names to match the Python PEP-8 style guide, I hope that's OK ;)
class Controller:
def __init__( self, joy_index=0 ):
pygame.joystick.init() # is it OK to keep calling this?
self.joystick = pygame.joystick.Joystick( joy_index )
self.joystick.init()
def getAxisValue( self, axis ):
value = self.joystick.get_axis( axis )
return value
Maybe you left the extra code out of the question, but a PyGame program without an event loop will eventually lock up.
import pygame
# Window size
WINDOW_WIDTH = 300
WINDOW_HEIGHT = 300
class Controller:
""" Class to interface with a Joystick """
def __init__( self, joy_index=0 ):
pygame.joystick.init()
self.joystick = pygame.joystick.Joystick( joy_index )
self.joystick.init()
def getAxisValue( self, axis ):
value = self.joystick.get_axis( axis )
return value
### initialisation
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
clock = pygame.time.Clock()
pygame.display.set_caption( "Any Joy?" )
# Talk to the Joystick
control = controller()
# Main loop
done = False
while not done:
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
# Query the Joystick
val = control.getAxisValue( 0 )
print( "Joystick Axis: " + str( val ) )
# Update the window, but not more than 60fps
window.fill( (0,0,0) )
pygame.display.flip()
clock.tick_busy_loop(60)
pygame.quit()

How to identify which button is being pressed on PS4 controller using pygame

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

Omxplayer fill mode not working

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.

pygame.key.set_repeat for Joystick

I'm building a menu using pygame and I want to make it navigable using a specific gamepad. Ideally I want to be able to press and hold *down" on the D-pad repeatedly, or get something like on a keyboard where the first button press has a delay before repeatedly entering the same character (seemingly).
I'm trying to emulate the pygame.key.set_repeat(...) function for a Joystick. my approach so far has been
pygame.time.set_timer(pygame.USEREVENT, 10)
DELAY_TIME = 0.250 #ms
y_delay = True
while not done:
for event in pygame.event.get():
y_axis = gamepad.get_axis(1)
if y_axis > 0.5: # pushing down
main_menu.move_down()
redraw() #redraw everything on the surface before sleeping
if y_delay:
time.sleep(DELAY_TIME)
y_delay = False #don't delay the next time the y axis is used
elif y_axis < -0.5: #pushing up
# repetitive I know, but I'm still working on it
main_menu.move_up()
redraw()
if y_delay:
time.sleep(DELAY_TIME)
y_delay = False
else:
y_delay = True # delay the next time
my issue is if someone taps up or down faster than DELAY_TIME they are limited to the DELAY_TIME before they can move again. Also if someone releases and depresses the up/down button within the time.sleep interval, python never sees that it was released at all and doesn't allow for a delay.
Maybe there's a way to do this using events or mapping the joystick to keys somehow? qjoypad doesn't cut it for me, and joy2keys is trash. I would need to do the mapping within the python program.
Sleep causes the program to halt execution, so it's not a viable option. You can also do this without using set_timer and events. I did it using a couple of flags and pygame.time's get_ticks.
import pygame
from pygame.locals import *
def main():
pygame.init()
pygame.display.set_mode((480, 360))
gamepad = pygame.joystick.Joystick(0)
gamepad.init()
delay = 1000
neutral = True
pressed = 0
last_update = pygame.time.get_ticks()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
move = False
if gamepad.get_axis(1) == 0:
neutral = True
pressed = 0
else:
if neutral:
move = True
neutral = False
else:
pressed += pygame.time.get_ticks() - last_update
if pressed > delay:
move = True
pressed -= delay
if move:
print "move"
last_update = pygame.time.get_ticks()
if __name__ == "__main__":
main()
pygame.quit()
When get_axis indicates no motion, the neutral flag is set, and the pressed timer is reset, causing the move flag to remain unset. When the neutral flag is unset, if it's newly set, the move flag is set. If it's not newly set, the pressed timer increases, and move is set only if the pressed timer is greater than delay.

Categories