1
0
mirror of https://github.com/martinohanlon/XboxController.git synced 2025-03-14 11:29:13 +01:00

Merge ee991cc9a0e53fc105b2221e6025ef7ce7b2a699 into 2a8e6e715efa7c377480e2c4fab8915f308f0eb6

This commit is contained in:
Wladymir Brborich 2020-09-13 13:00:54 -04:00 committed by GitHub
commit 4e3b175c70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,18 +1,18 @@
#Martin O'Hanlon
#www.stuffaboutcode.com
#A class for reading values from an xbox controller
# Martin O'Hanlon
# www.stuffaboutcode.com
# A class for reading values from an xbox controller
# uses xboxdrv and pygame
# xboxdrv should already be running
import pygame
from pygame.locals import *
import os, sys
import os
import sys
import threading
import time
"""
NOTES - pygame events and values
JOYAXISMOTION
event.axis event.value
0 - x axis left thumb (+1 is right, -1 is left)
@ -21,7 +21,6 @@ event.axis event.value
3 - y axis right thumb (+1 is down, -1 is up)
4 - right trigger
5 - left trigger
JOYBUTTONDOWN | JOYBUTTONUP
event.button
A = 0
@ -35,7 +34,6 @@ START = 7
XBOX = 8
LEFTTHUMB = 9
RIGHTTHUMB = 10
JOYHATMOTION
event.value
[0] - horizontal
@ -46,12 +44,13 @@ event.value
[1].0 - middle
[1].-1 - bottom
[1].+1 - top
"""
#Main class for reading the xbox controller values
# Main class for reading the xbox controller values
class XboxController(threading.Thread):
#internal ids for the xbox controls
# internal ids for the xbox controls
class XboxControls():
LTHUMBX = 0
LTHUMBY = 1
@ -72,7 +71,7 @@ class XboxController(threading.Thread):
RIGHTTHUMB = 16
DPAD = 17
#pygame axis constants for the analogue controls of the xbox controller
# pygame axis constants for the analogue controls of the xbox controller
class PyGameAxis():
LTHUMBX = 0
LTHUMBY = 1
@ -81,7 +80,7 @@ class XboxController(threading.Thread):
RTRIGGER = 4
LTRIGGER = 5
#pygame constants for the buttons of the xbox controller
# pygame constants for the buttons of the xbox controller
class PyGameButtons():
A = 0
B = 1
@ -95,17 +94,17 @@ class XboxController(threading.Thread):
LEFTTHUMB = 9
RIGHTTHUMB = 10
#map between pygame axis (analogue stick) ids and xbox control ids
# map between pygame axis (analogue stick) ids and xbox control ids
AXISCONTROLMAP = {PyGameAxis.LTHUMBX: XboxControls.LTHUMBX,
PyGameAxis.LTHUMBY: XboxControls.LTHUMBY,
PyGameAxis.RTHUMBX: XboxControls.RTHUMBX,
PyGameAxis.RTHUMBY: XboxControls.RTHUMBY}
#map between pygame axis (trigger) ids and xbox control ids
# map between pygame axis (trigger) ids and xbox control ids
TRIGGERCONTROLMAP = {PyGameAxis.RTRIGGER: XboxControls.RTRIGGER,
PyGameAxis.LTRIGGER: XboxControls.LTRIGGER}
#map between pygame buttons ids and xbox contorl ids
# map between pygame buttons ids and xbox contorl ids
BUTTONCONTROLMAP = {PyGameButtons.A: XboxControls.A,
PyGameButtons.B: XboxControls.B,
PyGameButtons.X: XboxControls.X,
@ -117,19 +116,19 @@ class XboxController(threading.Thread):
PyGameButtons.XBOX: XboxControls.XBOX,
PyGameButtons.LEFTTHUMB: XboxControls.LEFTTHUMB,
PyGameButtons.RIGHTTHUMB: XboxControls.RIGHTTHUMB}
#setup xbox controller class
def __init__(self,
controllerCallBack = None,
joystickNo = 0,
deadzone = 0.1,
scale = 1,
invertYAxis = False):
#setup threading
# setup xbox controller class
def __init__(self,
controllerCallBack=None,
joystickNo=0,
deadzone=0.1,
scale=1,
invertYAxis=False):
# setup threading
threading.Thread.__init__(self)
#persist values
# persist values
self.running = False
self.controllerCallBack = controllerCallBack
self.joystickNo = joystickNo
@ -139,30 +138,30 @@ class XboxController(threading.Thread):
self.invertYAxis = invertYAxis
self.controlCallbacks = {}
#setup controller properties
self.controlValues = {self.XboxControls.LTHUMBX:0,
self.XboxControls.LTHUMBY:0,
self.XboxControls.RTHUMBX:0,
self.XboxControls.RTHUMBY:0,
self.XboxControls.RTRIGGER:0,
self.XboxControls.LTRIGGER:0,
self.XboxControls.A:0,
self.XboxControls.B:0,
self.XboxControls.X:0,
self.XboxControls.Y:0,
self.XboxControls.LB:0,
self.XboxControls.RB:0,
self.XboxControls.BACK:0,
self.XboxControls.START:0,
self.XboxControls.XBOX:0,
self.XboxControls.LEFTTHUMB:0,
self.XboxControls.RIGHTTHUMB:0,
self.XboxControls.DPAD:(0,0)}
# setup controller properties
self.controlValues = {self.XboxControls.LTHUMBX: 0,
self.XboxControls.LTHUMBY: 0,
self.XboxControls.RTHUMBX: 0,
self.XboxControls.RTHUMBY: 0,
self.XboxControls.RTRIGGER: 0,
self.XboxControls.LTRIGGER: 0,
self.XboxControls.A: 0,
self.XboxControls.B: 0,
self.XboxControls.X: 0,
self.XboxControls.Y: 0,
self.XboxControls.LB: 0,
self.XboxControls.RB: 0,
self.XboxControls.BACK: 0,
self.XboxControls.START: 0,
self.XboxControls.XBOX: 0,
self.XboxControls.LEFTTHUMB: 0,
self.XboxControls.RIGHTTHUMB: 0,
self.XboxControls.DPAD: (0, 0)}
#setup pygame
# setup pygame
self._setupPygame(joystickNo)
#Create controller properties
# Create controller properties
@property
def LTHUMBX(self):
return self.controlValues[self.XboxControls.LTHUMBX]
@ -235,7 +234,7 @@ class XboxController(threading.Thread):
def DPAD(self):
return self.controlValues[self.XboxControls.DPAD]
#setup pygame
# setup pygame
def _setupPygame(self, joystickNo):
# set SDL to use the dummy NULL video driver, so it doesn't need a windowing system.
os.environ["SDL_VIDEODRIVER"] = "dummy"
@ -246,100 +245,107 @@ class XboxController(threading.Thread):
# init the joystick control
pygame.joystick.init()
# how many joysticks are there
#print pygame.joystick.get_count()
# print pygame.joystick.get_count()
# get the first joystick
joy = pygame.joystick.Joystick(joystickNo)
# init that joystick
joy.init()
#called by the thread
# called by the thread
def run(self):
self._start()
#start the controller
# start the controller
def _start(self):
self.running = True
#run until the controller is stopped
# run until the controller is stopped
while(self.running):
#react to the pygame events that come from the xbox controller
# react to the pygame events that come from the xbox controller
for event in pygame.event.get():
#thumb sticks, trigger buttons
if event.type == pygame.QUIT:
print("Received event 'Quit', exiting.")
self.stop()
# thumb sticks, trigger buttons
if event.type == JOYAXISMOTION:
#is this axis on our xbox controller
# is this axis on our xbox controller
if event.axis in self.AXISCONTROLMAP:
#is this a y axis
yAxis = True if (event.axis == self.PyGameAxis.LTHUMBY or event.axis == self.PyGameAxis.RTHUMBY) else False
#update the control value
# is this a y axis
yAxis = True if (
event.axis == self.PyGameAxis.LTHUMBY or event.axis == self.PyGameAxis.RTHUMBY) else False
# update the control value
self.updateControlValue(self.AXISCONTROLMAP[event.axis],
self._sortOutAxisValue(event.value, yAxis))
#is this axis a trigger
# is this axis a trigger
if event.axis in self.TRIGGERCONTROLMAP:
#update the control value
# update the control value
self.updateControlValue(self.TRIGGERCONTROLMAP[event.axis],
self._sortOutTriggerValue(event.value))
#d pad
elif event.type == JOYHATMOTION:
#update control value
self.updateControlValue(self.XboxControls.DPAD, event.value)
#button pressed and unpressed
# d pad
elif event.type == JOYHATMOTION:
# update control value
self.updateControlValue(
self.XboxControls.DPAD, event.value)
# button pressed and unpressed
elif event.type == JOYBUTTONUP or event.type == JOYBUTTONDOWN:
#is this button on our xbox controller
# is this button on our xbox controller
if event.button in self.BUTTONCONTROLMAP:
#update control value
# update control value
self.updateControlValue(self.BUTTONCONTROLMAP[event.button],
self._sortOutButtonValue(event.type))
#stops the controller
# stops the controller
def stop(self):
self.running = False
#updates a specific value in the control dictionary
# updates a specific value in the control dictionary
def updateControlValue(self, control, value):
#if the value has changed update it and call the callbacks
# if the value has changed update it and call the callbacks
if self.controlValues[control] != value:
self.controlValues[control] = value
self.doCallBacks(control, value)
#calls the call backs if necessary
def doCallBacks(self, control, value):
#call the general callback
if self.controllerCallBack != None: self.controllerCallBack(control, value)
#has a specific callback been setup?
# calls the call backs if necessary
def doCallBacks(self, control, value):
# call the general callback
if self.controllerCallBack != None:
self.controllerCallBack(control, value)
# has a specific callback been setup?
if control in self.controlCallbacks:
self.controlCallbacks[control](value)
#used to add a specific callback to a control
# used to add a specific callback to a control
def setupControlCallback(self, control, callbackFunction):
# add callback to the dictionary
self.controlCallbacks[control] = callbackFunction
#scales the axis values, applies the deadzone
def _sortOutAxisValue(self, value, yAxis = False):
#invert yAxis
if yAxis and self.invertYAxis: value = value * -1
#scale the value
# scales the axis values, applies the deadzone
def _sortOutAxisValue(self, value, yAxis=False):
# invert yAxis
if yAxis and self.invertYAxis:
value = value * -1
# scale the value
value = value * self.scale
#apply the deadzone
if value < self.upperDeadzone and value > self.lowerDeadzone: value = 0
# apply the deadzone
if value < self.upperDeadzone and value > self.lowerDeadzone:
value = 0
return value
#turns the trigger value into something sensible and scales it
# turns the trigger value into something sensible and scales it
def _sortOutTriggerValue(self, value):
#trigger goes -1 to 1 (-1 is off, 1 is full on, half is 0) - I want this to be 0 - 1
value = max(0,(value + 1) / 2)
#scale the value
# trigger goes -1 to 1 (-1 is off, 1 is full on, half is 0) - I want this to be 0 - 1
value = max(0, (value + 1) / 2)
# scale the value
value = value * self.scale
return value
#turns the event type (up/down) into a value
# turns the event type (up/down) into a value
def _sortOutButtonValue(self, eventType):
#if the button is down its 1, if the button is up its 0
# if the button is down its 1, if the button is up its 0
value = 1 if eventType == JOYBUTTONDOWN else 0
return value
@ -348,13 +354,13 @@ if __name__ == '__main__':
#generic call back
def controlCallBack(xboxControlId, value):
print "Control Id = {}, Value = {}".format(xboxControlId, value)
print ("Control Id = {}, Value = {}".format(xboxControlId, value))
#specific callbacks for the left thumb (X & Y)
def leftThumbX(xValue):
print "LX {}".format(xValue)
print ("LX {}".format(xValue))
def leftThumbY(yValue):
print "LY {}".format(yValue)
print ("LY {}".format(yValue))
#setup xbox controller, set out the deadzone and scale, also invert the Y Axis (for some reason in Pygame negative is up - wierd!
xboxCont = XboxController(controlCallBack, deadzone = 30, scale = 100, invertYAxis = True)
@ -366,17 +372,15 @@ if __name__ == '__main__':
try:
#start the controller
xboxCont.start()
print "xbox controller running"
while True:
time.sleep(1)
print ("xbox controller running")
#Ctrl C
except KeyboardInterrupt:
print "User cancelled"
except KeyboardInterrupt:
print ("User cancelled")
#error
except:
print "Unexpected error:", sys.exc_info()[0]
print ("Unexpected error:", sys.exc_info()[0])
raise
finally: