X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/299647acac7960652aadb008775429c1f8ea9b8d..b4eecebd423df0c6efe86d7658cd3df818d67ed8:/wxPython/demo/Joystick.py diff --git a/wxPython/demo/Joystick.py b/wxPython/demo/Joystick.py index 2c4113adf0..c94e215507 100644 --- a/wxPython/demo/Joystick.py +++ b/wxPython/demo/Joystick.py @@ -1,206 +1,1107 @@ +#---------------------------------------------------------------------------- +# Name: Joystick.py +# Purpose: Demonstrate use of wx.Joystick +# +# Author: Jeff Grimmett (grimmtoo@softhome.net), adapted from original +# .wdr-derived demo +# +# Created: 02-Jan-2004 +# RCS-ID: $Id$ +# Copyright: +# Licence: wxWindows license +#---------------------------------------------------------------------------- +# + +import math +import wx + +haveJoystick = True +if wx.Platform == "__WXMAC__": + haveJoystick = False #---------------------------------------------------------------------------- -from wxPython.wx import * -from joystick_wdr import * +# Once all supported versions of Python support 32-bit integers on all +# platforms, this can go up to 32. +MAX_BUTTONS = 16 +#---------------------------------------------------------------------------- -class JoystickTestPanel(wxPanel): - def __init__(self, parent, id, - pos = wxDefaultPosition, size = wxDefaultSize, - style = wxTAB_TRAVERSAL ): - wxPanel.__init__(self, parent, id, pos, size, style) +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) - MakeJoystickTestPanel( self, True ) + self.SetFont( + wx.Font( + parent.GetFont().GetPointSize(), + parent.GetFont().GetFamily(), + parent.GetFont().GetStyle(), + wx.BOLD + )) - try: - self.stick = wxJoystick() - self.stick.SetCapture(self) - EVT_JOYSTICK_EVENTS(self, self.OnJoystick) - self.UpdateFields() - except NotImplementedError, v: - wxMessageBox(str(v), "Exception Message") +#---------------------------------------------------------------------------- + + +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 + 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 + yratio = float(edgeSize) / yrange + + # calc the displayable value based on position times ratio + xval = int(joyx * xratio) + yval = int(joyy * yratio) + + # 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 - def UpdateFields(self): + # 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.GetXPositionCtrl().SetValue(str(s.GetPosition().x)) - self.GetYPositionCtrl().SetValue(str(s.GetPosition().y)) - self.GetZPositionCtrl().SetValue(str(s.GetZPosition())) - self.GetPovCtsPosCtrl().SetValue(str(s.GetPOVPosition())) - self.GetRudderPosCtrl().SetValue(str(s.GetRudderPosition())) - self.GetHasRudderCtrl().SetValue(str(s.HasRudder())) - self.GetHasZCtrl().SetValue(str(s.HasZ())) - self.GetHasPovCtrl().SetValue(str(s.HasPOV())) - self.GetHasPov4dirCtrl().SetValue(str(s.HasPOV4Dir())) - self.GetMfgIdCtrl().SetValue(str(s.GetManufacturerId())) - self.GetProdNameCtrl().SetValue(str(s.GetProductName())) - self.GetZMinCtrl().SetValue(str(s.GetZMin())) - self.GetXMaxCtrl().SetValue(str(s.GetXMax())) - self.GetNumButtonsCtrl().SetValue(str(s.GetNumberButtons())) - self.GetNumAxesCtrl().SetValue(str(s.GetNumberAxes())) - self.GetPollingMinCtrl().SetValue(str(s.GetPollingMin())) - self.GetPollingMaxCtrl().SetValue(str(s.GetPollingMax())) - self.GetUMinCtrl().SetValue(str(s.GetUMin())) - self.GetUMaxCtrl().SetValue(str(s.GetUMax())) - self.GetButtonStateCtrl().SetValue(str(s.GetButtonState())) - self.GetPovPositionCtrl().SetValue(str(s.GetPOVPosition())) - self.GetUPositionCtrl().SetValue(str(s.GetUPosition())) - self.GetVPositionCtrl().SetValue(str(s.GetVPosition())) - self.GetHasUCtrl().SetValue(str(s.HasU())) - self.GetHasVCtrl().SetValue(str(s.HasV())) - self.GetHasPovCtsCtrl().SetValue(str(s.HasPOVCTS())) - self.GetNumSticksCtrl().SetValue(str(s.GetNumberJoysticks())) - self.GetXMinCtrl().SetValue(str(s.GetXMin())) - self.GetYMinCtrl().SetValue(str(s.GetYMin())) - self.GetYMaxCtrl().SetValue(str(s.GetYMax())) - self.GetZMaxCtrl().SetValue(str(s.GetZMax())) - self.GetMaxButtonsCtrl().SetValue(str(s.GetMaxButtons())) - self.GetMaxAxesCtrl().SetValue(str(s.GetMaxAxes())) - self.GetRudderMinCtrl().SetValue(str(s.GetRudderMin())) - self.GetRudderMaxCtrl().SetValue(str(s.GetRudderMax())) - self.GetVMinCtrl().SetValue(str(s.GetVMin())) - self.GetVMaxCtrl().SetValue(str(s.GetVMax())) + self.avail = s.HasPOV() + self.fourDir = s.HasPOV4Dir() + self.cts = s.HasPOVCTS() - def OnJoystick(self, evt): - self.UpdateFields() +#---------------------------------------------------------------------------- + +class POVStatus(wx.Panel): + # + # Displays static info about the POV control + # + def __init__(self, parent, stick): + self.stick = stick - # WDR: methods for JoystickTestPanel + wx.Panel.__init__(self, parent, -1, size=(100, 100)) - def GetYPositionCtrl(self): - return self.FindWindowById(ID_Y_Position_Ctrl) + 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) - def GetXPositionCtrl(self): - return self.FindWindowById(ID_X_Position_Ctrl) + 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) - def GetVMaxCtrl(self): - return self.FindWindowById(ID_V_Max_Ctrl) + self.SetSizer(sizer) + sizer.Fit(self) - def GetVMinCtrl(self): - return self.FindWindowById(ID_V_Min_Ctrl) + # Effectively makes the checkboxes read-only. + self.Bind(wx.EVT_CHECKBOX, self.Calibrate) - def GetRudderMaxCtrl(self): - return self.FindWindowById(ID_Rudder_Max_Ctrl) - def GetRudderMinCtrl(self): - return self.FindWindowById(ID_Rudder_Min_Ctrl) + def Calibrate(self, evt=None): + s = self.stick + self.avail.SetValue(s.HasPOV()) + self.fourDir.SetValue(s.HasPOV4Dir()) + self.cts.SetValue(s.HasPOVCTS()) - def GetMaxAxesCtrl(self): - return self.FindWindowById(ID_Max_Axes_Ctrl) - def GetMaxButtonsCtrl(self): - return self.FindWindowById(ID_Max_Buttons_Ctrl) +#---------------------------------------------------------------------------- - def GetZMaxCtrl(self): - return self.FindWindowById(ID_Z_Max_Ctrl) +class POVPanel(wx.Panel): + def __init__(self, parent, stick): - def GetYMaxCtrl(self): - return self.FindWindowById(ID_Y_Max_Ctrl) + self.stick = stick - def GetYMinCtrl(self): - return self.FindWindowById(ID_Y_Min_Ctrl) + wx.Panel.__init__(self, parent, -1, size=(100, 100)) - def GetXMinCtrl(self): - return self.FindWindowById(ID_X_Min_Ctrl) + sizer = wx.BoxSizer(wx.HORIZONTAL) + gsizer = wx.BoxSizer(wx.VERTICAL) - def GetNumSticksCtrl(self): - return self.FindWindowById(ID_Num_Sticks_Ctrl) + 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) - def GetHasPovCtsCtrl(self): - return self.FindWindowById(ID_Has_POV_CTS_Ctrl) + self.SetSizer(sizer) + sizer.Fit(self) - def GetHasVCtrl(self): - return self.FindWindowById(ID_Has_V_Ctrl) - def GetHasUCtrl(self): - return self.FindWindowById(ID_Has_U_Ctrl) + def Calibrate(self): + self.display.Calibrate() + self.status.Calibrate() - def GetVPositionCtrl(self): - return self.FindWindowById(ID_V_Position_Ctrl) - def GetUPositionCtrl(self): - return self.FindWindowById(ID_U_Position_Ctrl) + def Update(self): + self.display.Update() - def GetPovPositionCtrl(self): - return self.FindWindowById(ID_POV_Position_Ctrl) - def GetButtonStateCtrl(self): - return self.FindWindowById(ID_Button_State_Ctrl) +#---------------------------------------------------------------------------- - def GetUMaxCtrl(self): - return self.FindWindowById(ID_U_Max_Ctrl) +class LED(wx.Panel): + def __init__(self, parent, number): - def GetUMinCtrl(self): - return self.FindWindowById(ID_U_Min_Ctrl) + self.state = -1 + self.size = (20, 20) + self.number = number - def GetPollingMaxCtrl(self): - return self.FindWindowById(ID_Polling_Max_Ctrl) + self.fn = wx.Font( + parent.GetFont().GetPointSize() - 1, + parent.GetFont().GetFamily(), + parent.GetFont().GetStyle(), + wx.BOLD + ) - def GetPollingMinCtrl(self): - return self.FindWindowById(ID_Polling_Min_Ctrl) + wx.Panel.__init__(self, parent, -1, size=self.size) - def GetNumAxesCtrl(self): - return self.FindWindowById(ID_Num_Axes_Ctrl) + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None) - def GetNumButtonsCtrl(self): - return self.FindWindowById(ID_Num_Buttons_Ctrl) + self.buffer = wx.EmptyBitmap(*self.size) + dc = wx.BufferedDC(None, self.buffer) + self.DrawFace(dc) + self.DrawLED(dc) - def GetXMaxCtrl(self): - return self.FindWindowById(ID_X_Max_Ctrl) - def GetZMinCtrl(self): - return self.FindWindowById(ID_Z_Min_Ctrl) + 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 GetProdNameCtrl(self): - return self.FindWindowById(ID_Prod_Name_Ctrl) - def GetMfgIdCtrl(self): - return self.FindWindowById(ID_Mfg_ID_Ctrl) + def DrawFace(self, dc): + dc.SetBackground(wx.Brush(self.GetBackgroundColour())) + dc.Clear() - def GetHasPov4dirCtrl(self): - return self.FindWindowById(ID_Has_POV_4DIR_Ctrl) - def GetHasPovCtrl(self): - return self.FindWindowById(ID_Has_POV_Ctrl) + 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 GetHasZCtrl(self): - return self.FindWindowById(ID_Has_Z_Ctrl) - def GetHasRudderCtrl(self): - return self.FindWindowById(ID_Has_Rudder_Ctrl) + def DrawLED(self, dc): + # bitmap size + bw, bh = self.size - def GetRudderPosCtrl(self): - return self.FindWindowById(ID_Rudder_Pos_Ctrl) + # center of bitmap + center = bw / 2 - def GetPovCtsPosCtrl(self): - return self.FindWindowById(ID_POV_CTS_Pos_Ctrl) + # calc the 0, 0 origin of the bitmap + xorigin = center - (bw / 2) + yorigin = center - (bh / 2) - def GetZPositionCtrl(self): - return self.FindWindowById(ID_Z_Position_Ctrl) + # Optimize drawing a bit (for Win) + dc.BeginDrawing() - # WDR: handler implementations for JoysticktestPanel + # 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) -def runTest(frame, nb, log): - win = JoystickTestPanel(nb, -1) - return win + # 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< + +

wx.Joystick

+This demo illustrates the use of the wx.Joystick class, which is an interface to +one or more joysticks attached to your system. + +

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 can get from the wx.Joystick control. + +

+ +

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. + +

Data types

+ +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. + +

Analog Input

+ +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. +

Button Input

+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 +
+     # 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
+
+ +

The problem here is that some OSs return a 32-bit value for up to 32 buttons +(imagine that stick!). Python V2.3 will generate an exception for bit +values over 30. For that reason, this demo is limited to 16 buttons. + +

Note that more than one button can be pressed at a time, so be sure to check all of them! + + +

POV Input

+ +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. + +

Different methods are provided to retrieve the POV data for a CTS hat +versus a four-way hat. + +

Caveats

+ +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 can 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. + + + +

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. + +

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(). + + + +""" + +#---------------------------------------------------------------------------- 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:])