+ #----------------------------------------------------------------------------
+
+ sizer.Add(Label(self, 'Polling -- '), (2, 3), (1, 1), wx.ALL | wx.GROW, 2)
+
+ sizer.Add(Label(self, 'Min: '), (2, 4), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_RIGHT, 2)
+ self.PollMin = wx.TextCtrl(self, -1, value='', size=(45, -1), style=wx.TE_READONLY)
+ sizer.Add(self.PollMin, (2, 5), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_LEFT, 2)
+
+ sizer.Add(Label(self, 'Max: '), (2, 6), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_RIGHT, 2)
+ self.PollMax = wx.TextCtrl(self, -1, value='', size=(45, -1), style=wx.TE_READONLY)
+ sizer.Add(self.PollMax, (2, 7), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_LEFT, 2)
+
+ #----------------------------------------------------------------------------
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+
+ def Calibrate(self):
+ if not self.stick:
+ return
+
+ s = self.stick
+
+ self.MfgID.SetValue(str(s.GetManufacturerId()))
+ self.ProdName.SetValue(str(s.GetProductName()))
+ self.Threshold.SetValue(str(s.GetMovementThreshold()))
+ self.NumJoysticks.SetValue(str(s.GetNumberJoysticks()))
+ self.NumAxis.SetValue(str(s.GetNumberAxes()))
+ self.MaxAxis.SetValue(str(s.GetMaxAxes()))
+ self.PollMin.SetValue(str(s.GetPollingMin()))
+ self.PollMax.SetValue(str(s.GetPollingMax()))
+
+
+#----------------------------------------------------------------------------
+
+class AxisBar(wx.Gauge):
+ #
+ # This class allows us to use a wx.Gauge to display the axis value
+ # with a fancy label overlayed onto the guage itself. Two values are
+ # used to do things: first of all, since the gauge is limited to
+ # positive numbers, the scale is fixed at 0 to 1000. We will receive
+ # an adjusted value to use to render the gauge itself. The other value
+ # is a raw value and actually reflects the value from the joystick itself,
+ # which is then drawn over the gauge.
+ #
+ def __init__(self, parent):
+ wx.Gauge.__init__(self, parent, -1, 1000, size=(-1, 20), style = wx.GA_HORIZONTAL | wx.GA_SMOOTH )
+
+ # This is the value we will display.
+ self.rawvalue = 0
+
+ self.SetBackgroundColour('light blue')
+ self.SetForegroundColour('orange')
+
+ # Capture paint events for purpose of updating
+ # the displayed value.
+ self.Bind(wx.EVT_PAINT, self.onPaint)
+
+ def Update(self, value, rawvalue):
+ # Updates the gauge itself, sets the raw value for
+ # the next EVT_PAINT
+ self.SetValue(value)
+ self.rawvalue = rawvalue
+
+ def onPaint(self, evt):
+ # Must always create a PaintDC when capturing
+ # an EVT_PAINT event
+ self.ShowValue(wx.PaintDC(self), evt)
+
+ def ShowValue(self, dc, evt):
+ # This method handles actual painting of and drawing
+ # on the gauge.
+
+ # Clear out the gauge
+ dc.Clear()
+ # and then carry out business as usual
+ wx.Gauge.OnPaint(self, evt)
+
+ # This is the size available to us.
+ w, h = dc.GetSize()
+
+ # This is what we will overlay on the gauge.
+ # It reflects the actual value received from the
+ # wx.Joystick.
+ txt = str(self.rawvalue)
+
+ # Copy the default font, make it bold.
+ fn = wx.Font(
+ self.GetFont().GetPointSize(),
+ self.GetFont().GetFamily(),
+ self.GetFont().GetStyle(),
+ wx.BOLD
+ )
+
+ # Set the font for the DC ...
+ dc.SetFont(fn)
+ # ... and calculate how much space our value
+ # will take up.
+ fw, fh = dc.GetTextExtent(txt)
+
+ # Calc the center of the gauge, and from that
+ # derive the origin of our value.
+ center = w / 2
+ tx = center - (fw/2)
+
+ center = h / 2
+ ty = center - (fh/2)
+
+ # I draw the value twice so as to give it a pseudo-shadow.
+ # This is (mostly) because I'm too lazy to figure out how
+ # to blit my text onto the gauge using one of the logical
+ # 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.SetTextForeground('white')
+ dc.DrawText(txt, (tx-1, ty-1))
+
+
+#----------------------------------------------------------------------------
+
+class Axis(wx.Panel):
+ #
+ # This class is a container for the min, max, and current
+ # values of the joystick axis in question. It contains
+ # also special features to render a 'dummy' if the axis
+ # in question is not available.
+ #
+ def __init__(self, parent, token, stick):
+
+ self.stick = stick
+
+ #
+ # token represents the type of axis we're displaying.
+ #
+ self.token = token
+
+ #
+ # Create a call to the 'Has*()' method for the stick.
+ # X and Y are always there, so we tie the Has* method
+ # to a hardwired True value.
+ #
+ if token not in ['X', 'Y']:
+ self.HasFunc = eval('stick.Has%s' % token)
+ else:
+ self.HasFunc = self.alwaysTrue
+
+ # Now init the panel.
+ wx.Panel.__init__(self, parent, -1)
+
+ sizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ if self.HasFunc():
+ #
+ # Tie our calibration functions to the appropriate
+ # stick method. If we don't have the axis in question,
+ # we won't need them.
+ #
+ self.GetMin = eval('stick.Get%sMin' % token)
+ 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.bar = AxisBar(self)
+
+ sizer.Add(self.Min, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 1)
+ sizer.Add(self.bar, 1, wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, 1)
+ sizer.Add(self.Max, 0, wx.ALL | wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, 1)
+
+ else:
+ # We go here if the axis in question is not available.
+ self.control = wx.StaticText(self, -1, ' *** Not Present ***')
+ sizer.Add(self.control, 1, wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, 1)
+
+ #----------------------------------------------------------------------------
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+ wx.CallAfter(self.Update)
+
+
+ def Calibrate(self):
+ if not self.HasFunc():
+ return
+
+ self.Min.SetLabel(str(self.GetMin()))
+ self.Max.SetLabel(str(self.GetMax()))
+
+
+ def Update(self):
+ # Don't bother if the axis doesn't exist.
+ if not self.HasFunc():
+ return
+
+ min = int(self.Min.GetLabel())
+ max = int(self.Max.GetLabel())
+
+ #
+ # Not all values are available from a wx.JoystickEvent, so I've elected
+ # to not use it at all. Therefore, we are getting our values direct from
+ # the stick. These values also seem to be more stable and reliable than
+ # those received from the event itself, so maybe it's a good idea to
+ # use the stick directly for your program.
+ #
+ # Here we either select the appropriate member of stick.GetPosition() or
+ # apply the appropriate Get*Position method call.
+ #
+ if self.token == 'X':
+ val = self.stick.GetPosition().x
+ elif self.token == 'Y':
+ val = self.stick.GetPosition().y
+ 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.
+ #
+ 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)
+
+ #
+ # Pass both the raw and relative values to the derived Gauge
+ #
+ self.bar.Update(relative, val)
+
+
+ def alwaysTrue(self):
+ # a dummy method used for X and Y axis.
+ return True
+
+
+#----------------------------------------------------------------------------
+
+class AxisPanel(wx.Panel):
+ #
+ # Contained herein is a panel that offers a graphical display
+ # of the levels for all axes supported by wx.Joystick. If
+ # your system doesn't have a particular axis, it will be
+ # 'dummied' for transparent use.
+ #
+ def __init__(self, parent, stick):
+
+ self.stick = stick
+
+ # Defines labels and 'tokens' to identify each
+ # supporte axis.
+ axesList = [
+ ('X Axis ', 'X'), ('Y Axis ', 'Y'),
+ ('Z Axis ', 'Z'), ('Rudder ', 'Rudder'),
+ ('U Axis ', 'U'), ('V Axis ', 'V')
+ ]
+
+ # Contains a list of all axis initialized.
+ self.axes = []
+
+ wx.Panel.__init__(self, parent, -1)
+
+ sizer = wx.FlexGridSizer(3, 4, 1, 1)
+ sizer.AddGrowableCol(1)
+ sizer.AddGrowableCol(3)
+
+ #----------------------------------------------------------------------------
+
+ # Go through the list of labels and tokens and add a label and
+ # axis display to the sizer for each.
+ for label, token in axesList:
+ sizer.Add(Label(self, label), 0, wx.ALL | wx.ALIGN_RIGHT, 2)
+ t = Axis(self, token, self.stick)
+ self.axes.append(t)
+ sizer.Add(t, 1, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 2)
+
+ #----------------------------------------------------------------------------
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+ wx.CallAfter(self.Update)
+
+ def Calibrate(self):
+ for i in self.axes:
+ i.Calibrate()
+
+ def Update(self):
+ for i in self.axes:
+ i.Update()
+
+
+#----------------------------------------------------------------------------
+
+class JoystickDemoPanel(wx.Panel):
+
+ def __init__(self, parent, log):
+
+ self.log = log
+
+ wx.Panel.__init__(self, parent, -1)
+
+ # Try to grab the control. If we get it, capture the stick.
+ # Otherwise, throw up an exception message and play stupid.
+ try:
+ self.stick = wx.Joystick()
+ self.stick.SetCapture(self)
+ # Calibrate our controls
+ wx.CallAfter(self.Calibrate)
+ wx.CallAfter(self.OnJoystick)
+ except NotImplementedError, v:
+ wx.MessageBox(str(v), "Exception Message")
+ self.stick = None
+
+ # One Sizer to Rule Them All...
+ sizer = wx.GridBagSizer(2,2)
+
+ self.info = InfoPanel(self, self.stick)
+ sizer.Add(self.info, (0, 0), (1, 3), wx.ALL | wx.GROW, 2)
+
+ self.info.Bind(wx.EVT_BUTTON, self.Calibrate)
+
+ self.joy = JoyPanel(self, self.stick)
+ sizer.Add(self.joy, (1, 0), (1, 1), wx.ALL | wx.GROW, 2)
+
+ self.pov = POVPanel(self, self.stick)
+ sizer.Add(self.pov, (1, 1), (1, 2), wx.ALL | wx.GROW, 2)
+
+ self.axes = AxisPanel(self, self.stick)
+ sizer.Add(self.axes, (2, 0), (1, 3), wx.ALL | wx.GROW, 2)
+
+ self.buttons = JoyButtons(self, self.stick)
+ sizer.Add(self.buttons, (3, 0), (1, 3), wx.ALL | wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, 1)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ # 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:
+ return
+
+ self.info.Calibrate()
+ self.axes.Calibrate()
+ 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()
+
+
+#----------------------------------------------------------------------------
+
+def runTest(frame, nb, log):
+ if haveJoystick:
+ win = JoystickDemoPanel(nb, log)
+ return win
+ else:
+ dlg = wx.MessageDialog(
+ frame, 'wx.Joystick is not available on this platform.',
+ 'Sorry', wx.OK | wx.ICON_INFORMATION
+ )
+ dlg.ShowModal()
+ dlg.Destroy()
+
+
+#----------------------------------------------------------------------------