git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@25273 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			1074 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1074 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#----------------------------------------------------------------------------
 | 
						|
# Name:        Joystick.py
 | 
						|
# Purpose:     Demonstrate use of wx.Joystick
 | 
						|
#
 | 
						|
# Author:      Jeff Grimmett (grimmtoo@softhome.net), adapted from original
 | 
						|
#              .wdr-derived demo
 | 
						|
#
 | 
						|
# Created:     01/02/04
 | 
						|
# RCS-ID:      $Id$
 | 
						|
# Copyright:
 | 
						|
# Licence:     wxWindows license
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
#
 | 
						|
 | 
						|
import  math
 | 
						|
import  wx
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
# Once all supported versions of Python support 32-bit integers on all
 | 
						|
# platforms, this can go up to 32.
 | 
						|
MAX_BUTTONS = 16
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
class Label(wx.StaticText):
 | 
						|
    # A derived StaticText that always aligns right and renders
 | 
						|
    # in a bold font.
 | 
						|
    def __init__(self, parent, label):
 | 
						|
        wx.StaticText.__init__(self, parent, -1, label, style=wx.ALIGN_RIGHT)
 | 
						|
 | 
						|
        self.SetFont(
 | 
						|
            wx.Font(
 | 
						|
                parent.GetFont().GetPointSize(),
 | 
						|
                parent.GetFont().GetFamily(),
 | 
						|
                parent.GetFont().GetStyle(),
 | 
						|
                wx.BOLD
 | 
						|
                ))
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
class JoyGauge(wx.Panel):
 | 
						|
    def __init__(self, parent, stick):
 | 
						|
 | 
						|
        self.stick = stick
 | 
						|
        size = (100,100)
 | 
						|
        
 | 
						|
        wx.Panel.__init__(self, parent, -1, size=size)
 | 
						|
 | 
						|
        self.Bind(wx.EVT_PAINT, self.OnPaint)
 | 
						|
        self.Bind(wx.EVT_SIZE, self.OnSize)
 | 
						|
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
 | 
						|
 | 
						|
        self.buffer = wx.EmptyBitmap(*size)
 | 
						|
        dc = wx.BufferedDC(None, self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawJoystick(dc)
 | 
						|
       
 | 
						|
 | 
						|
    def OnSize(self, event):
 | 
						|
        # The face Bitmap init is done here, to make sure the buffer is always
 | 
						|
        # the same size as the Window
 | 
						|
        w, h  = self.GetClientSize()
 | 
						|
        self.buffer = wx.EmptyBitmap(w,h)
 | 
						|
        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawJoystick(dc)
 | 
						|
 | 
						|
 | 
						|
    def DrawFace(self, dc):
 | 
						|
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
 | 
						|
        dc.Clear()
 | 
						|
 | 
						|
 | 
						|
    def OnPaint(self, evt):
 | 
						|
        # When dc is destroyed it will blit self.buffer to the window,
 | 
						|
        # since no other drawing is needed we'll just return and let it
 | 
						|
        # do it's thing
 | 
						|
        dc = wx.BufferedPaintDC(self, self.buffer)
 | 
						|
 | 
						|
 | 
						|
    def DrawJoystick(self, dc):
 | 
						|
        # draw the guage as a maxed square in the center of this window.
 | 
						|
        w, h = self.GetClientSize()
 | 
						|
        edgeSize = min(w, h)
 | 
						|
 | 
						|
        xorigin = (w - edgeSize) / 2
 | 
						|
        yorigin = (h - edgeSize) / 2
 | 
						|
        center = edgeSize / 2
 | 
						|
 | 
						|
        # Restrict our drawing activities to the square defined
 | 
						|
        # above.
 | 
						|
        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.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))
 | 
						|
 | 
						|
        if self.stick:
 | 
						|
            # Get the joystick position as a float
 | 
						|
            joyx =  float(self.stick.GetPosition().x)
 | 
						|
            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()
 | 
						|
 | 
						|
            # calc a ratio of our range versus the joystick range
 | 
						|
            xratio = float(edgeSize) / xrange
 | 
						|
            yratio = float(edgeSize) / yrange
 | 
						|
 | 
						|
            # calc the displayable value based on position times ratio
 | 
						|
            xval = int(joyx * xratio)
 | 
						|
            yval = int(joyy * xratio)
 | 
						|
 | 
						|
            # and normalize the value from our brush's origin
 | 
						|
            x = xval + xorigin
 | 
						|
            y = yval + yorigin
 | 
						|
 | 
						|
            # Now to draw it.
 | 
						|
            dc.SetPen(wx.Pen(wx.RED, 2))
 | 
						|
            dc.CrossHair((x, y))
 | 
						|
 | 
						|
        # Turn off drawing optimization
 | 
						|
        dc.EndDrawing()
 | 
						|
 | 
						|
 | 
						|
    def Update(self):
 | 
						|
        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawJoystick(dc)
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
class JoyPanel(wx.Panel):
 | 
						|
    def __init__(self, parent, stick):
 | 
						|
 | 
						|
        self.stick = stick
 | 
						|
 | 
						|
        wx.Panel.__init__(self, parent, -1)
 | 
						|
 | 
						|
        sizer = wx.BoxSizer(wx.VERTICAL)
 | 
						|
 | 
						|
        fn = wx.Font(
 | 
						|
                parent.GetFont().GetPointSize() + 3,
 | 
						|
                parent.GetFont().GetFamily(),
 | 
						|
                parent.GetFont().GetStyle(),
 | 
						|
                wx.BOLD
 | 
						|
                )
 | 
						|
 | 
						|
        t = wx.StaticText(self, -1, "X - Y Axes", style = wx.ALIGN_CENTRE)
 | 
						|
        t.SetFont(fn)
 | 
						|
        sizer.Add(t, 0, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_HORIZONTAL, 1)
 | 
						|
 | 
						|
        self.control = JoyGauge(self, self.stick)
 | 
						|
        sizer.Add(self.control, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_HORIZONTAL, 1)
 | 
						|
 | 
						|
        self.SetSizer(sizer)
 | 
						|
        sizer.Fit(self)
 | 
						|
 | 
						|
    def Update(self):
 | 
						|
        self.control.Update()
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
class POVGauge(wx.Panel):
 | 
						|
    #
 | 
						|
    # Display the current postion of the POV control
 | 
						|
    #
 | 
						|
    def __init__(self, parent, stick):
 | 
						|
 | 
						|
        self.stick = stick
 | 
						|
        self.size = (100, 100)
 | 
						|
        self.avail = False
 | 
						|
        self.fourDir = False
 | 
						|
        self.cts = False
 | 
						|
 | 
						|
        wx.Panel.__init__(self, parent, -1, size=self.size)
 | 
						|
 | 
						|
        self.Bind(wx.EVT_PAINT, self.OnPaint)
 | 
						|
        self.Bind(wx.EVT_SIZE, self.OnSize)
 | 
						|
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
 | 
						|
 | 
						|
        self.buffer = wx.EmptyBitmap(*self.size)
 | 
						|
        dc = wx.BufferedDC(None, self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawPOV(dc)
 | 
						|
 | 
						|
 | 
						|
    def OnSize(self, event):
 | 
						|
        # calculate the size of our display and make a buffer for it.
 | 
						|
        w, h  = self.GetClientSize()
 | 
						|
        s = min(w, h)
 | 
						|
        self.size = (s, s)
 | 
						|
        self.buffer = wx.EmptyBitmap(w,h)
 | 
						|
        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawPOV(dc)
 | 
						|
 | 
						|
    
 | 
						|
    def DrawFace(self, dc):
 | 
						|
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
 | 
						|
        dc.Clear()
 | 
						|
 | 
						|
 | 
						|
    def OnPaint(self, evt):
 | 
						|
        # When dc is destroyed it will blit self.buffer to the window,
 | 
						|
        # since no other drawing is needed we'll just return and let it
 | 
						|
        # do it's thing
 | 
						|
        dc = wx.BufferedPaintDC(self, self.buffer)
 | 
						|
 | 
						|
 | 
						|
    def DrawPOV(self, dc):
 | 
						|
        # draw the guage as a maxed circle in the center of this window.
 | 
						|
        w, h = self.GetClientSize()
 | 
						|
        diameter = min(w, h)
 | 
						|
 | 
						|
        xorigin = (w - diameter) / 2
 | 
						|
        yorigin = (h - diameter) / 2
 | 
						|
        xcenter = xorigin + diameter / 2
 | 
						|
        ycenter = yorigin + diameter / 2
 | 
						|
 | 
						|
        # Optimize drawing a bit (for Win)
 | 
						|
        dc.BeginDrawing()
 | 
						|
 | 
						|
        # our 'raster'.
 | 
						|
        dc.SetBrush(wx.Brush(wx.WHITE))
 | 
						|
        dc.DrawCircle((xcenter, ycenter), diameter/2)
 | 
						|
        dc.SetBrush(wx.Brush(wx.BLACK))
 | 
						|
        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))
 | 
						|
 | 
						|
        if self.stick:
 | 
						|
            if self.avail:
 | 
						|
 | 
						|
                pos = -1
 | 
						|
 | 
						|
                # use the appropriate function to get the POV position
 | 
						|
                if self.fourDir:
 | 
						|
                    pos = self.stick.GetPOVPosition()
 | 
						|
 | 
						|
                if self.cts:
 | 
						|
                    pos = self.stick.GetPOVCTSPosition()
 | 
						|
 | 
						|
                # trap invalid values
 | 
						|
                if 0 <= pos <= 36000:
 | 
						|
                    vector = 30
 | 
						|
                else:
 | 
						|
                    vector = 0
 | 
						|
 | 
						|
                # rotate CCW by 90 so that 0 is up.
 | 
						|
                pos = (pos / 100) - 90
 | 
						|
 | 
						|
                # Normalize
 | 
						|
                if pos < 0:
 | 
						|
                    pos = pos + 360
 | 
						|
 | 
						|
                # Stolen from wx.lib.analogclock :-)
 | 
						|
                radiansPerDegree = math.pi / 180
 | 
						|
                pointX = int(round(vector * math.cos(pos * radiansPerDegree)))
 | 
						|
                pointY = int(round(vector * math.sin(pos * radiansPerDegree)))
 | 
						|
 | 
						|
                # normalise value to match our actual center.
 | 
						|
                nx = pointX + xcenter
 | 
						|
                ny = pointY + ycenter
 | 
						|
 | 
						|
                # Draw the line
 | 
						|
                dc.SetPen(wx.Pen(wx.BLUE, 2))
 | 
						|
                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)
 | 
						|
 | 
						|
        # Turn off drawing optimization
 | 
						|
        dc.EndDrawing()
 | 
						|
 | 
						|
 | 
						|
    def Update(self):
 | 
						|
        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawPOV(dc)
 | 
						|
 | 
						|
 | 
						|
    def Calibrate(self):
 | 
						|
        s = self.stick
 | 
						|
        self.avail = s.HasPOV()
 | 
						|
        self.fourDir = s.HasPOV4Dir()
 | 
						|
        self.cts = s.HasPOVCTS()
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
class POVStatus(wx.Panel):
 | 
						|
    #
 | 
						|
    # Displays static info about the POV control
 | 
						|
    #
 | 
						|
    def __init__(self, parent, stick):
 | 
						|
 | 
						|
        self.stick = stick
 | 
						|
 | 
						|
        wx.Panel.__init__(self, parent, -1, size=(100, 100))
 | 
						|
 | 
						|
        sizer = wx.BoxSizer(wx.VERTICAL)
 | 
						|
        sizer.Add((20,20))
 | 
						|
        
 | 
						|
        self.avail = wx.CheckBox(self, -1, "Available")
 | 
						|
        sizer.Add(self.avail, 0, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        self.fourDir = wx.CheckBox(self, -1, "4-Way Only")
 | 
						|
        sizer.Add(self.fourDir, 0, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        self.cts = wx.CheckBox(self, -1, "Continuous")
 | 
						|
        sizer.Add(self.cts, 0, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        self.SetSizer(sizer)
 | 
						|
        sizer.Fit(self)
 | 
						|
 | 
						|
        # Effectively makes the checkboxes read-only.
 | 
						|
        self.Bind(wx.EVT_CHECKBOX, self.Calibrate)
 | 
						|
 | 
						|
 | 
						|
    def Calibrate(self, evt=None):
 | 
						|
        s = self.stick
 | 
						|
        self.avail.SetValue(s.HasPOV())
 | 
						|
        self.fourDir.SetValue(s.HasPOV4Dir())
 | 
						|
        self.cts.SetValue(s.HasPOVCTS())
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
class POVPanel(wx.Panel):
 | 
						|
    def __init__(self, parent, stick):
 | 
						|
 | 
						|
        self.stick = stick
 | 
						|
 | 
						|
        wx.Panel.__init__(self, parent, -1, size=(100, 100))
 | 
						|
 | 
						|
        sizer = wx.BoxSizer(wx.HORIZONTAL)
 | 
						|
        gsizer = wx.BoxSizer(wx.VERTICAL)
 | 
						|
 | 
						|
        sizer.Add((25,25))
 | 
						|
        
 | 
						|
        fn = wx.Font(
 | 
						|
                parent.GetFont().GetPointSize() + 3,
 | 
						|
                parent.GetFont().GetFamily(),
 | 
						|
                parent.GetFont().GetStyle(),
 | 
						|
                wx.BOLD
 | 
						|
                )
 | 
						|
        t = wx.StaticText(self, -1, "POV Control", style = wx.ALIGN_CENTER)
 | 
						|
        t.SetFont(fn)
 | 
						|
        gsizer.Add(t, 0, wx.ALL | wx.EXPAND, 1)
 | 
						|
        
 | 
						|
        self.display = POVGauge(self, stick)
 | 
						|
        gsizer.Add(self.display, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER, 1)
 | 
						|
        sizer.Add(gsizer, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER, 1)
 | 
						|
        
 | 
						|
        self.status = POVStatus(self, stick)
 | 
						|
        sizer.Add(self.status, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER, 1)
 | 
						|
 | 
						|
        self.SetSizer(sizer)
 | 
						|
        sizer.Fit(self)
 | 
						|
 | 
						|
 | 
						|
    def Calibrate(self):
 | 
						|
        self.display.Calibrate()
 | 
						|
        self.status.Calibrate()
 | 
						|
 | 
						|
 | 
						|
    def Update(self):
 | 
						|
        self.display.Update()
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
class LED(wx.Panel):
 | 
						|
    def __init__(self, parent, number):
 | 
						|
 | 
						|
        self.state = -1
 | 
						|
        self.size = (20, 20)
 | 
						|
        self.number = number
 | 
						|
 | 
						|
        self.fn = wx.Font(
 | 
						|
                parent.GetFont().GetPointSize() - 1,
 | 
						|
                parent.GetFont().GetFamily(),
 | 
						|
                parent.GetFont().GetStyle(),
 | 
						|
                wx.BOLD
 | 
						|
                )
 | 
						|
 | 
						|
        wx.Panel.__init__(self, parent, -1, size=self.size)
 | 
						|
 | 
						|
        self.Bind(wx.EVT_PAINT, self.OnPaint)
 | 
						|
        self.Bind(wx.EVT_SIZE, self.OnSize)
 | 
						|
        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
 | 
						|
 | 
						|
        self.buffer = wx.EmptyBitmap(*self.size)
 | 
						|
        dc = wx.BufferedDC(None, self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawLED(dc)
 | 
						|
 | 
						|
 | 
						|
    def OnSize(self, event):
 | 
						|
        # calculate the size of our display.
 | 
						|
        w, h  = self.GetClientSize()
 | 
						|
        s = min(w, h)
 | 
						|
        self.size = (s, s)
 | 
						|
        self.buffer = wx.EmptyBitmap(*self.size)
 | 
						|
        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawLED(dc)
 | 
						|
 | 
						|
 | 
						|
    def DrawFace(self, dc):
 | 
						|
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
 | 
						|
        dc.Clear()
 | 
						|
 | 
						|
 | 
						|
    def OnPaint(self, evt):
 | 
						|
        # When dc is destroyed it will blit self.buffer to the window,
 | 
						|
        # since no other drawing is needed we'll just return and let it
 | 
						|
        # do it's thing
 | 
						|
        dc = wx.BufferedPaintDC(self, self.buffer)
 | 
						|
 | 
						|
 | 
						|
    def DrawLED(self, dc):
 | 
						|
        # bitmap size
 | 
						|
        bw, bh = self.size
 | 
						|
 | 
						|
        # center of bitmap
 | 
						|
        center = bw / 2
 | 
						|
 | 
						|
        # calc the 0, 0 origin of the bitmap
 | 
						|
        xorigin = center - (bw / 2)
 | 
						|
        yorigin = center - (bh / 2)
 | 
						|
 | 
						|
        # Optimize drawing a bit (for Win)
 | 
						|
        dc.BeginDrawing()
 | 
						|
 | 
						|
        # our 'raster'.
 | 
						|
        if self.state == 0:
 | 
						|
            dc.SetBrush(wx.Brush(wx.RED))
 | 
						|
        elif self.state == 1:
 | 
						|
            dc.SetBrush(wx.Brush(wx.GREEN))
 | 
						|
        else:
 | 
						|
            dc.SetBrush(wx.Brush(wx.BLACK))
 | 
						|
 | 
						|
        dc.DrawCircle((center, center), bw/2)
 | 
						|
 | 
						|
        txt = str(self.number)
 | 
						|
 | 
						|
        # Set the font for the DC ...
 | 
						|
        dc.SetFont(self.fn)
 | 
						|
        # ... and calculate how much space our value
 | 
						|
        # will take up.
 | 
						|
        fw, fh = dc.GetTextExtent(txt)
 | 
						|
 | 
						|
        # Calc the center of the LED, and from that
 | 
						|
        # derive the origin of our value.
 | 
						|
        tx = center - (fw/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.WHITE)
 | 
						|
        dc.DrawText(txt, (tx, ty))
 | 
						|
 | 
						|
        # Turn off drawing optimization
 | 
						|
        dc.EndDrawing()
 | 
						|
 | 
						|
 | 
						|
    def Update(self):
 | 
						|
        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
 | 
						|
        self.DrawFace(dc)
 | 
						|
        self.DrawLED(dc)
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
class JoyButtons(wx.Panel):
 | 
						|
    def __init__(self, parent, stick):
 | 
						|
 | 
						|
        self.stick = stick
 | 
						|
        self.leds = {}
 | 
						|
 | 
						|
        wx.Panel.__init__(self, parent, -1)
 | 
						|
 | 
						|
        tsizer = wx.BoxSizer(wx.VERTICAL)
 | 
						|
 | 
						|
        fn = wx.Font(
 | 
						|
                parent.GetFont().GetPointSize() + 3,
 | 
						|
                parent.GetFont().GetFamily(),
 | 
						|
                parent.GetFont().GetStyle(),
 | 
						|
                wx.BOLD
 | 
						|
                )
 | 
						|
 | 
						|
        t = wx.StaticText(self, -1, "Buttons", style = wx.ALIGN_LEFT)
 | 
						|
        t.SetFont(fn)
 | 
						|
        tsizer.Add(t, 0, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 1)
 | 
						|
 | 
						|
        sizer = wx.FlexGridSizer(4, 16, 2, 2)
 | 
						|
 | 
						|
        fn.SetPointSize(parent.GetFont().GetPointSize() + 1)
 | 
						|
 | 
						|
        for i in range(0, MAX_BUTTONS):
 | 
						|
            t = LED(self, i)
 | 
						|
            self.leds[i] = t
 | 
						|
            sizer.Add(t, 1, wx.ALL|wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL, 1)
 | 
						|
            sizer.AddGrowableCol(i)
 | 
						|
 | 
						|
        tsizer.Add(sizer, 1, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 1)
 | 
						|
 | 
						|
        self.SetSizer(tsizer)
 | 
						|
        tsizer.Fit(self)
 | 
						|
 | 
						|
    def Calibrate(self):
 | 
						|
        for i in range(0, MAX_BUTTONS):
 | 
						|
            self.leds[i].state = -1
 | 
						|
 | 
						|
        t = self.stick.GetNumberButtons()
 | 
						|
 | 
						|
        for i in range(0, t):
 | 
						|
            self.leds[i].state = 0
 | 
						|
 | 
						|
    def Update(self):
 | 
						|
        t = self.stick.GetButtonState()
 | 
						|
 | 
						|
        for i in range(0, MAX_BUTTONS):
 | 
						|
            if self.leds[i].state == 1:
 | 
						|
                self.leds[i].state = 0
 | 
						|
 | 
						|
            if (t & (1<<i)):
 | 
						|
                self.leds[i].state = 1
 | 
						|
 | 
						|
            self.leds[i].Update()
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
class InfoPanel(wx.Panel):
 | 
						|
    def __init__(self, parent, stick):
 | 
						|
 | 
						|
        self.stick = stick
 | 
						|
 | 
						|
        wx.Panel.__init__(self, parent, -1)
 | 
						|
 | 
						|
        sizer = wx.GridBagSizer(1, 1)
 | 
						|
 | 
						|
        sizer.Add(Label(self, 'Mfr ID: '), (0, 0), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_RIGHT, 2)
 | 
						|
        self.MfgID = wx.TextCtrl(self, -1, value='', size=(45, -1), style=wx.TE_READONLY)
 | 
						|
        sizer.Add(self.MfgID, (0, 1), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        sizer.Add(Label(self, 'Prod Name: '), (0, 2), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_RIGHT, 2)
 | 
						|
        self.ProdName = wx.TextCtrl(self, -1, value='', style=wx.TE_READONLY)
 | 
						|
        sizer.Add(self.ProdName, (0, 3), (1, 3), wx.ALL | wx.GROW | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        sizer.Add(Label(self, 'Threshold: '), (0, 6), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_RIGHT, 2)
 | 
						|
        self.Threshold = wx.TextCtrl(self, -1, value='', size=(45, -1), style=wx.TE_READONLY)
 | 
						|
        sizer.Add(self.Threshold, (0, 7), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        #----------------------------------------------------------------------------
 | 
						|
        b = wx.Button(self, -1, "Calibrate")
 | 
						|
        sizer.Add(b, (1, 0), (2, 2), wx.ALL | wx.ALIGN_CENTER, 2)
 | 
						|
 | 
						|
        sizer.Add(Label(self, '# of Sticks: '), (1, 2), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_RIGHT, 2)
 | 
						|
        self.NumJoysticks = wx.TextCtrl(self, -1, value='', size=(45, -1), style=wx.TE_READONLY)
 | 
						|
        sizer.Add(self.NumJoysticks, (1, 3), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        sizer.Add(Label(self, '# of Axes: '), (1, 4), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_RIGHT, 2)
 | 
						|
        self.NumAxis = wx.TextCtrl(self, -1, value='', size=(45, -1), style=wx.TE_READONLY)
 | 
						|
        sizer.Add(self.NumAxis, (1, 5), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        sizer.Add(Label(self, 'Max # Axes: '), (1, 6), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_RIGHT, 2)
 | 
						|
        self.MaxAxis = wx.TextCtrl(self, -1, value='', size=(45, -1), style=wx.TE_READONLY)
 | 
						|
        sizer.Add(self.MaxAxis, (1, 7), (1, 1), wx.ALL | wx.GROW | wx.ALIGN_LEFT, 2)
 | 
						|
 | 
						|
        #----------------------------------------------------------------------------
 | 
						|
 | 
						|
        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):
 | 
						|
    win = JoystickDemoPanel(nb, log)
 | 
						|
    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])])
 |