]>
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             xrange = self
.stick
.GetXMax() - self
.stick
.GetXMin() 
 118             yrange 
= self
.stick
.GetYMax() - self
.stick
.GetYMin() 
 120             # calc a ratio of our range versus the joystick range 
 121             xratio 
= float(edgeSize
) / xrange 
 122             yratio 
= float(edgeSize
) / yrange
 
 124             # calc the displayable value based on position times ratio 
 125             xval 
= int(joyx 
* xratio
) 
 126             yval 
= int(joyy 
* xratio
) 
 128             # and normalize the value from our brush's origin 
 133             dc
.SetPen(wx
.Pen(wx
.RED
, 2)) 
 136         # Turn off drawing optimization 
 141         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 143         self
.DrawJoystick(dc
) 
 146 #---------------------------------------------------------------------------- 
 148 class JoyPanel(wx
.Panel
): 
 149     def __init__(self
, parent
, stick
): 
 153         wx
.Panel
.__init
__(self
, parent
, -1) 
 155         sizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 158                 parent
.GetFont().GetPointSize() + 3, 
 159                 parent
.GetFont().GetFamily(), 
 160                 parent
.GetFont().GetStyle(), 
 164         t 
= wx
.StaticText(self
, -1, "X - Y Axes", style 
= wx
.ALIGN_CENTRE
) 
 166         sizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_HORIZONTAL
, 1) 
 168         self
.control 
= JoyGauge(self
, self
.stick
) 
 169         sizer
.Add(self
.control
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_HORIZONTAL
, 1) 
 175         self
.control
.Update() 
 178 #---------------------------------------------------------------------------- 
 180 class POVGauge(wx
.Panel
): 
 182     # Display the current postion of the POV control 
 184     def __init__(self
, parent
, stick
): 
 187         self
.size 
= (100, 100) 
 192         wx
.Panel
.__init
__(self
, parent
, -1, size
=self
.size
) 
 194         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 195         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 196         self
.Bind(wx
.EVT_ERASE_BACKGROUND
, lambda e
: None) 
 198         self
.buffer = wx
.EmptyBitmap(*self
.size
) 
 199         dc 
= wx
.BufferedDC(None, self
.buffer) 
 204     def OnSize(self
, event
): 
 205         # calculate the size of our display and make a buffer for it. 
 206         w
, h  
= self
.GetClientSize() 
 209         self
.buffer = wx
.EmptyBitmap(w
,h
) 
 210         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 215     def DrawFace(self
, dc
): 
 216         dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour())) 
 220     def OnPaint(self
, evt
): 
 221         # When dc is destroyed it will blit self.buffer to the window, 
 222         # since no other drawing is needed we'll just return and let it 
 224         dc 
= wx
.BufferedPaintDC(self
, self
.buffer) 
 227     def DrawPOV(self
, dc
): 
 228         # draw the guage as a maxed circle in the center of this window. 
 229         w
, h 
= self
.GetClientSize() 
 232         xorigin 
= (w 
- diameter
) / 2 
 233         yorigin 
= (h 
- diameter
) / 2 
 234         xcenter 
= xorigin 
+ diameter 
/ 2 
 235         ycenter 
= yorigin 
+ diameter 
/ 2 
 237         # Optimize drawing a bit (for Win) 
 241         dc
.SetBrush(wx
.Brush(wx
.WHITE
)) 
 242         dc
.DrawCircle((xcenter
, ycenter
), diameter
/2) 
 243         dc
.SetBrush(wx
.Brush(wx
.BLACK
)) 
 244         dc
.DrawCircle((xcenter
, ycenter
), 10) 
 247         dc
.SetPen(wx
.Pen(wx
.BLACK
, 1, wx
.DOT_DASH
)) 
 248         dc
.DrawLine((xorigin
, ycenter
), (xorigin 
+ diameter
, ycenter
)) 
 249         dc
.DrawLine((xcenter
, yorigin
), (xcenter
, yorigin 
+ diameter
)) 
 256                 # use the appropriate function to get the POV position 
 258                     pos 
= self
.stick
.GetPOVPosition() 
 261                     pos 
= self
.stick
.GetPOVCTSPosition() 
 263                 # trap invalid values 
 264                 if 0 <= pos 
<= 36000: 
 269                 # rotate CCW by 90 so that 0 is up. 
 270                 pos 
= (pos 
/ 100) - 90 
 276                 # Stolen from wx.lib.analogclock :-) 
 277                 radiansPerDegree 
= math
.pi 
/ 180 
 278                 pointX 
= int(round(vector 
* math
.cos(pos 
* radiansPerDegree
))) 
 279                 pointY 
= int(round(vector 
* math
.sin(pos 
* radiansPerDegree
))) 
 281                 # normalise value to match our actual center. 
 282                 nx 
= pointX 
+ xcenter
 
 283                 ny 
= pointY 
+ ycenter
 
 286                 dc
.SetPen(wx
.Pen(wx
.BLUE
, 2)) 
 287                 dc
.DrawLine((xcenter
, ycenter
), (nx
, ny
)) 
 289                 # And a little thing to show the endpoint 
 290                 dc
.SetBrush(wx
.Brush(wx
.BLUE
)) 
 291                 dc
.DrawCircle((nx
, ny
), 8) 
 293         # Turn off drawing optimization 
 298         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 305         self
.avail 
= s
.HasPOV() 
 306         self
.fourDir 
= s
.HasPOV4Dir() 
 307         self
.cts 
= s
.HasPOVCTS() 
 310 #---------------------------------------------------------------------------- 
 312 class POVStatus(wx
.Panel
): 
 314     # Displays static info about the POV control 
 316     def __init__(self
, parent
, stick
): 
 320         wx
.Panel
.__init
__(self
, parent
, -1, size
=(100, 100)) 
 322         sizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 325         self
.avail 
= wx
.CheckBox(self
, -1, "Available") 
 326         sizer
.Add(self
.avail
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2) 
 328         self
.fourDir 
= wx
.CheckBox(self
, -1, "4-Way Only") 
 329         sizer
.Add(self
.fourDir
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2) 
 331         self
.cts 
= wx
.CheckBox(self
, -1, "Continuous") 
 332         sizer
.Add(self
.cts
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2) 
 337         # Effectively makes the checkboxes read-only. 
 338         self
.Bind(wx
.EVT_CHECKBOX
, self
.Calibrate
) 
 341     def Calibrate(self
, evt
=None): 
 343         self
.avail
.SetValue(s
.HasPOV()) 
 344         self
.fourDir
.SetValue(s
.HasPOV4Dir()) 
 345         self
.cts
.SetValue(s
.HasPOVCTS()) 
 348 #---------------------------------------------------------------------------- 
 350 class POVPanel(wx
.Panel
): 
 351     def __init__(self
, parent
, stick
): 
 355         wx
.Panel
.__init
__(self
, parent
, -1, size
=(100, 100)) 
 357         sizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 358         gsizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 363                 parent
.GetFont().GetPointSize() + 3, 
 364                 parent
.GetFont().GetFamily(), 
 365                 parent
.GetFont().GetStyle(), 
 368         t 
= wx
.StaticText(self
, -1, "POV Control", style 
= wx
.ALIGN_CENTER
) 
 370         gsizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND
, 1) 
 372         self
.display 
= POVGauge(self
, stick
) 
 373         gsizer
.Add(self
.display
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1) 
 374         sizer
.Add(gsizer
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1) 
 376         self
.status 
= POVStatus(self
, stick
) 
 377         sizer
.Add(self
.status
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1) 
 384         self
.display
.Calibrate() 
 385         self
.status
.Calibrate() 
 389         self
.display
.Update() 
 392 #---------------------------------------------------------------------------- 
 395     def __init__(self
, parent
, number
): 
 402                 parent
.GetFont().GetPointSize() - 1, 
 403                 parent
.GetFont().GetFamily(), 
 404                 parent
.GetFont().GetStyle(), 
 408         wx
.Panel
.__init
__(self
, parent
, -1, size
=self
.size
) 
 410         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 411         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 412         self
.Bind(wx
.EVT_ERASE_BACKGROUND
, lambda e
: None) 
 414         self
.buffer = wx
.EmptyBitmap(*self
.size
) 
 415         dc 
= wx
.BufferedDC(None, self
.buffer) 
 420     def OnSize(self
, event
): 
 421         # calculate the size of our display. 
 422         w
, h  
= self
.GetClientSize() 
 425         self
.buffer = wx
.EmptyBitmap(*self
.size
) 
 426         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 431     def DrawFace(self
, dc
): 
 432         dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour())) 
 436     def OnPaint(self
, evt
): 
 437         # When dc is destroyed it will blit self.buffer to the window, 
 438         # since no other drawing is needed we'll just return and let it 
 440         dc 
= wx
.BufferedPaintDC(self
, self
.buffer) 
 443     def DrawLED(self
, dc
): 
 450         # calc the 0, 0 origin of the bitmap 
 451         xorigin 
= center 
- (bw 
/ 2) 
 452         yorigin 
= center 
- (bh 
/ 2) 
 454         # Optimize drawing a bit (for Win) 
 459             dc
.SetBrush(wx
.Brush(wx
.RED
)) 
 460         elif self
.state 
== 1: 
 461             dc
.SetBrush(wx
.Brush(wx
.GREEN
)) 
 463             dc
.SetBrush(wx
.Brush(wx
.BLACK
)) 
 465         dc
.DrawCircle((center
, center
), bw
/2) 
 467         txt 
= str(self
.number
) 
 469         # Set the font for the DC ... 
 471         # ... and calculate how much space our value 
 473         fw
, fh 
= dc
.GetTextExtent(txt
) 
 475         # Calc the center of the LED, and from that 
 476         # derive the origin of our value. 
 480         # I draw the value twice so as to give it a pseudo-shadow. 
 481         # This is (mostly) because I'm too lazy to figure out how 
 482         # to blit my text onto the gauge using one of the logical 
 483         # functions. The pseudo-shadow gives the text contrast 
 484         # regardless of whether the bar is under it or not. 
 485         dc
.SetTextForeground(wx
.WHITE
) 
 486         dc
.DrawText(txt
, (tx
, ty
)) 
 488         # Turn off drawing optimization 
 493         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer) 
 498 #---------------------------------------------------------------------------- 
 500 class JoyButtons(wx
.Panel
): 
 501     def __init__(self
, parent
, stick
): 
 506         wx
.Panel
.__init
__(self
, parent
, -1) 
 508         tsizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 511                 parent
.GetFont().GetPointSize() + 3, 
 512                 parent
.GetFont().GetFamily(), 
 513                 parent
.GetFont().GetStyle(), 
 517         t 
= wx
.StaticText(self
, -1, "Buttons", style 
= wx
.ALIGN_LEFT
) 
 519         tsizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 1) 
 521         sizer 
= wx
.FlexGridSizer(4, 16, 2, 2) 
 523         fn
.SetPointSize(parent
.GetFont().GetPointSize() + 1) 
 525         for i 
in range(0, MAX_BUTTONS
): 
 528             sizer
.Add(t
, 1, wx
.ALL|wx
.ALIGN_CENTER|wx
.ALIGN_CENTER_VERTICAL
, 1) 
 529             sizer
.AddGrowableCol(i
) 
 531         tsizer
.Add(sizer
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 1) 
 533         self
.SetSizer(tsizer
) 
 537         for i 
in range(0, MAX_BUTTONS
): 
 538             self
.leds
[i
].state 
= -1 
 540         t 
= self
.stick
.GetNumberButtons() 
 542         for i 
in range(0, t
): 
 543             self
.leds
[i
].state 
= 0 
 546         t 
= self
.stick
.GetButtonState() 
 548         for i 
in range(0, MAX_BUTTONS
): 
 549             if self
.leds
[i
].state 
== 1: 
 550                 self
.leds
[i
].state 
= 0 
 553                 self
.leds
[i
].state 
= 1 
 555             self
.leds
[i
].Update() 
 558 #---------------------------------------------------------------------------- 
 560 class InfoPanel(wx
.Panel
): 
 561     def __init__(self
, parent
, stick
): 
 565         wx
.Panel
.__init
__(self
, parent
, -1) 
 567         sizer 
= wx
.GridBagSizer(1, 1) 
 569         sizer
.Add(Label(self
, 'Mfr ID: '), (0, 0), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 570         self
.MfgID 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 571         sizer
.Add(self
.MfgID
, (0, 1), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 573         sizer
.Add(Label(self
, 'Prod Name: '), (0, 2), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 574         self
.ProdName 
= wx
.TextCtrl(self
, -1, value
='', style
=wx
.TE_READONLY
) 
 575         sizer
.Add(self
.ProdName
, (0, 3), (1, 3), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 577         sizer
.Add(Label(self
, 'Threshold: '), (0, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 578         self
.Threshold 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 579         sizer
.Add(self
.Threshold
, (0, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 581         #---------------------------------------------------------------------------- 
 582         b 
= wx
.Button(self
, -1, "Calibrate") 
 583         sizer
.Add(b
, (1, 0), (2, 2), wx
.ALL | wx
.ALIGN_CENTER
, 2) 
 585         sizer
.Add(Label(self
, '# of Sticks: '), (1, 2), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 586         self
.NumJoysticks 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 587         sizer
.Add(self
.NumJoysticks
, (1, 3), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 589         sizer
.Add(Label(self
, '# of Axes: '), (1, 4), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 590         self
.NumAxis 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 591         sizer
.Add(self
.NumAxis
, (1, 5), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 593         sizer
.Add(Label(self
, 'Max # Axes: '), (1, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 594         self
.MaxAxis 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 595         sizer
.Add(self
.MaxAxis
, (1, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 597         #---------------------------------------------------------------------------- 
 599         sizer
.Add(Label(self
, 'Polling -- '), (2, 3), (1, 1), wx
.ALL | wx
.GROW
, 2) 
 601         sizer
.Add(Label(self
, 'Min: '), (2, 4), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 602         self
.PollMin 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 603         sizer
.Add(self
.PollMin
, (2, 5), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 605         sizer
.Add(Label(self
, 'Max: '), (2, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2) 
 606         self
.PollMax 
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
) 
 607         sizer
.Add(self
.PollMax
, (2, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2) 
 609         #---------------------------------------------------------------------------- 
 621         self
.MfgID
.SetValue(str(s
.GetManufacturerId())) 
 622         self
.ProdName
.SetValue(str(s
.GetProductName())) 
 623         self
.Threshold
.SetValue(str(s
.GetMovementThreshold())) 
 624         self
.NumJoysticks
.SetValue(str(s
.GetNumberJoysticks())) 
 625         self
.NumAxis
.SetValue(str(s
.GetNumberAxes())) 
 626         self
.MaxAxis
.SetValue(str(s
.GetMaxAxes())) 
 627         self
.PollMin
.SetValue(str(s
.GetPollingMin())) 
 628         self
.PollMax
.SetValue(str(s
.GetPollingMax())) 
 631 #---------------------------------------------------------------------------- 
 633 class AxisBar(wx
.Gauge
): 
 635     # This class allows us to use a wx.Gauge to display the axis value 
 636     # with a fancy label overlayed onto the guage itself. Two values are 
 637     # used to do things: first of all, since the gauge is limited to 
 638     # positive numbers, the scale is fixed at 0 to 1000. We will receive 
 639     # an adjusted value to use to render the gauge itself. The other value 
 640     # is a raw value and actually reflects the value from the joystick itself, 
 641     # which is then drawn over the gauge. 
 643     def __init__(self
, parent
): 
 644         wx
.Gauge
.__init
__(self
, parent
, -1, 1000, size
=(-1, 20), style 
= wx
.GA_HORIZONTAL | wx
.GA_SMOOTH 
) 
 646         # This is the value we will display. 
 649         self
.SetBackgroundColour('light blue') 
 650         self
.SetForegroundColour('orange') 
 652         # Capture paint events for purpose of updating 
 653         # the displayed value. 
 654         self
.Bind(wx
.EVT_PAINT
, self
.onPaint
) 
 656     def Update(self
, value
, rawvalue
): 
 657         # Updates the gauge itself, sets the raw value for 
 660         self
.rawvalue 
= rawvalue
 
 662     def onPaint(self
, evt
): 
 663         # Must always create a PaintDC when capturing 
 665         self
.ShowValue(wx
.PaintDC(self
), evt
) 
 667     def ShowValue(self
, dc
, evt
): 
 668         # This method handles actual painting of and drawing 
 671         # Clear out the gauge 
 673         # and then carry out business as usual 
 674         wx
.Gauge
.OnPaint(self
, evt
) 
 676         # This is the size available to us. 
 679         # This is what we will overlay on the gauge. 
 680         # It reflects the actual value received from the 
 682         txt 
= str(self
.rawvalue
) 
 684         # Copy the default font, make it bold. 
 686                 self
.GetFont().GetPointSize(), 
 687                 self
.GetFont().GetFamily(), 
 688                 self
.GetFont().GetStyle(), 
 692         # Set the font for the DC ... 
 694         # ... and calculate how much space our value 
 696         fw
, fh 
= dc
.GetTextExtent(txt
) 
 698         # Calc the center of the gauge, and from that 
 699         # derive the origin of our value. 
 706         # I draw the value twice so as to give it a pseudo-shadow. 
 707         # This is (mostly) because I'm too lazy to figure out how 
 708         # to blit my text onto the gauge using one of the logical 
 709         # functions. The pseudo-shadow gives the text contrast 
 710         # regardless of whether the bar is under it or not. 
 711         dc
.SetTextForeground(wx
.BLACK
) 
 712         dc
.DrawText(txt
, (tx
, ty
)) 
 714         dc
.SetTextForeground('white') 
 715         dc
.DrawText(txt
, (tx
-1, ty
-1)) 
 718 #---------------------------------------------------------------------------- 
 720 class Axis(wx
.Panel
): 
 722     # This class is a container for the min, max, and current 
 723     # values of the joystick axis in question. It contains 
 724     # also special features to render a 'dummy' if the axis 
 725     # in question is not available. 
 727     def __init__(self
, parent
, token
, stick
): 
 732         # token represents the type of axis we're displaying. 
 737         # Create a call to the 'Has*()' method for the stick. 
 738         # X and Y are always there, so we tie the Has* method 
 739         # to a hardwired True value. 
 741         if token 
not in ['X', 'Y']: 
 742             self
.HasFunc 
= eval('stick.Has%s' % token
) 
 744             self
.HasFunc 
= self
.alwaysTrue
 
 746         # Now init the panel. 
 747         wx
.Panel
.__init
__(self
, parent
, -1) 
 749         sizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 753             # Tie our calibration functions to the appropriate 
 754             # stick method. If we don't have the axis in question, 
 755             # we won't need them. 
 757             self
.GetMin 
= eval('stick.Get%sMin' % token
) 
 758             self
.GetMax 
= eval('stick.Get%sMax' % token
) 
 760             # Create our displays and set them up. 
 761             self
.Min 
= wx
.StaticText(self
, -1, str(self
.GetMin()), 
 762                                      size
=(40,-1), style
=wx
.ALIGN_RIGHT | wx
.ST_NO_AUTORESIZE
) 
 763             self
.Max 
= wx
.StaticText(self
, -1, str(self
.GetMax()), 
 764                                      size
=(40,-1), style
=wx
.ALIGN_LEFT | wx
.ST_NO_AUTORESIZE
) 
 765             self
.bar 
= AxisBar(self
) 
 767             sizer
.Add(self
.Min
, 0, wx
.ALL | wx
.ALIGN_RIGHT | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 768             sizer
.Add(self
.bar
, 1, wx
.ALL | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 769             sizer
.Add(self
.Max
, 0, wx
.ALL | wx
.ALIGN_LEFT | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 772             # We go here if the axis in question is not available. 
 773             self
.control 
= wx
.StaticText(self
, -1, '       *** Not Present ***') 
 774             sizer
.Add(self
.control
, 1, wx
.ALL | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 776         #---------------------------------------------------------------------------- 
 780         wx
.CallAfter(self
.Update
) 
 784         if not self
.HasFunc(): 
 787         self
.Min
.SetLabel(str(self
.GetMin())) 
 788         self
.Max
.SetLabel(str(self
.GetMax())) 
 792         # Don't bother if the axis doesn't exist. 
 793         if not self
.HasFunc(): 
 796         min = int(self
.Min
.GetLabel()) 
 797         max = int(self
.Max
.GetLabel()) 
 800         # Not all values are available from a wx.JoystickEvent, so I've elected 
 801         # to not use it at all. Therefore, we are getting our values direct from 
 802         # the stick. These values also seem to be more stable and reliable than 
 803         # those received from the event itself, so maybe it's a good idea to 
 804         # use the stick directly for your program. 
 806         # Here we either select the appropriate member of stick.GetPosition() or 
 807         # apply the appropriate Get*Position method call. 
 809         if self
.token 
== 'X': 
 810             val 
= self
.stick
.GetPosition().x
 
 811         elif self
.token 
== 'Y': 
 812             val 
= self
.stick
.GetPosition().y
 
 814             val 
= eval('self.stick.Get%sPosition()' % self
.token
) 
 817         # While we might be able to rely on a range of 0-FFFFFF on Win, that might 
 818         # not be true of all drivers on all platforms. Thus, calc the actual full 
 821         range = float(max - min) 
 824         # The relative value is used by the derived wx.Gauge since it is a 
 825         # positive-only control. 
 829             relative 
= int(val 
/ range * 1000) 
 832         # Pass both the raw and relative values to the derived Gauge 
 834         self
.bar
.Update(relative
, val
) 
 837     def alwaysTrue(self
): 
 838         # a dummy method used for X and Y axis. 
 842 #---------------------------------------------------------------------------- 
 844 class AxisPanel(wx
.Panel
): 
 846     # Contained herein is a panel that offers a graphical display 
 847     # of the levels for all axes supported by wx.Joystick. If 
 848     # your system doesn't have a particular axis, it will be 
 849     # 'dummied' for transparent use. 
 851     def __init__(self
, parent
, stick
): 
 855         # Defines labels and 'tokens' to identify each 
 858             ('X Axis ', 'X'),   ('Y Axis ', 'Y'), 
 859             ('Z Axis ', 'Z'),   ('Rudder ', 'Rudder'), 
 860             ('U Axis ', 'U'),   ('V Axis ', 'V') 
 863         # Contains a list of all axis initialized. 
 866         wx
.Panel
.__init
__(self
, parent
, -1) 
 868         sizer 
= wx
.FlexGridSizer(3, 4, 1, 1) 
 869         sizer
.AddGrowableCol(1) 
 870         sizer
.AddGrowableCol(3) 
 872         #---------------------------------------------------------------------------- 
 874         # Go through the list of labels and tokens and add a label and 
 875         # axis display to the sizer for each. 
 876         for label
, token 
in axesList
: 
 877             sizer
.Add(Label(self
, label
), 0, wx
.ALL | wx
.ALIGN_RIGHT
, 2) 
 878             t 
= Axis(self
, token
, self
.stick
) 
 880             sizer
.Add(t
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2) 
 882         #---------------------------------------------------------------------------- 
 886         wx
.CallAfter(self
.Update
) 
 897 #---------------------------------------------------------------------------- 
 899 class JoystickDemoPanel(wx
.Panel
): 
 901     def __init__(self
, parent
, log
): 
 905         wx
.Panel
.__init
__(self
, parent
, -1) 
 907         # Try to grab the control. If we get it, capture the stick. 
 908         # Otherwise, throw up an exception message and play stupid. 
 910             self
.stick 
= wx
.Joystick() 
 911             self
.stick
.SetCapture(self
) 
 912             # Calibrate our controls 
 913             wx
.CallAfter(self
.Calibrate
) 
 914             wx
.CallAfter(self
.OnJoystick
) 
 915         except NotImplementedError, v
: 
 916             wx
.MessageBox(str(v
), "Exception Message") 
 919         # One Sizer to Rule Them All... 
 920         sizer 
= wx
.GridBagSizer(2,2) 
 922         self
.info 
= InfoPanel(self
, self
.stick
) 
 923         sizer
.Add(self
.info
, (0, 0), (1, 3), wx
.ALL | wx
.GROW
, 2) 
 925         self
.info
.Bind(wx
.EVT_BUTTON
, self
.Calibrate
) 
 927         self
.joy 
= JoyPanel(self
, self
.stick
) 
 928         sizer
.Add(self
.joy
, (1, 0), (1, 1), wx
.ALL | wx
.GROW
, 2) 
 930         self
.pov 
= POVPanel(self
, self
.stick
) 
 931         sizer
.Add(self
.pov
, (1, 1), (1, 2), wx
.ALL | wx
.GROW
, 2) 
 933         self
.axes 
= AxisPanel(self
, self
.stick
) 
 934         sizer
.Add(self
.axes
, (2, 0), (1, 3), wx
.ALL | wx
.GROW
, 2) 
 936         self
.buttons 
= JoyButtons(self
, self
.stick
) 
 937         sizer
.Add(self
.buttons
, (3, 0), (1, 3), wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1) 
 942         # Capture Joystick events (if they happen) 
 943         self
.Bind(wx
.EVT_JOYSTICK_EVENTS
, self
.OnJoystick
) 
 945         self
.stick
.SetMovementThreshold(10) 
 947     def Calibrate(self
, evt
=None): 
 948         # Do not try this without a stick 
 952         self
.info
.Calibrate() 
 953         self
.axes
.Calibrate() 
 955         self
.buttons
.Calibrate() 
 957     def OnJoystick(self
, evt
=None): 
 964         self
.buttons
.Update() 
 967 #---------------------------------------------------------------------------- 
 969 def runTest(frame
, nb
, log
): 
 971         win 
= JoystickDemoPanel(nb
, log
) 
 974         dlg 
= wx
.MessageDialog( 
 975                 frame
, 'wx.Joystick is not available on this platform.', 
 976                 'Sorry', wx
.OK | wx
.ICON_INFORMATION
 
 982 #---------------------------------------------------------------------------- 
 988 This demo illustrates the use of the wx.Joystick class, which is an interface to 
 989 one or more joysticks attached to your system. 
 991 <p>The data that can be retrieved from the joystick comes in four basic flavors. 
 992 All of these are illustrated in the demo. In fact, this demo illustrates everything 
 993 you <b>can</b> get from the wx.Joystick control. 
 996 <li>Static information such as Manufacturer ID and model name, 
 997 <li>Analog input from up to six axes, including X and Y for the actual stick, 
 998 <li>Button input from the fire button and any other buttons that the stick has, 
 999 <li>and the POV control (a kind of mini-joystick on top of the joystick) that many sticks come with. 
1002 <p>Getting data from the joystick can be event-driven thanks to four event types associated 
1003 with wx.JoystickEvent, or the joystick can be polled programatically to get data on 
1008 Data from the joystick comes in two flavors: that which defines the boundaries, and that 
1009 which defines the current state of the stick. Thus, we have Get*Max() and Get*Min()  
1010 methods for all axes, the max number of axes, the max number of buttons, and so on. In 
1011 general, this data can be read once and stored to speed computation up. 
1013 <h3>Analog Input</h3> 
1015 Analog input (the axes) is delivered as a whole, positive number. If you need to know  
1016 if the axis is at zero (centered) or not, you will first have to calculate that center 
1017 based on the max and min values. The demo shows a bar graph for each axis expressed 
1018 in native numerical format, plus a 'centered' X-Y axis compass showing the relationship 
1019 of that input to the calculated stick position. 
1021 Analog input may be jumpy and spurious, so the control has a means of 'smoothing' the 
1022 analog data by setting a movement threshold. This demo sets the threshold to 10, but 
1023 you can set it at any valid value between the min and max. 
1025 <h3>Button Input</h3> 
1027 Button state is retrieved as one int that contains each button state mapped to a bit. 
1028 You get the state of a button by AND-ing its bit against the returned value, in the form 
1031      # assume buttonState is what the stick returned, and buttonBit  
1032      # is the bit you want to examine 
1034      if (buttonState & ( 1 << buttonBit )) : 
1035          # button pressed, do something with it 
1038 <p>The problem here is that some OSs return a 32-bit value for up to 32 buttons  
1039 (imagine <i>that</i> stick!). Python V2.3 will generate an exception for bit  
1040 values over 30. For that reason, this demo is limited to 16 buttons. 
1042 <p>Note that more than one button can be pressed at a time, so be sure to check all of them! 
1047 POV hats come in two flavors: four-way, and continuous. four-way POVs are restricted to 
1048 the cardinal points of the compass; continuous, or CTS POV hats can deliver input in 
1049 .01 degree increments, theoreticaly. The data is returned as a whole number; the last 
1050 two digits are considered to be to the right of the decimal point, so in order to  
1051 use this information, you need to divide by 100 right off the bat.  
1053 <p>Different methods are provided to retrieve the POV data for a CTS hat  
1054 versus a four-way hat. 
1058 The wx.Joystick control is in many ways incomplete at the C++ library level, but it is 
1059 not insurmountable.  In short, while the joystick interface <i>can</i> be event-driven, 
1060 the wx.JoystickEvent class lacks event binders for all event types. Thus, you cannot 
1061 rely on wx.JoystickEvents to tell you when something has changed, necessarilly. 
1064 <li>There are no events associated with the POV control. 
1065 <li>There are no events associated with the Rudder 
1066 <li>There are no events associated with the U and V axes. 
1069 <p>Fortunately, there is an easy workaround. In the top level frame, create a wx.Timer 
1070 that will poll the stick at a set interval. Of course, if you do this, you might as 
1071 well forgo catching wxEVT_JOYSTICK_* events at all and rely on the timer to do the 
1074 <p>Ideally, the timer should be a one-shot; after it fires, collect and process data as  
1075 needed, then re-start the timer, possibly using wx.CallAfter(). 
1081 #---------------------------------------------------------------------------- 
1083 if __name__ 
== '__main__': 
1086     run
.main(['', os
.path
.basename(sys
.argv
[0])] + sys
.argv
[1:])