Created
October 23, 2012 04:27
-
-
Save robsears/3936657 to your computer and use it in GitHub Desktop.
wxPython Joystick Example 5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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