]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/Joystick.py
   1 #---------------------------------------------------------------------------- 
   3 # Purpose:     Demonstrate use of wx.Joystick 
   5 # Author:      Jeff Grimmett (grimmtoo@softhome.net), adapted from original 
  11 # Licence:     wxWindows license 
  12 #---------------------------------------------------------------------------- 
  19 if wx
.Platform 
== "__WXMAC__": 
  22 #---------------------------------------------------------------------------- 
  24 # Once all supported versions of Python support 32-bit integers on all 
  25 # platforms, this can go up to 32. 
  28 #---------------------------------------------------------------------------- 
  30 class Label(wx
.StaticText
): 
  31     # A derived StaticText that always aligns right and renders 
  33     def __init__(self
, parent
, label
): 
  34         wx
.StaticText
.__init
__(self
, parent
, -1, label
, style
=wx
.ALIGN_RIGHT
) 
  38                 parent
.GetFont().GetPointSize(), 
  39                 parent
.GetFont().GetFamily(), 
  40                 parent
.GetFont().GetStyle(), 
  44 #---------------------------------------------------------------------------- 
  47 class JoyGauge(wx
.Panel
): 
  48     def __init__(self
, parent
, stick
): 
  53         wx
.Panel
.__init
__(self
, parent
, -1, size
=size
) 
  55         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
  56         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
  57         self
.Bind(wx
.EVT_ERASE_BACKGROUND
, lambda e
: None) 
  59         self
.buffer = wx
.EmptyBitmap(*size
) 
  60         dc 
= wx
.BufferedDC(None, self
.buffer) 
  65     def OnSize(self
, event
): 
  66         # The face Bitmap init is done here, to make sure the buffer is always 
  67         # the same size as the Window 
  68         w
, h  
= self
.GetClientSize() 
  69         self
.buffer = wx
.EmptyBitmap(w
,h
) 
  70         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
  75     def DrawFace(self
, dc
): 
  76         dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour())) 
  80     def OnPaint(self
, evt
): 
  81         # When dc is destroyed it will blit self.buffer to the window, 
  82         # since no other drawing is needed we'll just return and let it 
  84         dc 
= wx
.BufferedPaintDC(self
, self
.buffer) 
  87     def DrawJoystick(self
, dc
): 
  88         # draw the guage as a maxed square in the center of this window. 
  89         w
, h 
= self
.GetClientSize() 
  92         xorigin 
= (w 
- edgeSize
) / 2 
  93         yorigin 
= (h 
- edgeSize
) / 2 
  96         # Restrict our drawing activities to the square defined 
  98         dc
.SetClippingRegion(xorigin
, yorigin
, edgeSize
, edgeSize
) 
 100         # Optimize drawing a bit (for Win) 
 103         dc
.SetBrush(wx
.Brush(wx
.Colour(251, 252, 237))) 
 104         dc
.DrawRectangle(xorigin
, yorigin
, edgeSize
, edgeSize
) 
 106         dc
.SetPen(wx
.Pen(wx
.BLACK
, 1, wx
.DOT_DASH
)) 
 108         dc
.DrawLine(xorigin
, yorigin 
+ center
, xorigin 
+ edgeSize
, yorigin 
+ center
) 
 109         dc
.DrawLine(xorigin 
+ center
, yorigin
, xorigin 
+ center
, yorigin 
+ edgeSize
) 
 112             # Get the joystick position as a float 
 113             joyx 
=  float(self
.stick
.GetPosition().x
) 
 114             joyy 
=  float(self
.stick
.GetPosition().y
) 
 116             # Get the joystick range of motion 
 117             xmin 
= self
.stick
.GetXMin() 
 118             xmax 
= self
.stick
.GetXMax() 
 123             xrange = max(xmax 
- xmin
, 1) 
 125             ymin 
= self
.stick
.GetYMin() 
 126             ymax 
= self
.stick
.GetYMax() 
 131             yrange 
= max(ymax 
- ymin
, 1) 
 133             # calc a ratio of our range versus the joystick range 
 134             xratio 
= float(edgeSize
) / xrange 
 135             yratio 
= float(edgeSize
) / yrange
 
 137             # calc the displayable value based on position times ratio 
 138             xval 
= int(joyx 
* xratio
) 
 139             yval 
= int(joyy 
* yratio
) 
 141             # and normalize the value from our brush's origin 
 146             dc
.SetPen(wx
.Pen(wx
.RED
, 2)) 
 149         # Turn off drawing optimization 
 154         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 156         self
.DrawJoystick(dc
) 
 159 #---------------------------------------------------------------------------- 
 161 class JoyPanel(wx
.Panel
): 
 162     def __init__(self
, parent
, stick
): 
 166         wx
.Panel
.__init
__(self
, parent
, -1) 
 168         sizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 171                 parent
.GetFont().GetPointSize() + 3, 
 172                 parent
.GetFont().GetFamily(), 
 173                 parent
.GetFont().GetStyle(), 
 177         t 
= wx
.StaticText(self
, -1, "X - Y Axes", style 
= wx
.ALIGN_CENTRE
) 
 179         sizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_HORIZONTAL
, 1) 
 181         self
.control 
= JoyGauge(self
, self
.stick
) 
 182         sizer
.Add(self
.control
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_HORIZONTAL
, 1) 
 188         self
.control
.Update() 
 191 #---------------------------------------------------------------------------- 
 193 class POVGauge(wx
.Panel
): 
 195     # Display the current postion of the POV control 
 197     def __init__(self
, parent
, stick
): 
 200         self
.size 
= (100, 100) 
 205         wx
.Panel
.__init
__(self
, parent
, -1, size
=self
.size
) 
 207         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 208         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 209         self
.Bind(wx
.EVT_ERASE_BACKGROUND
, lambda e
: None) 
 211         self
.buffer = wx
.EmptyBitmap(*self
.size
) 
 212         dc 
= wx
.BufferedDC(None, self
.buffer) 
 217     def OnSize(self
, event
): 
 218         # calculate the size of our display and make a buffer for it. 
 219         w
, h  
= self
.GetClientSize() 
 222         self
.buffer = wx
.EmptyBitmap(w
,h
) 
 223         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 228     def DrawFace(self
, dc
): 
 229         dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour())) 
 233     def OnPaint(self
, evt
): 
 234         # When dc is destroyed it will blit self.buffer to the window, 
 235         # since no other drawing is needed we'll just return and let it 
 237         dc 
= wx
.BufferedPaintDC(self
, self
.buffer) 
 240     def DrawPOV(self
, dc
): 
 241         # draw the guage as a maxed circle in the center of this window. 
 242         w
, h 
= self
.GetClientSize() 
 245         xorigin 
= (w 
- diameter
) / 2 
 246         yorigin 
= (h 
- diameter
) / 2 
 247         xcenter 
= xorigin 
+ diameter 
/ 2 
 248         ycenter 
= yorigin 
+ diameter 
/ 2 
 250         # Optimize drawing a bit (for Win) 
 254         dc
.SetBrush(wx
.Brush(wx
.WHITE
)) 
 255         dc
.DrawCircle(xcenter
, ycenter
, diameter
/2) 
 256         dc
.SetBrush(wx
.Brush(wx
.BLACK
)) 
 257         dc
.DrawCircle(xcenter
, ycenter
, 10) 
 260         dc
.SetPen(wx
.Pen(wx
.BLACK
, 1, wx
.DOT_DASH
)) 
 261         dc
.DrawLine(xorigin
, ycenter
, xorigin 
+ diameter
, ycenter
) 
 262         dc
.DrawLine(xcenter
, yorigin
, xcenter
, yorigin 
+ diameter
) 
 269                 # use the appropriate function to get the POV position 
 271                     pos 
= self
.stick
.GetPOVPosition() 
 274                     pos 
= self
.stick
.GetPOVCTSPosition() 
 276                 # trap invalid values 
 277                 if 0 <= pos 
<= 36000: 
 282                 # rotate CCW by 90 so that 0 is up. 
 283                 pos 
= (pos 
/ 100) - 90 
 289                 # Stolen from wx.lib.analogclock :-) 
 290                 radiansPerDegree 
= math
.pi 
/ 180 
 291                 pointX 
= int(round(vector 
* math
.cos(pos 
* radiansPerDegree
))) 
 292                 pointY 
= int(round(vector 
* math
.sin(pos 
* radiansPerDegree
))) 
 294                 # normalise value to match our actual center. 
 295                 nx 
= pointX 
+ xcenter
 
 296                 ny 
= pointY 
+ ycenter
 
 299                 dc
.SetPen(wx
.Pen(wx
.BLUE
, 2)) 
 300                 dc
.DrawLine(xcenter
, ycenter
, nx
, ny
) 
 302                 # And a little thing to show the endpoint 
 303                 dc
.SetBrush(wx
.Brush(wx
.BLUE
)) 
 304                 dc
.DrawCircle(nx
, ny
, 8) 
 306         # Turn off drawing optimization 
 311         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 318         self
.avail 
= s
.HasPOV() 
 319         self
.fourDir 
= s
.HasPOV4Dir() 
 320         self
.cts 
= s
.HasPOVCTS() 
 323 #---------------------------------------------------------------------------- 
 325 class POVStatus(wx
.Panel
): 
 327     # Displays static info about the POV control 
 329     def __init__(self
, parent
, stick
): 
 333         wx
.Panel
.__init
__(self
, parent
, -1, size
=(100, 100)) 
 335         sizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 338         self
.avail 
= wx
.CheckBox(self
, -1, "Available") 
 339         sizer
.Add(self
.avail
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2) 
 341         self
.fourDir 
= wx
.CheckBox(self
, -1, "4-Way Only") 
 342         sizer
.Add(self
.fourDir
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2) 
 344         self
.cts 
= wx
.CheckBox(self
, -1, "Continuous") 
 345         sizer
.Add(self
.cts
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2) 
 350         # Effectively makes the checkboxes read-only. 
 351         self
.Bind(wx
.EVT_CHECKBOX
, self
.Calibrate
) 
 354     def Calibrate(self
, evt
=None): 
 356         self
.avail
.SetValue(s
.HasPOV()) 
 357         self
.fourDir
.SetValue(s
.HasPOV4Dir()) 
 358         self
.cts
.SetValue(s
.HasPOVCTS()) 
 361 #---------------------------------------------------------------------------- 
 363 class POVPanel(wx
.Panel
): 
 364     def __init__(self
, parent
, stick
): 
 368         wx
.Panel
.__init
__(self
, parent
, -1, size
=(100, 100)) 
 370         sizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 371         gsizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 376                 parent
.GetFont().GetPointSize() + 3, 
 377                 parent
.GetFont().GetFamily(), 
 378                 parent
.GetFont().GetStyle(), 
 381         t 
= wx
.StaticText(self
, -1, "POV Control", style 
= wx
.ALIGN_CENTER
) 
 383         gsizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND
, 1) 
 385         self
.display 
= POVGauge(self
, stick
) 
 386         gsizer
.Add(self
.display
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1) 
 387         sizer
.Add(gsizer
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1) 
 389         self
.status 
= POVStatus(self
, stick
) 
 390         sizer
.Add(self
.status
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1) 
 397         self
.display
.Calibrate() 
 398         self
.status
.Calibrate() 
 402         self
.display
.Update() 
 405 #---------------------------------------------------------------------------- 
 408     def __init__(self
, parent
, number
): 
 415                 parent
.GetFont().GetPointSize() - 1, 
 416                 parent
.GetFont().GetFamily(), 
 417                 parent
.GetFont().GetStyle(), 
 421         wx
.Panel
.__init
__(self
, parent
, -1, size
=self
.size
) 
 423         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 424         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 425         self
.Bind(wx
.EVT_ERASE_BACKGROUND
, lambda e
: None) 
 427         self
.buffer = wx
.EmptyBitmap(*self
.size
) 
 428         dc 
= wx
.BufferedDC(None, self
.buffer) 
 433     def OnSize(self
, event
): 
 434         # calculate the size of our display. 
 435         w
, h  
= self
.GetClientSize() 
 438         self
.buffer = wx
.EmptyBitmap(*self
.size
) 
 439         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 444     def DrawFace(self
, dc
): 
 445         dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour())) 
 449     def OnPaint(self
, evt
): 
 450         # When dc is destroyed it will blit self.buffer to the window, 
 451         # since no other drawing is needed we'll just return and let it 
 453         dc 
= wx
.BufferedPaintDC(self
, self
.buffer) 
 456     def DrawLED(self
, dc
): 
 463         # calc the 0, 0 origin of the bitmap 
 464         xorigin 
= center 
- (bw 
/ 2) 
 465         yorigin 
= center 
- (bh 
/ 2) 
 467         # Optimize drawing a bit (for Win) 
 472             dc
.SetBrush(wx
.Brush(wx
.RED
)) 
 473         elif self
.state 
== 1: 
 474             dc
.SetBrush(wx
.Brush(wx
.GREEN
)) 
 476             dc
.SetBrush(wx
.Brush(wx
.BLACK
)) 
 478         dc
.DrawCircle(center
, center
, bw
/2) 
 480         txt 
= str(self
.number
) 
 482         # Set the font for the DC ... 
 484         # ... and calculate how much space our value 
 486         fw
, fh 
= dc
.GetTextExtent(txt
) 
 488         # Calc the center of the LED, and from that 
 489         # derive the origin of our value. 
 493         # I draw the value twice so as to give it a pseudo-shadow. 
 494         # This is (mostly) because I'm too lazy to figure out how 
 495         # to blit my text onto the gauge using one of the logical 
 496         # functions. The pseudo-shadow gives the text contrast 
 497         # regardless of whether the bar is under it or not. 
 498         dc
.SetTextForeground(wx
.WHITE
) 
 499         dc
.DrawText(txt
, tx
, ty
) 
 501         # Turn off drawing optimization 
 506         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 511 #---------------------------------------------------------------------------- 
 513 class JoyButtons(wx
.Panel
): 
 514     def __init__(self
, parent
, stick
): 
 519         wx
.Panel
.__init
__(self
, parent
, -1) 
 521         tsizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 524                 parent
.GetFont().GetPointSize() + 3, 
 525                 parent
.GetFont().GetFamily(), 
 526                 parent
.GetFont().GetStyle(), 
 530         t 
= wx
.StaticText(self
, -1, "Buttons", style 
= wx
.ALIGN_LEFT
) 
 532         tsizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 1) 
 534         sizer 
= wx
.FlexGridSizer(4, 16, 2, 2) 
 536         fn
.SetPointSize(parent
.GetFont().GetPointSize() + 1) 
 538         for i 
in range(0, MAX_BUTTONS
): 
 541             sizer
.Add(t
, 1, wx
.ALL|wx
.ALIGN_CENTER|wx
.ALIGN_CENTER_VERTICAL
, 1) 
 542             sizer
.AddGrowableCol(i
) 
 544         tsizer
.Add(sizer
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 1) 
 546         self
.SetSizer(tsizer
) 
 550         for i 
in range(0, MAX_BUTTONS
): 
 551             self
.leds
[i
].state 
= -1 
 553         t 
= self
.stick
.GetNumberButtons() 
 555         for i 
in range(0, t
): 
 556             self
.leds
[i
].state 
= 0 
 559         t 
= self
.stick
.GetButtonState() 
 561         for i 
in range(0, MAX_BUTTONS
): 
 562             if self
.leds
[i
].state 
== 1: 
 563                 self
.leds
[i
].state 
= 0 
 566                 self
.leds
[i
].state 
= 1 
 568             self
.leds
[i
].Update() 
 571 #---------------------------------------------------------------------------- 
 573 class InfoPanel(wx
.Panel
): 
 574     def __init__(self
, parent
, stick
): 
 578         wx
.Panel
.__init
__(self
, parent
, -1) 
 580         sizer 
= wx
.GridBagSizer(1, 1) 
 582         sizer
.Add(Label(self
, 'Mfr ID: '), (0, 0), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 583         self
.MfgID 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 584         sizer
.Add(self
.MfgID
, (0, 1), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 586         sizer
.Add(Label(self
, 'Prod Name: '), (0, 2), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 587         self
.ProdName 
= wx
.TextCtrl(self
, -1, value
='', style
=wx
.TE_READONLY
) 
 588         sizer
.Add(self
.ProdName
, (0, 3), (1, 3), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 590         sizer
.Add(Label(self
, 'Threshold: '), (0, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 591         self
.Threshold 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 592         sizer
.Add(self
.Threshold
, (0, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 594         #---------------------------------------------------------------------------- 
 595         b 
= wx
.Button(self
, -1, "Calibrate") 
 596         sizer
.Add(b
, (1, 0), (2, 2), wx
.ALL | wx
.ALIGN_CENTER
, 2) 
 598         sizer
.Add(Label(self
, '# of Sticks: '), (1, 2), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 599         self
.NumJoysticks 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 600         sizer
.Add(self
.NumJoysticks
, (1, 3), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 602         sizer
.Add(Label(self
, '# of Axes: '), (1, 4), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 603         self
.NumAxis 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 604         sizer
.Add(self
.NumAxis
, (1, 5), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 606         sizer
.Add(Label(self
, 'Max # Axes: '), (1, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 607         self
.MaxAxis 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 608         sizer
.Add(self
.MaxAxis
, (1, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 610         #---------------------------------------------------------------------------- 
 612         sizer
.Add(Label(self
, 'Polling -- '), (2, 3), (1, 1), wx
.ALL | wx
.GROW
, 2) 
 614         sizer
.Add(Label(self
, 'Min: '), (2, 4), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 615         self
.PollMin 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 616         sizer
.Add(self
.PollMin
, (2, 5), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 618         sizer
.Add(Label(self
, 'Max: '), (2, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 619         self
.PollMax 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 620         sizer
.Add(self
.PollMax
, (2, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 622         #---------------------------------------------------------------------------- 
 634         self
.MfgID
.SetValue(str(s
.GetManufacturerId())) 
 635         self
.ProdName
.SetValue(str(s
.GetProductName())) 
 636         self
.Threshold
.SetValue(str(s
.GetMovementThreshold())) 
 637         self
.NumJoysticks
.SetValue(str(s
.GetNumberJoysticks())) 
 638         self
.NumAxis
.SetValue(str(s
.GetNumberAxes())) 
 639         self
.MaxAxis
.SetValue(str(s
.GetMaxAxes())) 
 640         self
.PollMin
.SetValue(str(s
.GetPollingMin())) 
 641         self
.PollMax
.SetValue(str(s
.GetPollingMax())) 
 644 #---------------------------------------------------------------------------- 
 646 class AxisBar(wx
.Gauge
): 
 648     # This class allows us to use a wx.Gauge to display the axis value 
 649     # with a fancy label overlayed onto the guage itself. Two values are 
 650     # used to do things: first of all, since the gauge is limited to 
 651     # positive numbers, the scale is fixed at 0 to 1000. We will receive 
 652     # an adjusted value to use to render the gauge itself. The other value 
 653     # is a raw value and actually reflects the value from the joystick itself, 
 654     # which is then drawn over the gauge. 
 656     def __init__(self
, parent
): 
 657         wx
.Gauge
.__init
__(self
, parent
, -1, 1000, size
=(-1, 20), style 
= wx
.GA_HORIZONTAL | wx
.GA_SMOOTH 
) 
 659         # This is the value we will display. 
 662         self
.SetBackgroundColour('light blue') 
 663         self
.SetForegroundColour('orange') 
 665         # Capture paint events for purpose of updating 
 666         # the displayed value. 
 667         self
.Bind(wx
.EVT_PAINT
, self
.onPaint
) 
 669     def Update(self
, value
, rawvalue
): 
 670         # Updates the gauge itself, sets the raw value for 
 673         self
.rawvalue 
= rawvalue
 
 675     def onPaint(self
, evt
): 
 676         # Must always create a PaintDC when capturing 
 678         self
.ShowValue(wx
.PaintDC(self
), evt
) 
 680     def ShowValue(self
, dc
, evt
): 
 681         # This method handles actual painting of and drawing 
 684         # Clear out the gauge 
 686         # and then carry out business as usual 
 687         wx
.Gauge
.OnPaint(self
, evt
) 
 689         # This is the size available to us. 
 692         # This is what we will overlay on the gauge. 
 693         # It reflects the actual value received from the 
 695         txt 
= str(self
.rawvalue
) 
 697         # Copy the default font, make it bold. 
 699                 self
.GetFont().GetPointSize(), 
 700                 self
.GetFont().GetFamily(), 
 701                 self
.GetFont().GetStyle(), 
 705         # Set the font for the DC ... 
 707         # ... and calculate how much space our value 
 709         fw
, fh 
= dc
.GetTextExtent(txt
) 
 711         # Calc the center of the gauge, and from that 
 712         # derive the origin of our value. 
 719         # I draw the value twice so as to give it a pseudo-shadow. 
 720         # This is (mostly) because I'm too lazy to figure out how 
 721         # to blit my text onto the gauge using one of the logical 
 722         # functions. The pseudo-shadow gives the text contrast 
 723         # regardless of whether the bar is under it or not. 
 724         dc
.SetTextForeground(wx
.BLACK
) 
 725         dc
.DrawText(txt
, tx
, ty
) 
 727         dc
.SetTextForeground('white') 
 728         dc
.DrawText(txt
, tx
-1, ty
-1) 
 731 #---------------------------------------------------------------------------- 
 733 class Axis(wx
.Panel
): 
 735     # This class is a container for the min, max, and current 
 736     # values of the joystick axis in question. It contains 
 737     # also special features to render a 'dummy' if the axis 
 738     # in question is not available. 
 740     def __init__(self
, parent
, token
, stick
): 
 745         # token represents the type of axis we're displaying. 
 750         # Create a call to the 'Has*()' method for the stick. 
 751         # X and Y are always there, so we tie the Has* method 
 752         # to a hardwired True value. 
 754         if token 
not in ['X', 'Y']: 
 755             self
.HasFunc 
= eval('stick.Has%s' % token
) 
 757             self
.HasFunc 
= self
.alwaysTrue
 
 759         # Now init the panel. 
 760         wx
.Panel
.__init
__(self
, parent
, -1) 
 762         sizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 766             # Tie our calibration functions to the appropriate 
 767             # stick method. If we don't have the axis in question, 
 768             # we won't need them. 
 770             self
.GetMin 
= eval('stick.Get%sMin' % token
) 
 771             self
.GetMax 
= eval('stick.Get%sMax' % token
) 
 773             # Create our displays and set them up. 
 774             self
.Min 
= wx
.StaticText(self
, -1, str(self
.GetMin()), style
=wx
.ALIGN_RIGHT
) 
 775             self
.Max 
= wx
.StaticText(self
, -1, str(self
.GetMax()), style
=wx
.ALIGN_LEFT
) 
 776             self
.bar 
= AxisBar(self
) 
 778             sizer
.Add(self
.Min
, 0, wx
.ALL | wx
.ALIGN_RIGHT | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 779             sizer
.Add(self
.bar
, 1, wx
.ALL | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 780             sizer
.Add(self
.Max
, 0, wx
.ALL | wx
.ALIGN_LEFT | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 783             # We go here if the axis in question is not available. 
 784             self
.control 
= wx
.StaticText(self
, -1, '       *** Not Present ***') 
 785             sizer
.Add(self
.control
, 1, wx
.ALL | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 787         #---------------------------------------------------------------------------- 
 791         wx
.CallAfter(self
.Update
) 
 795         if not self
.HasFunc(): 
 798         self
.Min
.SetLabel(str(self
.GetMin())) 
 799         self
.Max
.SetLabel(str(self
.GetMax())) 
 803         # Don't bother if the axis doesn't exist. 
 804         if not self
.HasFunc(): 
 807         min = int(self
.Min
.GetLabel()) 
 808         max = int(self
.Max
.GetLabel()) 
 811         # Not all values are available from a wx.JoystickEvent, so I've elected 
 812         # to not use it at all. Therefore, we are getting our values direct from 
 813         # the stick. These values also seem to be more stable and reliable than 
 814         # those received from the event itself, so maybe it's a good idea to 
 815         # use the stick directly for your program. 
 817         # Here we either select the appropriate member of stick.GetPosition() or 
 818         # apply the appropriate Get*Position method call. 
 820         if self
.token 
== 'X': 
 821             val 
= self
.stick
.GetPosition().x
 
 822         elif self
.token 
== 'Y': 
 823             val 
= self
.stick
.GetPosition().y
 
 825             val 
= eval('self.stick.Get%sPosition()' % self
.token
) 
 829         # While we might be able to rely on a range of 0-FFFFFF on Win, that might 
 830         # not be true of all drivers on all platforms. Thus, calc the actual full 
 837         range = float(max - min) 
 840         # The relative value is used by the derived wx.Gauge since it is a 
 841         # positive-only control. 
 845             relative 
= int( val 
/ range * 1000) 
 848         # Pass both the raw and relative values to the derived Gauge 
 850         self
.bar
.Update(relative
, val
) 
 853     def alwaysTrue(self
): 
 854         # a dummy method used for X and Y axis. 
 858 #---------------------------------------------------------------------------- 
 860 class AxisPanel(wx
.Panel
): 
 862     # Contained herein is a panel that offers a graphical display 
 863     # of the levels for all axes supported by wx.Joystick. If 
 864     # your system doesn't have a particular axis, it will be 
 865     # 'dummied' for transparent use. 
 867     def __init__(self
, parent
, stick
): 
 871         # Defines labels and 'tokens' to identify each 
 874             ('X Axis ', 'X'),   ('Y Axis ', 'Y'), 
 875             ('Z Axis ', 'Z'),   ('Rudder ', 'Rudder'), 
 876             ('U Axis ', 'U'),   ('V Axis ', 'V') 
 879         # Contains a list of all axis initialized. 
 882         wx
.Panel
.__init
__(self
, parent
, -1) 
 884         sizer 
= wx
.FlexGridSizer(3, 4, 1, 1) 
 885         sizer
.AddGrowableCol(1) 
 886         sizer
.AddGrowableCol(3) 
 888         #---------------------------------------------------------------------------- 
 890         # Go through the list of labels and tokens and add a label and 
 891         # axis display to the sizer for each. 
 892         for label
, token 
in axesList
: 
 893             sizer
.Add(Label(self
, label
), 0, wx
.ALL | wx
.ALIGN_RIGHT
, 2) 
 894             t 
= Axis(self
, token
, self
.stick
) 
 896             sizer
.Add(t
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2) 
 898         #---------------------------------------------------------------------------- 
 902         wx
.CallAfter(self
.Update
) 
 913 #---------------------------------------------------------------------------- 
 915 class JoystickDemoPanel(wx
.Panel
): 
 917     def __init__(self
, parent
, log
): 
 921         wx
.Panel
.__init
__(self
, parent
, -1) 
 923         # Try to grab the control. If we get it, capture the stick. 
 924         # Otherwise, throw up an exception message and play stupid. 
 926             self
.stick 
= wx
.Joystick() 
 927             self
.stick
.SetCapture(self
) 
 928             # Calibrate our controls 
 929             wx
.CallAfter(self
.Calibrate
) 
 930             wx
.CallAfter(self
.OnJoystick
) 
 931         except NotImplementedError, v
: 
 932             wx
.MessageBox(str(v
), "Exception Message") 
 935         # One Sizer to Rule Them All... 
 936         sizer 
= wx
.GridBagSizer(2,2) 
 938         self
.info 
= InfoPanel(self
, self
.stick
) 
 939         sizer
.Add(self
.info
, (0, 0), (1, 3), wx
.ALL | wx
.GROW
, 2) 
 941         self
.info
.Bind(wx
.EVT_BUTTON
, self
.Calibrate
) 
 943         self
.joy 
= JoyPanel(self
, self
.stick
) 
 944         sizer
.Add(self
.joy
, (1, 0), (1, 1), wx
.ALL | wx
.GROW
, 2) 
 946         self
.pov 
= POVPanel(self
, self
.stick
) 
 947         sizer
.Add(self
.pov
, (1, 1), (1, 2), wx
.ALL | wx
.GROW
, 2) 
 949         self
.axes 
= AxisPanel(self
, self
.stick
) 
 950         sizer
.Add(self
.axes
, (2, 0), (1, 3), wx
.ALL | wx
.GROW
, 2) 
 952         self
.buttons 
= JoyButtons(self
, self
.stick
) 
 953         sizer
.Add(self
.buttons
, (3, 0), (1, 3), wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 958         # Capture Joystick events (if they happen) 
 959         self
.Bind(wx
.EVT_JOYSTICK_EVENTS
, self
.OnJoystick
) 
 960         self
.stick
.SetMovementThreshold(10) 
 963     def Calibrate(self
, evt
=None): 
 964         # Do not try this without a stick 
 968         self
.info
.Calibrate() 
 969         self
.axes
.Calibrate() 
 971         self
.buttons
.Calibrate() 
 974     def OnJoystick(self
, evt
=None): 
 981         if evt 
is not None and evt
.IsButton(): 
 982             self
.buttons
.Update() 
 985     def ShutdownDemo(self
): 
 987             self
.stick
.ReleaseCapture() 
 990 #---------------------------------------------------------------------------- 
 992 def runTest(frame
, nb
, log
): 
 994         win 
= JoystickDemoPanel(nb
, log
) 
 997         from Main 
import MessagePanel
 
 998         win 
= MessagePanel(nb
, 'wx.Joystick is not available on this platform.', 
 999                            'Sorry', wx
.ICON_WARNING
) 
1003 #---------------------------------------------------------------------------- 
1008 <h1>wx.Joystick</h1> 
1009 This demo illustrates the use of the wx.Joystick class, which is an interface to 
1010 one or more joysticks attached to your system. 
1012 <p>The data that can be retrieved from the joystick comes in four basic flavors. 
1013 All of these are illustrated in the demo. In fact, this demo illustrates everything 
1014 you <b>can</b> get from the wx.Joystick control. 
1017 <li>Static information such as Manufacturer ID and model name, 
1018 <li>Analog input from up to six axes, including X and Y for the actual stick, 
1019 <li>Button input from the fire button and any other buttons that the stick has, 
1020 <li>and the POV control (a kind of mini-joystick on top of the joystick) that many sticks come with. 
1023 <p>Getting data from the joystick can be event-driven thanks to four event types associated 
1024 with wx.JoystickEvent, or the joystick can be polled programatically to get data on 
1029 Data from the joystick comes in two flavors: that which defines the boundaries, and that 
1030 which defines the current state of the stick. Thus, we have Get*Max() and Get*Min()  
1031 methods for all axes, the max number of axes, the max number of buttons, and so on. In 
1032 general, this data can be read once and stored to speed computation up. 
1034 <h3>Analog Input</h3> 
1036 Analog input (the axes) is delivered as a whole, positive number. If you need to know  
1037 if the axis is at zero (centered) or not, you will first have to calculate that center 
1038 based on the max and min values. The demo shows a bar graph for each axis expressed 
1039 in native numerical format, plus a 'centered' X-Y axis compass showing the relationship 
1040 of that input to the calculated stick position. 
1042 Analog input may be jumpy and spurious, so the control has a means of 'smoothing' the 
1043 analog data by setting a movement threshold. This demo sets the threshold to 10, but 
1044 you can set it at any valid value between the min and max. 
1046 <h3>Button Input</h3> 
1048 Button state is retrieved as one int that contains each button state mapped to a bit. 
1049 You get the state of a button by AND-ing its bit against the returned value, in the form 
1052      # assume buttonState is what the stick returned, and buttonBit  
1053      # is the bit you want to examine 
1055      if (buttonState & ( 1 << buttonBit )) : 
1056          # button pressed, do something with it 
1059 <p>The problem here is that some OSs return a 32-bit value for up to 32 buttons  
1060 (imagine <i>that</i> stick!). Python V2.3 will generate an exception for bit  
1061 values over 30. For that reason, this demo is limited to 16 buttons. 
1063 <p>Note that more than one button can be pressed at a time, so be sure to check all of them! 
1068 POV hats come in two flavors: four-way, and continuous. four-way POVs are restricted to 
1069 the cardinal points of the compass; continuous, or CTS POV hats can deliver input in 
1070 .01 degree increments, theoreticaly. The data is returned as a whole number; the last 
1071 two digits are considered to be to the right of the decimal point, so in order to  
1072 use this information, you need to divide by 100 right off the bat.  
1074 <p>Different methods are provided to retrieve the POV data for a CTS hat  
1075 versus a four-way hat. 
1079 The wx.Joystick control is in many ways incomplete at the C++ library level, but it is 
1080 not insurmountable.  In short, while the joystick interface <i>can</i> be event-driven, 
1081 the wx.JoystickEvent class lacks event binders for all event types. Thus, you cannot 
1082 rely on wx.JoystickEvents to tell you when something has changed, necessarilly. 
1085 <li>There are no events associated with the POV control. 
1086 <li>There are no events associated with the Rudder 
1087 <li>There are no events associated with the U and V axes. 
1090 <p>Fortunately, there is an easy workaround. In the top level frame, create a wx.Timer 
1091 that will poll the stick at a set interval. Of course, if you do this, you might as 
1092 well forgo catching wxEVT_JOYSTICK_* events at all and rely on the timer to do the 
1095 <p>Ideally, the timer should be a one-shot; after it fires, collect and process data as  
1096 needed, then re-start the timer, possibly using wx.CallAfter(). 
1102 #---------------------------------------------------------------------------- 
1104 if __name__ 
== '__main__': 
1107     run
.main(['', os
.path
.basename(sys
.argv
[0])] + sys
.argv
[1:])