diff --git a/XboxController.py b/XboxController.py index d3d76d2..f9be1a8 100755 --- a/XboxController.py +++ b/XboxController.py @@ -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: