]>
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:])