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