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)
Related
This program is an etchasketch program that uses turtle and I'm trying to figure out how to keep the turtle going without holding down the keys for the direction of the turtle.
from turtle import *
import sys
import keyboard
PIXEL_MOVE = 20
def main():
running = True
pen_down = False
keep_moving = True
print("This is lab 7")
screen_width = int(sys.argv[1])
screen_height = int(sys.argv[2])
#setting the size and color of the backround and turtle
screensize(screen_width, screen_height)
tobject = Turtle("turtle")
tobject.up()
while running:
tobject.forward(PIXEL_MOVE)
event = keyboard.read_event()
if event.event_type == keyboard.KEY_DOWN:
if event.name == 'q':
running = False
elif event.name == "p":
if pen_down == False:
pen_down = True
tobject.down()
else:
pen_down == False
tobject.up()
elif event.name == 'w':
tobject.setheading(90)
elif event.name == 'a':
tobject.setheading(180)
elif event.name == 's':
tobject.setheading(270)
elif event.name == 'd':
tobject.setheading(0)
main()
I got it to keep going in the direction if the key is being held down and pressed. But I want it to be able to move with just a keystroke and keep moving in that direction.
One way to do this is to store the last pressed key or last moved direction in a variable.
lastPressed = None
while running:
# Code before reading event
if event.event_type == keyboard.KEY_DOWN:
lastPressed = event.name
if lastPressed:
# Check if "w", "a", "s", "d", etc. and move
Hope this helps in some way :)
I am trying to write a simple interface which turns blue whenever a user selects it and turns white whenever it is unselected via the mouse. I am unsure why my solution does not work. (The function is being fired correctly). i believe the if statement is at fault.
def ColorChange(event):
if event.type == "Enter":
FirstEntry.configure(bg="lightblue")
elif event.type == "Leave":
FirstEntry.configure(bg="white")
else:
pass
#Entry Boxes
FirstEntry=tk.Entry(interface,textvariable=inputedData,font=labelFont)
FirstEntry.grid(row=2,column=2,columnspan=2)
FirstEntry.bind("<Enter>",ColorChange)
FirstEntry.bind("<Leave>",ColorChange)
Full Program
import tkinter as tk
from tkinter.font import Font
root = tk.Tk()
root.title("Basic Interface")
interface=tk.Frame(root)
interface.configure(bg='white')
interface.grid(row=0, column=0,sticky='news')
interface.tkraise()
#Tkinter Variables
inputedData= tk.StringVar()
outputData= tk.StringVar()
#Define Functions
def Process(event):
getData=inputedData.get()
print(getData)
outputData.set(getData)
print(event.time)
print(event.char)
print(event.type)
print(event.widget)
print(event.x)
print(event.y)
def ColorChange(event):
if event.type == "Enter":
FirstEntry.configure(bg="lightblue")
elif event.type == "Leave":
FirstEntry.configure(bg="white")
else:
pass
#Fonts
titleFont = Font(family="Arial", size="48")
labelFont = Font(family="Arial", size="24")
buttonFont = Font(family="Arial",size = "20")
#Labels
titleLabel=tk.Label(interface,text="Interface Title",fg="black",font=titleFont,bg='white')
titleLabel.grid(row=1,column=1,columnspan=5)
inputLabel=tk.Label(interface,text="Input Data: ",fg="black",font=labelFont,bg='white')
inputLabel.grid(row=2,column=1)
oLabel=tk.Label(interface,text="Output Data: ",fg="black",font=labelFont,bg='white')
oLabel.grid(row=3,column=1)
outputLabel=tk.Label(interface,textvariable=outputData,fg="black",font=labelFont,bg='white')
outputLabel.grid(row=3,column=2)
#Entry Boxes
FirstEntry=tk.Entry(interface,textvariable=inputedData,font=labelFont)
FirstEntry.grid(row=2,column=2,columnspan=2)
FirstEntry.bind("<Enter>",ColorChange)
FirstEntry.bind("<Leave>",ColorChange)
#Buttons
processButton=tk.Button(interface,text="Process",fg="black",font=buttonFont,bg='white')
processButton.bind("<Leave>",Process)
processButton.grid(row=4,column=2)
root.mainloop()
For those asking for a reproducible example, here it is
The event.type is returning a tkinter.EventType, not a string directly,
it looks like <EventType.Enter: '7'>.
There are two ways to get it to work:
Change the EventType key to a string:
def ColorChange(event):
if str(event.type) == "Enter":
FirstEntry.configure(bg="lightblue")
elif str(event.type) == "Leave":
FirstEntry.configure(bg="white")
else:
pass
Or use the tkinter.EventType as the value:
def ColorChange(event):
if event.type == tk.EventType.Enter:
FirstEntry.configure(bg="lightblue")
elif event.type == tk.EventType.Leave:
FirstEntry.configure(bg="white")
else:
pass
Either will work:
import tkinter as tk
def ColorChange(event):
if str(event.type) == "Enter":
FirstEntry.configure(bg="lightblue")
elif str(event.type) == "Leave":
FirstEntry.configure(bg="white")
else:
pass
interface = tk.Tk()
inputedData = tk.StringVar()
#Entry Boxes
FirstEntry=tk.Entry(interface,textvariable=inputedData) # ,font=labelFont)
FirstEntry.grid(row=2,column=2,columnspan=2)
FirstEntry.bind("<Enter>",ColorChange)
FirstEntry.bind("<Leave>",ColorChange)
interface.mainloop()
Apparently the event.type is of type <enum 'EventType'> which is not a stright up string, so the equality will fail. Instead try:
if str(event.type) == 'Enter':
or
if int(event.type) == 7:
Just a note, Python 3.8.7 (release notes) has changed how str() works on tkinter event types:
bpo-41831: str() for the type attribute of the tkinter.Event object always returns now the numeric code returned by Tk instead of the name of the event type.
tgikal's solution works even with Python 3.8.7:
if event.type == tk.EventType.Enter:
# Do something
elif event.type == tk.EventType.Leave:
# Do something else
else:
pass
Also, figbeam's solution above works too, if you compare event.type to a string version of the event numeric code or cast event.type to an int, but these are not as intuitive:
# Compare string version
if event.type == "7":
# Do something
elif event.type == "8":
# Do something else
else:
pass
# Compare integer version
if int(event.type) == 7:
# Do something
elif int(event.type) == 8:
# Do something else
else:
pass
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.
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.