# Author: Jeff Grimmett (grimmtoo@softhome.net), adapted from original
# .wdr-derived demo
#
-# Created: 01/02/04
+# Created: 02-Jan-2004
# RCS-ID: $Id$
# Copyright:
# Licence: wxWindows license
import math
import wx
+haveJoystick = True
+if wx.Platform == "__WXMAC__":
+ haveJoystick = False
+
#----------------------------------------------------------------------------
-# For convenience
-spacer = (10, 10)
+# Once all supported versions of Python support 32-bit integers on all
+# platforms, this can go up to 32.
MAX_BUTTONS = 16
#----------------------------------------------------------------------------
# Restrict our drawing activities to the square defined
# above.
- dc.SetClippingRegion((xorigin, yorigin), (edgeSize, edgeSize))
+ dc.SetClippingRegion(xorigin, yorigin, edgeSize, edgeSize)
# Optimize drawing a bit (for Win)
dc.BeginDrawing()
dc.SetBrush(wx.Brush(wx.Colour(251, 252, 237)))
- dc.DrawRectangle((xorigin, yorigin), (edgeSize, edgeSize))
+ dc.DrawRectangle(xorigin, yorigin, edgeSize, edgeSize)
dc.SetPen(wx.Pen(wx.BLACK, 1, wx.DOT_DASH))
- dc.DrawLine((xorigin, yorigin + center), (xorigin + edgeSize, yorigin + center))
- dc.DrawLine((xorigin + center, yorigin), (xorigin + center, yorigin + edgeSize))
+ dc.DrawLine(xorigin, yorigin + center, xorigin + edgeSize, yorigin + center)
+ dc.DrawLine(xorigin + center, yorigin, xorigin + center, yorigin + edgeSize)
if self.stick:
# Get the joystick position as a float
joyy = float(self.stick.GetPosition().y)
# Get the joystick range of motion
- xrange = self.stick.GetXMax() - self.stick.GetXMin()
- yrange = self.stick.GetYMax() - self.stick.GetYMin()
+ xmin = self.stick.GetXMin()
+ xmax = self.stick.GetXMax()
+ if xmin < 0:
+ xmax += abs(xmin)
+ joyx += abs(xmin)
+ xmin = 0
+ xrange = max(xmax - xmin, 1)
+
+ ymin = self.stick.GetYMin()
+ ymax = self.stick.GetYMax()
+ if ymin < 0:
+ ymax += abs(ymin)
+ joyy += abs(ymin)
+ ymin = 0
+ yrange = max(ymax - ymin, 1)
# calc a ratio of our range versus the joystick range
xratio = float(edgeSize) / xrange
# calc the displayable value based on position times ratio
xval = int(joyx * xratio)
- yval = int(joyy * xratio)
+ yval = int(joyy * yratio)
# and normalize the value from our brush's origin
x = xval + xorigin
# Now to draw it.
dc.SetPen(wx.Pen(wx.RED, 2))
- dc.CrossHair((x, y))
+ dc.CrossHair(x, y)
# Turn off drawing optimization
dc.EndDrawing()
# our 'raster'.
dc.SetBrush(wx.Brush(wx.WHITE))
- dc.DrawCircle((xcenter, ycenter), diameter/2)
+ dc.DrawCircle(xcenter, ycenter, diameter/2)
dc.SetBrush(wx.Brush(wx.BLACK))
- dc.DrawCircle((xcenter, ycenter), 10)
+ dc.DrawCircle(xcenter, ycenter, 10)
# fancy decorations
dc.SetPen(wx.Pen(wx.BLACK, 1, wx.DOT_DASH))
- dc.DrawLine((xorigin, ycenter), (xorigin + diameter, ycenter))
- dc.DrawLine((xcenter, yorigin), (xcenter, yorigin + diameter))
+ dc.DrawLine(xorigin, ycenter, xorigin + diameter, ycenter)
+ dc.DrawLine(xcenter, yorigin, xcenter, yorigin + diameter)
if self.stick:
if self.avail:
# Draw the line
dc.SetPen(wx.Pen(wx.BLUE, 2))
- dc.DrawLine((xcenter, ycenter), (nx, ny))
+ dc.DrawLine(xcenter, ycenter, nx, ny)
# And a little thing to show the endpoint
dc.SetBrush(wx.Brush(wx.BLUE))
- dc.DrawCircle((nx, ny), 8)
+ dc.DrawCircle(nx, ny, 8)
# Turn off drawing optimization
dc.EndDrawing()
else:
dc.SetBrush(wx.Brush(wx.BLACK))
- dc.DrawCircle((center, center), bw/2)
+ dc.DrawCircle(center, center, bw/2)
txt = str(self.number)
# functions. The pseudo-shadow gives the text contrast
# regardless of whether the bar is under it or not.
dc.SetTextForeground(wx.WHITE)
- dc.DrawText(txt, (tx, ty))
+ dc.DrawText(txt, tx, ty)
# Turn off drawing optimization
dc.EndDrawing()
# functions. The pseudo-shadow gives the text contrast
# regardless of whether the bar is under it or not.
dc.SetTextForeground(wx.BLACK)
- dc.DrawText(txt, (tx, ty))
+ dc.DrawText(txt, tx, ty)
dc.SetTextForeground('white')
- dc.DrawText(txt, (tx-1, ty-1))
+ dc.DrawText(txt, tx-1, ty-1)
#----------------------------------------------------------------------------
self.GetMax = eval('stick.Get%sMax' % token)
# Create our displays and set them up.
- self.Min = wx.StaticText(self, -1, str(self.GetMin()),
- size=(40,-1), style=wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE)
- self.Max = wx.StaticText(self, -1, str(self.GetMax()),
- size=(40,-1), style=wx.ALIGN_LEFT | wx.ST_NO_AUTORESIZE)
+ self.Min = wx.StaticText(self, -1, str(self.GetMin()), style=wx.ALIGN_RIGHT)
+ self.Max = wx.StaticText(self, -1, str(self.GetMax()), style=wx.ALIGN_LEFT)
self.bar = AxisBar(self)
sizer.Add(self.Min, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 1)
else:
val = eval('self.stick.Get%sPosition()' % self.token)
+
#
# While we might be able to rely on a range of 0-FFFFFF on Win, that might
# not be true of all drivers on all platforms. Thus, calc the actual full
# range first.
#
+ if min < 0:
+ max += abs(min)
+ val += abs(min)
+ min = 0
range = float(max - min)
-
+
#
# The relative value is used by the derived wx.Gauge since it is a
# positive-only control.
#
relative = 0
if range:
- relative = int(val / range * 1000)
+ relative = int( val / range * 1000)
#
# Pass both the raw and relative values to the derived Gauge
# Capture Joystick events (if they happen)
self.Bind(wx.EVT_JOYSTICK_EVENTS, self.OnJoystick)
-
self.stick.SetMovementThreshold(10)
+
def Calibrate(self, evt=None):
# Do not try this without a stick
if not self.stick:
self.pov.Calibrate()
self.buttons.Calibrate()
+
def OnJoystick(self, evt=None):
if not self.stick:
return
self.axes.Update()
self.joy.Update()
self.pov.Update()
- self.buttons.Update()
+ if evt is not None and evt.IsButton():
+ self.buttons.Update()
+ def ShutdownDemo(self):
+ if self.stick:
+ self.stick.ReleaseCapture()
+ self.stick = None
+
#----------------------------------------------------------------------------
def runTest(frame, nb, log):
- win = JoystickDemoPanel(nb, log)
- return win
+ if haveJoystick:
+ win = JoystickDemoPanel(nb, log)
+ return win
+ else:
+ from Main import MessagePanel
+ win = MessagePanel(nb, 'wx.Joystick is not available on this platform.',
+ 'Sorry', wx.ICON_WARNING)
+ return win
+
#----------------------------------------------------------------------------
overview = """\
-
-
+<html>
+<body>
+<h1>wx.Joystick</h1>
+This demo illustrates the use of the wx.Joystick class, which is an interface to
+one or more joysticks attached to your system.
+
+<p>The data that can be retrieved from the joystick comes in four basic flavors.
+All of these are illustrated in the demo. In fact, this demo illustrates everything
+you <b>can</b> get from the wx.Joystick control.
+
+<ul>
+<li>Static information such as Manufacturer ID and model name,
+<li>Analog input from up to six axes, including X and Y for the actual stick,
+<li>Button input from the fire button and any other buttons that the stick has,
+<li>and the POV control (a kind of mini-joystick on top of the joystick) that many sticks come with.
+</ul>
+
+<p>Getting data from the joystick can be event-driven thanks to four event types associated
+with wx.JoystickEvent, or the joystick can be polled programatically to get data on
+a regular basis.
+
+<h2>Data types</h2>
+
+Data from the joystick comes in two flavors: that which defines the boundaries, and that
+which defines the current state of the stick. Thus, we have Get*Max() and Get*Min()
+methods for all axes, the max number of axes, the max number of buttons, and so on. In
+general, this data can be read once and stored to speed computation up.
+
+<h3>Analog Input</h3>
+
+Analog input (the axes) is delivered as a whole, positive number. If you need to know
+if the axis is at zero (centered) or not, you will first have to calculate that center
+based on the max and min values. The demo shows a bar graph for each axis expressed
+in native numerical format, plus a 'centered' X-Y axis compass showing the relationship
+of that input to the calculated stick position.
+
+Analog input may be jumpy and spurious, so the control has a means of 'smoothing' the
+analog data by setting a movement threshold. This demo sets the threshold to 10, but
+you can set it at any valid value between the min and max.
+
+<h3>Button Input</h3>
+
+Button state is retrieved as one int that contains each button state mapped to a bit.
+You get the state of a button by AND-ing its bit against the returned value, in the form
+
+<pre>
+ # assume buttonState is what the stick returned, and buttonBit
+ # is the bit you want to examine
+
+ if (buttonState & ( 1 << buttonBit )) :
+ # button pressed, do something with it
+</pre>
+
+<p>The problem here is that some OSs return a 32-bit value for up to 32 buttons
+(imagine <i>that</i> stick!). Python V2.3 will generate an exception for bit
+values over 30. For that reason, this demo is limited to 16 buttons.
+
+<p>Note that more than one button can be pressed at a time, so be sure to check all of them!
+
+
+<h3>POV Input</h3>
+
+POV hats come in two flavors: four-way, and continuous. four-way POVs are restricted to
+the cardinal points of the compass; continuous, or CTS POV hats can deliver input in
+.01 degree increments, theoreticaly. The data is returned as a whole number; the last
+two digits are considered to be to the right of the decimal point, so in order to
+use this information, you need to divide by 100 right off the bat.
+
+<p>Different methods are provided to retrieve the POV data for a CTS hat
+versus a four-way hat.
+
+<h2>Caveats</h2>
+
+The wx.Joystick control is in many ways incomplete at the C++ library level, but it is
+not insurmountable. In short, while the joystick interface <i>can</i> be event-driven,
+the wx.JoystickEvent class lacks event binders for all event types. Thus, you cannot
+rely on wx.JoystickEvents to tell you when something has changed, necessarilly.
+
+<ul>
+<li>There are no events associated with the POV control.
+<li>There are no events associated with the Rudder
+<li>There are no events associated with the U and V axes.
+</ul>
+
+<p>Fortunately, there is an easy workaround. In the top level frame, create a wx.Timer
+that will poll the stick at a set interval. Of course, if you do this, you might as
+well forgo catching wxEVT_JOYSTICK_* events at all and rely on the timer to do the
+polling.
+
+<p>Ideally, the timer should be a one-shot; after it fires, collect and process data as
+needed, then re-start the timer, possibly using wx.CallAfter().
+
+</body>
+</html>
"""
#----------------------------------------------------------------------------
if __name__ == '__main__':
import sys,os
import run
- run.main(['', os.path.basename(sys.argv[0])])
+ run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])