Skip to content

Instantly share code, notes, and snippets.

@robsears
Created October 23, 2012 04:27
Show Gist options
  • Save robsears/3936657 to your computer and use it in GitHub Desktop.
Save robsears/3936657 to your computer and use it in GitHub Desktop.
wxPython Joystick Example 5
# Import wx objects
import wx
# Taken from wxPython demos:
MAX_BUTTONS = 16 # This comes from a limit in Python dealing with binary numbers I guess. More info on line 209
haveJoystick = True
if wx.Platform == "__WXMAC__":
haveJoystick = False
# Create a few global variables so we can tweak alignment in the GUI window
X_LABEL = 10
X_QUANT = 120
Y = 10
LINE_HEIGHT = 15
def PrintJoystickMethod(self, label, val, attrns):
# Create a function to condense and clarify the code. This function takes a
# label and a value then positions them in the frame. The 'attrns' input
# makes the wx.StaticText object an attribute of the joystick object. For example,
# calling PrintJoystickMethod(self, 'Status: ', self.stick.IsOk(), 'status')
# is equivalent to running:
# 1. wx.StaticText(self, -1, 'Status: ', pos=(X_LABEL,Y))
# 2. self.stick.status=wx.StaticText(self, -1, str(self.stick.IsOk()), pos=(X_QUANT, Y))
# 3. Y = Y + LINE_HEIGHT
# This way we can condense three lines of code down to just one, and do it in a way that
# allows us to access and change the values later (like if the joystick is moved, button
# pressed, etc). Since this example covers around 40 methods, this function allows us to
# reduce the code from 120 lines to just 40, while maintaining a consistent visual layout.
global X_LABEL, X_QUANT, Y
wx.StaticText(self, -1, str(label), pos=(X_LABEL,Y))
setattr(self.stick, str(attrns), wx.StaticText(self, -1, str(val), pos=(X_QUANT,Y)))
Y = Y + LINE_HEIGHT
# The main GUI window:
class Example(wx.Frame):
def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)
global Y
# Simple function to create a new column of data
def NewColumn():
global X_LABEL, X_QUANT, Y
Y = 10
X_LABEL = X_QUANT + 75
X_QUANT = X_LABEL + 110
# Simple function to create a new row of data:
def NewRow():
global Y
Y = Y + LINE_HEIGHT
# Create Joystick object
self.stick = wx.Joystick()
self.stick.SetCapture(self)
# Print the product information to the screen:
joystick_status = {True: 'Good', False: 'Fail'}[self.stick.IsOk() == True] # Translate Boolean value to text. For more info, see: http://en.wikipedia.org/wiki/%3F:#Python
PrintJoystickMethod(self, 'Joystick Status:', joystick_status, 'status')
PrintJoystickMethod(self, 'Manufacturer ID:', self.stick.GetManufacturerId(), 'mid')
PrintJoystickMethod(self, 'Product ID:', self.stick.GetProductId(), 'pid')
# Get the joystick name:
joystick_name = "<None>"
try:
# This is placed in a try...except block to catch errors that sometimes occur.
# Depends on the joystick, drivers, registry, etc. May or may not work as expected.
# If it throws errors during testing, just comment out this line and uncomment the line below it
joystick_name = self.stick.GetProductName()
#joystick_name = 'YOUR JOYSTICK NAME'
except:
pass
PrintJoystickMethod(self, 'Product Name:', joystick_name, 'name')
# Print some physical properties of the joystick:
NewRow()
PrintJoystickMethod(self, 'Movement Threshold:', self.stick.GetMovementThreshold(), 'movethresh')
PrintJoystickMethod(self, 'Number of Axes:', self.stick.GetNumberAxes(), 'numaxes')
PrintJoystickMethod(self, 'Number of Buttons:', self.stick.GetNumberButtons(), 'numbuttons')
PrintJoystickMethod(self, 'Number of Joysticks:', self.stick.GetNumberJoysticks(), 'numjoysticks')
# Translate boolean results to Yes/No answers for some joystick properties:
has_rudder = {True: 'Yes', False: 'No'}[self.stick.HasRudder() == True]
has_u = {True: 'Yes', False: 'No'}[self.stick.HasU() == True]
has_v = {True: 'Yes', False: 'No'}[self.stick.HasV() == True]
has_z = {True: 'Yes', False: 'No'}[self.stick.HasZ() == True]
has_pov = {True: 'Yes', False: 'No'}[self.stick.HasPOV() == True]
has_pov4dir = {True: 'Yes', False: 'No'}[self.stick.HasPOV4Dir() == True]
has_povcts = {True: 'Yes', False: 'No'}[self.stick.HasPOVCTS() == True]
# Print details about the rudder (if applicable):
NewRow()
PrintJoystickMethod(self, 'Has Rudder?:', has_rudder, 'hasrudder')
if self.stick.HasRudder() == True:
PrintJoystickMethod(self, 'Rudder Max:', self.stick.GetRudderMax(), 'ruddermax')
PrintJoystickMethod(self, 'Rudder Min:', self.stick.GetRudderMin(), 'ruddermin')
PrintJoystickMethod(self, 'Rudder Position:', self.stick.GetRudderPosition(), 'rudderpos')
# Print details about the joystick's U-axis (if applicable):
NewRow()
PrintJoystickMethod(self, 'Has U?:', has_u, 'hasu')
if self.stick.HasU() == True:
PrintJoystickMethod(self, 'U Max:', self.stick.GetUMax(), 'umax')
PrintJoystickMethod(self, 'U Min:', self.stick.GetUMin(), 'umin')
PrintJoystickMethod(self, 'U Position:', self.stick.GetUPosition(), 'upos')
# Print details about the joystick's V-axis (if applicable):
NewRow()
PrintJoystickMethod(self, 'Has V?:', has_v, 'hasv')
if self.stick.HasV() == True:
PrintJoystickMethod(self, 'V Max:', self.stick.GetVMax(), 'vmax')
PrintJoystickMethod(self, 'V Min:', self.stick.GetVMin(), 'vmin')
PrintJoystickMethod(self, 'V Position:', self.stick.GetVPosition(), 'vpos')
# Print details about the joystick's Z-axis (if applicable):
NewRow()
PrintJoystickMethod(self, 'Has Z?:', has_z, 'hasz')
if self.stick.HasZ() == True:
PrintJoystickMethod(self, 'Z Max:', self.stick.GetZMax(), 'zmax')
PrintJoystickMethod(self, 'Z Min:', self.stick.GetZMin(), 'zmin')
PrintJoystickMethod(self, 'Z Position:', self.stick.GetZPosition(), 'zpos')
# Print details about the joystick's POV switch (if applicable):
NewColumn()
PrintJoystickMethod(self, 'Has POV?:', has_pov, 'haspov')
if self.stick.HasPOV() == True:
PrintJoystickMethod(self, 'POV Position:', self.stick.GetPOVPosition(), 'povpos')
# Does the joystick offer 4-directional POV switch?
# (forward, backward, left, and right):
NewRow()
PrintJoystickMethod(self, 'Has POV4Dir?:', has_pov4dir, 'haspov4dir')
# Print details about the joystick's POV switch's
# continuous degree bearings (if applicable):
NewRow()
PrintJoystickMethod(self, 'Has POVCTS?:', has_povcts, 'haspovcts')
if self.stick.HasPOVCTS() == True:
PrintJoystickMethod(self, 'POVCTS Position:', self.stick.GetPOVCTSPosition(), 'povctspos')
# Print details about the joystick's polling:
NewRow()
PrintJoystickMethod(self, 'Polling (max):', self.stick.GetPollingMax(), 'pollingmin')
PrintJoystickMethod(self, 'Polling (min):', self.stick.GetPollingMin(), 'pollingmax')
# Print details about the joystick's X-Y postion:
NewRow()
PrintJoystickMethod(self, '(X,Y) Position:', self.stick.GetPosition(), 'xyposition')
# Print details about the joystick's X-axis:
NewRow()
PrintJoystickMethod(self, 'X (max):', self.stick.GetXMax(), 'xmax')
PrintJoystickMethod(self, 'X (min):', self.stick.GetXMin(), 'xmin')
# Print details about the joystick's Y-axis:
NewRow()
PrintJoystickMethod(self, 'Y (max):', self.stick.GetYMax(), 'ymax')
PrintJoystickMethod(self, 'Y (min):', self.stick.GetYMin(), 'ymin')
# Print joystick button press bitlist. More info on this in line 209
NewRow()
PrintJoystickMethod(self, 'Bitlist:', self.stick.GetNumberButtons(), 'bitlist')
# Create an array attribute on the joystick object to hold a StaticText
# object for each button state. This approach forgoes the PrintJoystickMethod
# function to make the button states easier to access/modify later
self.stick.butns = {}
for i in range(self.stick.GetNumberButtons()):
wx.StaticText(self, -1, 'Button '+str(i+1)+': ', pos=(X_LABEL,Y))
self.stick.butns[i] = wx.StaticText(self, -1, 'Off', pos=(X_QUANT,Y))
Y = Y + LINE_HEIGHT
# Bind joystick actions to an updater function:
self.Bind(wx.EVT_JOYSTICK_EVENTS, self.OnJoystick) # If anything happens w/joystick, call OnJoystick
self.stick.SetMovementThreshold(1) # Sets the movement threshold, the number of steps outside which the joystick is deemed to have moved.
# Create some simple gauge widgets for the joystick positions
NewColumn()
wx.StaticText(self, -1, 'X-axis Gauge: ', pos=(X_LABEL,Y))
self.xpos = wx.Gauge(self, range=self.stick.GetXMax(), pos=(X_QUANT, Y), size=(150, 5))
wx.StaticText(self, -1, 'Y-axis Gauge: ', pos=(X_LABEL,Y+35))
self.ypos = wx.Gauge(self, range=self.stick.GetYMax(), pos=(X_QUANT, Y+35), size=(150, 5))
wx.StaticText(self, -1, 'Z-axis Gauge: ', pos=(X_LABEL,Y+70))
self.zpos = wx.Gauge(self, range=self.stick.GetZMax(), pos=(X_QUANT, Y+70), size=(150, 5))
wx.StaticText(self, -1, 'Rudder Gauge: ', pos=(X_LABEL,Y+105))
self.rpos = wx.Gauge(self, range=self.stick.GetRudderMax(), pos=(X_QUANT, Y+105), size=(150, 5))
self.SetSize((750, 575))
self.SetTitle('Joystick Info')
self.Centre()
self.Show(True)
def OnJoystick(self, e):
# Update the values for xy position, rudder position, z-axis position and the button bitlist.
# These are all values that can change. The number of buttons, axes, etc - values that are
# fixed - are not updated for that reason. You can add more values to update by simply adding
# a new line of the format self.stick.ATTRNS.SetLabel(str(self.stick.METHOD())) where ATTRNS
# is the same one defined for the value during initialization, and METHOD is the wxPython
# method for collecting the value.
self.stick.xyposition.SetLabel(str(self.stick.GetPosition()))
self.stick.rudderpos.SetLabel( str(self.stick.GetRudderPosition()))
self.stick.zpos.SetLabel( str(self.stick.GetZPosition()))
self.stick.bitlist.SetLabel( str(self.stick.GetButtonState()))
# Checking the joystick button states is one of the trickier things to do in this example.
# The state of each button is read as 1 (pressed) or 0 (not pressed). The GetButtonState()
# method does NOT return an array or list as one might expect (supposedly it used to - the
# docs are unclear about this). The current version of wxPython returns an integer representing
# the binary state of all the buttons. So, let's say that your joystick has ten buttons. When
# none are pressed, wxPython treats this as a 10-bit value of 0000000000, and the GetButtonState
# method returns an integer of 0. Now, let's say you press a button that the joystick treats
# as 'button 4'; then wxPython treats this as 0000001000 (remember that binary is read from
# right to left!!), and GetButtonState returns a value of 8 (which is the decimal representation
# of 0000001000). Let's say you press buttons 2, 5, and 8 on your ten button joystick, wxPython
# reads this as 0010010010 and GetButtonState returns a value of 146 (146 in decimal is 0010010010
# in binary). Make sense?
# In the for loop below, we iterate the variable i over the range of the number of joystick buttons
# (i=0..BUTTONS-1). The line that reads "if (t & (1<<i)):" (taken from the wxPython demo) does some
# pretty important things. It's extremely concise, yet expresses a TON of logic necessary to make
# this all work. We'll go through it now:
# The single ampersand is a binary AND operator. This is different from a logical AND operator in
# that it combines the binary representations of two values by copying a bit only if the
# corresponding bits of the two operands is 1. What does that mean? If we have two binary values,
# 0101 and 1110, then (0101 & 1110) evaluates to 0100 (binary) or 4 (dec). Note that the only digit in which
# both operands share a 1 is the 3rd place from the right, hence the result of 0100. Further,
# the << is a bitwise left shift operator, which takes the binary representation of the left operand
# and shifts it to the left by a number of places determined by the operand on the right. So "3<<2"
# means "take the binary value of 3 (0011) and shift everything two places to the left," and results
# in a value of 1100, or 12. In the for loop below, this simple conditional statement handles all
# of the logic necessary for determining which button was pressed.
# If we return to the example of a 10-button joystick where buttons 2, 5, and 8 are simultaneously
# pressed, we can build a truth table for all of the possibilities generated by the for loop:
# i t 1<<i (t & (1<<i)) Evaluates as Button Number
# 0 0010010010 0000000001 0000000000 = 0 FALSE 1
# 1 0010010010 0000000010 0000000010 = 2 TRUE 2
# 2 0010010010 0000000100 0000000000 = 0 FALSE 3
# 3 0010010010 0000001000 0000000000 = 0 FALSE 4
# 4 0010010010 0000010000 0000010000 = 16 TRUE 5
# 5 0010010010 0000100000 0000000000 = 0 FALSE 6
# 6 0010010010 0001000000 0000000000 = 0 FALSE 7
# 7 0010010010 0010000000 0010000000 = 128 TRUE 8
# 8 0010010010 0100000000 0000000000 = 0 FALSE 9
# 9 0010010010 1000000000 0000000000 = 0 FALSE 10
# So we can see that this little conditional statement within the for loop correctly returns TRUE
# for any and all buttons that are being pressed. Using this, and the button array we initialized
# on the joystick object, we can now update each value to indicate whether it is being pressed ("On")
# or not ("Off")
t = self.stick.GetButtonState()
for i in range(self.stick.GetNumberButtons()):
if (t & (1<<i)):
self.stick.butns[i].SetLabel('On')
else:
self.stick.butns[i].SetLabel('Off')
# Update the widgets:
newx, newy = self.stick.GetPosition()
self.xpos.SetValue(newx)
self.ypos.SetValue(newy)
self.zpos.SetValue(self.stick.GetZPosition())
self.rpos.SetValue(self.stick.GetRudderPosition())
def main():
if haveJoystick == False:
print 'wx.Joystick is not available on this platform.'
return
else:
ex = wx.App()
Example(None)
ex.MainLoop()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment