mirror of
https://github.com/martinohanlon/XboxController.git
synced 2025-03-14 11:29:13 +01:00
Update XboxController.py
suport for python 3.7 and fixed some issues with ctrl+c
This commit is contained in:
parent
b0f1c7b5b6
commit
ee991cc9a0
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user