]>
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 #----------------------------------------------------------------------------
18 #----------------------------------------------------------------------------
20 # Once all supported versions of Python support 32-bit integers on all
21 # platforms, this can go up to 32.
24 #----------------------------------------------------------------------------
26 class Label(wx
.StaticText
):
27 # A derived StaticText that always aligns right and renders
29 def __init__(self
, parent
, label
):
30 wx
.StaticText
.__init
__(self
, parent
, -1, label
, style
=wx
.ALIGN_RIGHT
)
34 parent
.GetFont().GetPointSize(),
35 parent
.GetFont().GetFamily(),
36 parent
.GetFont().GetStyle(),
40 #----------------------------------------------------------------------------
43 class JoyGauge(wx
.Panel
):
44 def __init__(self
, parent
, stick
):
49 wx
.Panel
.__init
__(self
, parent
, -1, size
=size
)
51 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
52 self
.Bind(wx
.EVT_SIZE
, self
.OnSize
)
53 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, lambda e
: None)
55 self
.buffer = wx
.EmptyBitmap(*size
)
56 dc
= wx
.BufferedDC(None, self
.buffer)
61 def OnSize(self
, event
):
62 # The face Bitmap init is done here, to make sure the buffer is always
63 # the same size as the Window
64 w
, h
= self
.GetClientSize()
65 self
.buffer = wx
.EmptyBitmap(w
,h
)
66 dc
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer)
71 def DrawFace(self
, dc
):
72 dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour()))
76 def OnPaint(self
, evt
):
77 # When dc is destroyed it will blit self.buffer to the window,
78 # since no other drawing is needed we'll just return and let it
80 dc
= wx
.BufferedPaintDC(self
, self
.buffer)
83 def DrawJoystick(self
, dc
):
84 # draw the guage as a maxed square in the center of this window.
85 w
, h
= self
.GetClientSize()
88 xorigin
= (w
- edgeSize
) / 2
89 yorigin
= (h
- edgeSize
) / 2
92 # Restrict our drawing activities to the square defined
94 dc
.SetClippingRegion((xorigin
, yorigin
), (edgeSize
, edgeSize
))
96 # Optimize drawing a bit (for Win)
99 dc
.SetBrush(wx
.Brush(wx
.Colour(251, 252, 237)))
100 dc
.DrawRectangle((xorigin
, yorigin
), (edgeSize
, edgeSize
))
102 dc
.SetPen(wx
.Pen(wx
.BLACK
, 1, wx
.DOT_DASH
))
104 dc
.DrawLine((xorigin
, yorigin
+ center
), (xorigin
+ edgeSize
, yorigin
+ center
))
105 dc
.DrawLine((xorigin
+ center
, yorigin
), (xorigin
+ center
, yorigin
+ edgeSize
))
108 # Get the joystick position as a float
109 joyx
= float(self
.stick
.GetPosition().x
)
110 joyy
= float(self
.stick
.GetPosition().y
)
112 # Get the joystick range of motion
113 xrange = self
.stick
.GetXMax() - self
.stick
.GetXMin()
114 yrange
= self
.stick
.GetYMax() - self
.stick
.GetYMin()
116 # calc a ratio of our range versus the joystick range
117 xratio
= float(edgeSize
) / xrange
118 yratio
= float(edgeSize
) / yrange
120 # calc the displayable value based on position times ratio
121 xval
= int(joyx
* xratio
)
122 yval
= int(joyy
* xratio
)
124 # and normalize the value from our brush's origin
129 dc
.SetPen(wx
.Pen(wx
.RED
, 2))
132 # Turn off drawing optimization
137 dc
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer)
139 self
.DrawJoystick(dc
)
142 #----------------------------------------------------------------------------
144 class JoyPanel(wx
.Panel
):
145 def __init__(self
, parent
, stick
):
149 wx
.Panel
.__init
__(self
, parent
, -1)
151 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
154 parent
.GetFont().GetPointSize() + 3,
155 parent
.GetFont().GetFamily(),
156 parent
.GetFont().GetStyle(),
160 t
= wx
.StaticText(self
, -1, "X - Y Axes", style
= wx
.ALIGN_CENTRE
)
162 sizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_HORIZONTAL
, 1)
164 self
.control
= JoyGauge(self
, self
.stick
)
165 sizer
.Add(self
.control
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_HORIZONTAL
, 1)
171 self
.control
.Update()
174 #----------------------------------------------------------------------------
176 class POVGauge(wx
.Panel
):
178 # Display the current postion of the POV control
180 def __init__(self
, parent
, stick
):
183 self
.size
= (100, 100)
188 wx
.Panel
.__init
__(self
, parent
, -1, size
=self
.size
)
190 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
191 self
.Bind(wx
.EVT_SIZE
, self
.OnSize
)
192 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, lambda e
: None)
194 self
.buffer = wx
.EmptyBitmap(*self
.size
)
195 dc
= wx
.BufferedDC(None, self
.buffer)
200 def OnSize(self
, event
):
201 # calculate the size of our display and make a buffer for it.
202 w
, h
= self
.GetClientSize()
205 self
.buffer = wx
.EmptyBitmap(w
,h
)
206 dc
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer)
211 def DrawFace(self
, dc
):
212 dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour()))
216 def OnPaint(self
, evt
):
217 # When dc is destroyed it will blit self.buffer to the window,
218 # since no other drawing is needed we'll just return and let it
220 dc
= wx
.BufferedPaintDC(self
, self
.buffer)
223 def DrawPOV(self
, dc
):
224 # draw the guage as a maxed circle in the center of this window.
225 w
, h
= self
.GetClientSize()
228 xorigin
= (w
- diameter
) / 2
229 yorigin
= (h
- diameter
) / 2
230 xcenter
= xorigin
+ diameter
/ 2
231 ycenter
= yorigin
+ diameter
/ 2
233 # Optimize drawing a bit (for Win)
237 dc
.SetBrush(wx
.Brush(wx
.WHITE
))
238 dc
.DrawCircle((xcenter
, ycenter
), diameter
/2)
239 dc
.SetBrush(wx
.Brush(wx
.BLACK
))
240 dc
.DrawCircle((xcenter
, ycenter
), 10)
243 dc
.SetPen(wx
.Pen(wx
.BLACK
, 1, wx
.DOT_DASH
))
244 dc
.DrawLine((xorigin
, ycenter
), (xorigin
+ diameter
, ycenter
))
245 dc
.DrawLine((xcenter
, yorigin
), (xcenter
, yorigin
+ diameter
))
252 # use the appropriate function to get the POV position
254 pos
= self
.stick
.GetPOVPosition()
257 pos
= self
.stick
.GetPOVCTSPosition()
259 # trap invalid values
260 if 0 <= pos
<= 36000:
265 # rotate CCW by 90 so that 0 is up.
266 pos
= (pos
/ 100) - 90
272 # Stolen from wx.lib.analogclock :-)
273 radiansPerDegree
= math
.pi
/ 180
274 pointX
= int(round(vector
* math
.cos(pos
* radiansPerDegree
)))
275 pointY
= int(round(vector
* math
.sin(pos
* radiansPerDegree
)))
277 # normalise value to match our actual center.
278 nx
= pointX
+ xcenter
279 ny
= pointY
+ ycenter
282 dc
.SetPen(wx
.Pen(wx
.BLUE
, 2))
283 dc
.DrawLine((xcenter
, ycenter
), (nx
, ny
))
285 # And a little thing to show the endpoint
286 dc
.SetBrush(wx
.Brush(wx
.BLUE
))
287 dc
.DrawCircle((nx
, ny
), 8)
289 # Turn off drawing optimization
294 dc
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer)
301 self
.avail
= s
.HasPOV()
302 self
.fourDir
= s
.HasPOV4Dir()
303 self
.cts
= s
.HasPOVCTS()
306 #----------------------------------------------------------------------------
308 class POVStatus(wx
.Panel
):
310 # Displays static info about the POV control
312 def __init__(self
, parent
, stick
):
316 wx
.Panel
.__init
__(self
, parent
, -1, size
=(100, 100))
318 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
321 self
.avail
= wx
.CheckBox(self
, -1, "Available")
322 sizer
.Add(self
.avail
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2)
324 self
.fourDir
= wx
.CheckBox(self
, -1, "4-Way Only")
325 sizer
.Add(self
.fourDir
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2)
327 self
.cts
= wx
.CheckBox(self
, -1, "Continuous")
328 sizer
.Add(self
.cts
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2)
333 # Effectively makes the checkboxes read-only.
334 self
.Bind(wx
.EVT_CHECKBOX
, self
.Calibrate
)
337 def Calibrate(self
, evt
=None):
339 self
.avail
.SetValue(s
.HasPOV())
340 self
.fourDir
.SetValue(s
.HasPOV4Dir())
341 self
.cts
.SetValue(s
.HasPOVCTS())
344 #----------------------------------------------------------------------------
346 class POVPanel(wx
.Panel
):
347 def __init__(self
, parent
, stick
):
351 wx
.Panel
.__init
__(self
, parent
, -1, size
=(100, 100))
353 sizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
354 gsizer
= wx
.BoxSizer(wx
.VERTICAL
)
359 parent
.GetFont().GetPointSize() + 3,
360 parent
.GetFont().GetFamily(),
361 parent
.GetFont().GetStyle(),
364 t
= wx
.StaticText(self
, -1, "POV Control", style
= wx
.ALIGN_CENTER
)
366 gsizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND
, 1)
368 self
.display
= POVGauge(self
, stick
)
369 gsizer
.Add(self
.display
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1)
370 sizer
.Add(gsizer
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1)
372 self
.status
= POVStatus(self
, stick
)
373 sizer
.Add(self
.status
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER
, 1)
380 self
.display
.Calibrate()
381 self
.status
.Calibrate()
385 self
.display
.Update()
388 #----------------------------------------------------------------------------
391 def __init__(self
, parent
, number
):
398 parent
.GetFont().GetPointSize() - 1,
399 parent
.GetFont().GetFamily(),
400 parent
.GetFont().GetStyle(),
404 wx
.Panel
.__init
__(self
, parent
, -1, size
=self
.size
)
406 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
407 self
.Bind(wx
.EVT_SIZE
, self
.OnSize
)
408 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, lambda e
: None)
410 self
.buffer = wx
.EmptyBitmap(*self
.size
)
411 dc
= wx
.BufferedDC(None, self
.buffer)
416 def OnSize(self
, event
):
417 # calculate the size of our display.
418 w
, h
= self
.GetClientSize()
421 self
.buffer = wx
.EmptyBitmap(*self
.size
)
422 dc
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer)
427 def DrawFace(self
, dc
):
428 dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour()))
432 def OnPaint(self
, evt
):
433 # When dc is destroyed it will blit self.buffer to the window,
434 # since no other drawing is needed we'll just return and let it
436 dc
= wx
.BufferedPaintDC(self
, self
.buffer)
439 def DrawLED(self
, dc
):
446 # calc the 0, 0 origin of the bitmap
447 xorigin
= center
- (bw
/ 2)
448 yorigin
= center
- (bh
/ 2)
450 # Optimize drawing a bit (for Win)
455 dc
.SetBrush(wx
.Brush(wx
.RED
))
456 elif self
.state
== 1:
457 dc
.SetBrush(wx
.Brush(wx
.GREEN
))
459 dc
.SetBrush(wx
.Brush(wx
.BLACK
))
461 dc
.DrawCircle((center
, center
), bw
/2)
463 txt
= str(self
.number
)
465 # Set the font for the DC ...
467 # ... and calculate how much space our value
469 fw
, fh
= dc
.GetTextExtent(txt
)
471 # Calc the center of the LED, and from that
472 # derive the origin of our value.
476 # I draw the value twice so as to give it a pseudo-shadow.
477 # This is (mostly) because I'm too lazy to figure out how
478 # to blit my text onto the gauge using one of the logical
479 # functions. The pseudo-shadow gives the text contrast
480 # regardless of whether the bar is under it or not.
481 dc
.SetTextForeground(wx
.WHITE
)
482 dc
.DrawText(txt
, (tx
, ty
))
484 # Turn off drawing optimization
489 dc
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer)
494 #----------------------------------------------------------------------------
496 class JoyButtons(wx
.Panel
):
497 def __init__(self
, parent
, stick
):
502 wx
.Panel
.__init
__(self
, parent
, -1)
504 tsizer
= wx
.BoxSizer(wx
.VERTICAL
)
507 parent
.GetFont().GetPointSize() + 3,
508 parent
.GetFont().GetFamily(),
509 parent
.GetFont().GetStyle(),
513 t
= wx
.StaticText(self
, -1, "Buttons", style
= wx
.ALIGN_LEFT
)
515 tsizer
.Add(t
, 0, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 1)
517 sizer
= wx
.FlexGridSizer(4, 16, 2, 2)
519 fn
.SetPointSize(parent
.GetFont().GetPointSize() + 1)
521 for i
in range(0, MAX_BUTTONS
):
524 sizer
.Add(t
, 1, wx
.ALL|wx
.ALIGN_CENTER|wx
.ALIGN_CENTER_VERTICAL
, 1)
525 sizer
.AddGrowableCol(i
)
527 tsizer
.Add(sizer
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 1)
529 self
.SetSizer(tsizer
)
533 for i
in range(0, MAX_BUTTONS
):
534 self
.leds
[i
].state
= -1
536 t
= self
.stick
.GetNumberButtons()
538 for i
in range(0, t
):
539 self
.leds
[i
].state
= 0
542 t
= self
.stick
.GetButtonState()
544 for i
in range(0, MAX_BUTTONS
):
545 if self
.leds
[i
].state
== 1:
546 self
.leds
[i
].state
= 0
549 self
.leds
[i
].state
= 1
551 self
.leds
[i
].Update()
554 #----------------------------------------------------------------------------
556 class InfoPanel(wx
.Panel
):
557 def __init__(self
, parent
, stick
):
561 wx
.Panel
.__init
__(self
, parent
, -1)
563 sizer
= wx
.GridBagSizer(1, 1)
565 sizer
.Add(Label(self
, 'Mfr ID: '), (0, 0), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2)
566 self
.MfgID
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
)
567 sizer
.Add(self
.MfgID
, (0, 1), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2)
569 sizer
.Add(Label(self
, 'Prod Name: '), (0, 2), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2)
570 self
.ProdName
= wx
.TextCtrl(self
, -1, value
='', style
=wx
.TE_READONLY
)
571 sizer
.Add(self
.ProdName
, (0, 3), (1, 3), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2)
573 sizer
.Add(Label(self
, 'Threshold: '), (0, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2)
574 self
.Threshold
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
)
575 sizer
.Add(self
.Threshold
, (0, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2)
577 #----------------------------------------------------------------------------
578 b
= wx
.Button(self
, -1, "Calibrate")
579 sizer
.Add(b
, (1, 0), (2, 2), wx
.ALL | wx
.ALIGN_CENTER
, 2)
581 sizer
.Add(Label(self
, '# of Sticks: '), (1, 2), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2)
582 self
.NumJoysticks
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
)
583 sizer
.Add(self
.NumJoysticks
, (1, 3), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2)
585 sizer
.Add(Label(self
, '# of Axes: '), (1, 4), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2)
586 self
.NumAxis
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
)
587 sizer
.Add(self
.NumAxis
, (1, 5), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2)
589 sizer
.Add(Label(self
, 'Max # Axes: '), (1, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2)
590 self
.MaxAxis
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
)
591 sizer
.Add(self
.MaxAxis
, (1, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2)
593 #----------------------------------------------------------------------------
595 sizer
.Add(Label(self
, 'Polling -- '), (2, 3), (1, 1), wx
.ALL | wx
.GROW
, 2)
597 sizer
.Add(Label(self
, 'Min: '), (2, 4), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2)
598 self
.PollMin
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
)
599 sizer
.Add(self
.PollMin
, (2, 5), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2)
601 sizer
.Add(Label(self
, 'Max: '), (2, 6), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_RIGHT
, 2)
602 self
.PollMax
= wx
.TextCtrl(self
, -1, value
='', size
=(45, -1), style
=wx
.TE_READONLY
)
603 sizer
.Add(self
.PollMax
, (2, 7), (1, 1), wx
.ALL | wx
.GROW | wx
.ALIGN_LEFT
, 2)
605 #----------------------------------------------------------------------------
617 self
.MfgID
.SetValue(str(s
.GetManufacturerId()))
618 self
.ProdName
.SetValue(str(s
.GetProductName()))
619 self
.Threshold
.SetValue(str(s
.GetMovementThreshold()))
620 self
.NumJoysticks
.SetValue(str(s
.GetNumberJoysticks()))
621 self
.NumAxis
.SetValue(str(s
.GetNumberAxes()))
622 self
.MaxAxis
.SetValue(str(s
.GetMaxAxes()))
623 self
.PollMin
.SetValue(str(s
.GetPollingMin()))
624 self
.PollMax
.SetValue(str(s
.GetPollingMax()))
627 #----------------------------------------------------------------------------
629 class AxisBar(wx
.Gauge
):
631 # This class allows us to use a wx.Gauge to display the axis value
632 # with a fancy label overlayed onto the guage itself. Two values are
633 # used to do things: first of all, since the gauge is limited to
634 # positive numbers, the scale is fixed at 0 to 1000. We will receive
635 # an adjusted value to use to render the gauge itself. The other value
636 # is a raw value and actually reflects the value from the joystick itself,
637 # which is then drawn over the gauge.
639 def __init__(self
, parent
):
640 wx
.Gauge
.__init
__(self
, parent
, -1, 1000, size
=(-1, 20), style
= wx
.GA_HORIZONTAL | wx
.GA_SMOOTH
)
642 # This is the value we will display.
645 self
.SetBackgroundColour('light blue')
646 self
.SetForegroundColour('orange')
648 # Capture paint events for purpose of updating
649 # the displayed value.
650 self
.Bind(wx
.EVT_PAINT
, self
.onPaint
)
652 def Update(self
, value
, rawvalue
):
653 # Updates the gauge itself, sets the raw value for
656 self
.rawvalue
= rawvalue
658 def onPaint(self
, evt
):
659 # Must always create a PaintDC when capturing
661 self
.ShowValue(wx
.PaintDC(self
), evt
)
663 def ShowValue(self
, dc
, evt
):
664 # This method handles actual painting of and drawing
667 # Clear out the gauge
669 # and then carry out business as usual
670 wx
.Gauge
.OnPaint(self
, evt
)
672 # This is the size available to us.
675 # This is what we will overlay on the gauge.
676 # It reflects the actual value received from the
678 txt
= str(self
.rawvalue
)
680 # Copy the default font, make it bold.
682 self
.GetFont().GetPointSize(),
683 self
.GetFont().GetFamily(),
684 self
.GetFont().GetStyle(),
688 # Set the font for the DC ...
690 # ... and calculate how much space our value
692 fw
, fh
= dc
.GetTextExtent(txt
)
694 # Calc the center of the gauge, and from that
695 # derive the origin of our value.
702 # I draw the value twice so as to give it a pseudo-shadow.
703 # This is (mostly) because I'm too lazy to figure out how
704 # to blit my text onto the gauge using one of the logical
705 # functions. The pseudo-shadow gives the text contrast
706 # regardless of whether the bar is under it or not.
707 dc
.SetTextForeground(wx
.BLACK
)
708 dc
.DrawText(txt
, (tx
, ty
))
710 dc
.SetTextForeground('white')
711 dc
.DrawText(txt
, (tx
-1, ty
-1))
714 #----------------------------------------------------------------------------
716 class Axis(wx
.Panel
):
718 # This class is a container for the min, max, and current
719 # values of the joystick axis in question. It contains
720 # also special features to render a 'dummy' if the axis
721 # in question is not available.
723 def __init__(self
, parent
, token
, stick
):
728 # token represents the type of axis we're displaying.
733 # Create a call to the 'Has*()' method for the stick.
734 # X and Y are always there, so we tie the Has* method
735 # to a hardwired True value.
737 if token
not in ['X', 'Y']:
738 self
.HasFunc
= eval('stick.Has%s' % token
)
740 self
.HasFunc
= self
.alwaysTrue
742 # Now init the panel.
743 wx
.Panel
.__init
__(self
, parent
, -1)
745 sizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
749 # Tie our calibration functions to the appropriate
750 # stick method. If we don't have the axis in question,
751 # we won't need them.
753 self
.GetMin
= eval('stick.Get%sMin' % token
)
754 self
.GetMax
= eval('stick.Get%sMax' % token
)
756 # Create our displays and set them up.
757 self
.Min
= wx
.StaticText(self
, -1, str(self
.GetMin()),
758 size
=(40,-1), style
=wx
.ALIGN_RIGHT | wx
.ST_NO_AUTORESIZE
)
759 self
.Max
= wx
.StaticText(self
, -1, str(self
.GetMax()),
760 size
=(40,-1), style
=wx
.ALIGN_LEFT | wx
.ST_NO_AUTORESIZE
)
761 self
.bar
= AxisBar(self
)
763 sizer
.Add(self
.Min
, 0, wx
.ALL | wx
.ALIGN_RIGHT | wx
.ALIGN_CENTER_VERTICAL
, 1)
764 sizer
.Add(self
.bar
, 1, wx
.ALL | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1)
765 sizer
.Add(self
.Max
, 0, wx
.ALL | wx
.ALIGN_LEFT | wx
.ALIGN_CENTER_VERTICAL
, 1)
768 # We go here if the axis in question is not available.
769 self
.control
= wx
.StaticText(self
, -1, ' *** Not Present ***')
770 sizer
.Add(self
.control
, 1, wx
.ALL | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1)
772 #----------------------------------------------------------------------------
776 wx
.CallAfter(self
.Update
)
780 if not self
.HasFunc():
783 self
.Min
.SetLabel(str(self
.GetMin()))
784 self
.Max
.SetLabel(str(self
.GetMax()))
788 # Don't bother if the axis doesn't exist.
789 if not self
.HasFunc():
792 min = int(self
.Min
.GetLabel())
793 max = int(self
.Max
.GetLabel())
796 # Not all values are available from a wx.JoystickEvent, so I've elected
797 # to not use it at all. Therefore, we are getting our values direct from
798 # the stick. These values also seem to be more stable and reliable than
799 # those received from the event itself, so maybe it's a good idea to
800 # use the stick directly for your program.
802 # Here we either select the appropriate member of stick.GetPosition() or
803 # apply the appropriate Get*Position method call.
805 if self
.token
== 'X':
806 val
= self
.stick
.GetPosition().x
807 elif self
.token
== 'Y':
808 val
= self
.stick
.GetPosition().y
810 val
= eval('self.stick.Get%sPosition()' % self
.token
)
813 # While we might be able to rely on a range of 0-FFFFFF on Win, that might
814 # not be true of all drivers on all platforms. Thus, calc the actual full
817 range = float(max - min)
820 # The relative value is used by the derived wx.Gauge since it is a
821 # positive-only control.
825 relative
= int(val
/ range * 1000)
828 # Pass both the raw and relative values to the derived Gauge
830 self
.bar
.Update(relative
, val
)
833 def alwaysTrue(self
):
834 # a dummy method used for X and Y axis.
838 #----------------------------------------------------------------------------
840 class AxisPanel(wx
.Panel
):
842 # Contained herein is a panel that offers a graphical display
843 # of the levels for all axes supported by wx.Joystick. If
844 # your system doesn't have a particular axis, it will be
845 # 'dummied' for transparent use.
847 def __init__(self
, parent
, stick
):
851 # Defines labels and 'tokens' to identify each
854 ('X Axis ', 'X'), ('Y Axis ', 'Y'),
855 ('Z Axis ', 'Z'), ('Rudder ', 'Rudder'),
856 ('U Axis ', 'U'), ('V Axis ', 'V')
859 # Contains a list of all axis initialized.
862 wx
.Panel
.__init
__(self
, parent
, -1)
864 sizer
= wx
.FlexGridSizer(3, 4, 1, 1)
865 sizer
.AddGrowableCol(1)
866 sizer
.AddGrowableCol(3)
868 #----------------------------------------------------------------------------
870 # Go through the list of labels and tokens and add a label and
871 # axis display to the sizer for each.
872 for label
, token
in axesList
:
873 sizer
.Add(Label(self
, label
), 0, wx
.ALL | wx
.ALIGN_RIGHT
, 2)
874 t
= Axis(self
, token
, self
.stick
)
876 sizer
.Add(t
, 1, wx
.ALL | wx
.EXPAND | wx
.ALIGN_LEFT
, 2)
878 #----------------------------------------------------------------------------
882 wx
.CallAfter(self
.Update
)
893 #----------------------------------------------------------------------------
895 class JoystickDemoPanel(wx
.Panel
):
897 def __init__(self
, parent
, log
):
901 wx
.Panel
.__init
__(self
, parent
, -1)
903 # Try to grab the control. If we get it, capture the stick.
904 # Otherwise, throw up an exception message and play stupid.
906 self
.stick
= wx
.Joystick()
907 self
.stick
.SetCapture(self
)
908 # Calibrate our controls
909 wx
.CallAfter(self
.Calibrate
)
910 wx
.CallAfter(self
.OnJoystick
)
911 except NotImplementedError, v
:
912 wx
.MessageBox(str(v
), "Exception Message")
915 # One Sizer to Rule Them All...
916 sizer
= wx
.GridBagSizer(2,2)
918 self
.info
= InfoPanel(self
, self
.stick
)
919 sizer
.Add(self
.info
, (0, 0), (1, 3), wx
.ALL | wx
.GROW
, 2)
921 self
.info
.Bind(wx
.EVT_BUTTON
, self
.Calibrate
)
923 self
.joy
= JoyPanel(self
, self
.stick
)
924 sizer
.Add(self
.joy
, (1, 0), (1, 1), wx
.ALL | wx
.GROW
, 2)
926 self
.pov
= POVPanel(self
, self
.stick
)
927 sizer
.Add(self
.pov
, (1, 1), (1, 2), wx
.ALL | wx
.GROW
, 2)
929 self
.axes
= AxisPanel(self
, self
.stick
)
930 sizer
.Add(self
.axes
, (2, 0), (1, 3), wx
.ALL | wx
.GROW
, 2)
932 self
.buttons
= JoyButtons(self
, self
.stick
)
933 sizer
.Add(self
.buttons
, (3, 0), (1, 3), wx
.ALL | wx
.EXPAND | wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
, 1)
938 # Capture Joystick events (if they happen)
939 self
.Bind(wx
.EVT_JOYSTICK_EVENTS
, self
.OnJoystick
)
941 self
.stick
.SetMovementThreshold(10)
943 def Calibrate(self
, evt
=None):
944 # Do not try this without a stick
948 self
.info
.Calibrate()
949 self
.axes
.Calibrate()
951 self
.buttons
.Calibrate()
953 def OnJoystick(self
, evt
=None):
960 self
.buttons
.Update()
963 #----------------------------------------------------------------------------
965 def runTest(frame
, nb
, log
):
966 win
= JoystickDemoPanel(nb
, log
)
969 #----------------------------------------------------------------------------
975 This demo illustrates the use of the wx.Joystick class, which is an interface to
976 one or more joysticks attached to your system.
978 <p>The data that can be retrieved from the joystick comes in four basic flavors.
979 All of these are illustrated in the demo. In fact, this demo illustrates everything
980 you <b>can</b> get from the wx.Joystick control.
983 <li>Static information such as Manufacturer ID and model name,
984 <li>Analog input from up to six axes, including X and Y for the actual stick,
985 <li>Button input from the fire button and any other buttons that the stick has,
986 <li>and the POV control (a kind of mini-joystick on top of the joystick) that many sticks come with.
989 <p>Getting data from the joystick can be event-driven thanks to four event types associated
990 with wx.JoystickEvent, or the joystick can be polled programatically to get data on
995 Data from the joystick comes in two flavors: that which defines the boundaries, and that
996 which defines the current state of the stick. Thus, we have Get*Max() and Get*Min()
997 methods for all axes, the max number of axes, the max number of buttons, and so on. In
998 general, this data can be read once and stored to speed computation up.
1000 <h3>Analog Input</h3>
1002 Analog input (the axes) is delivered as a whole, positive number. If you need to know
1003 if the axis is at zero (centered) or not, you will first have to calculate that center
1004 based on the max and min values. The demo shows a bar graph for each axis expressed
1005 in native numerical format, plus a 'centered' X-Y axis compass showing the relationship
1006 of that input to the calculated stick position.
1008 Analog input may be jumpy and spurious, so the control has a means of 'smoothing' the
1009 analog data by setting a movement threshold. This demo sets the threshold to 10, but
1010 you can set it at any valid value between the min and max.
1012 <h3>Button Input</h3>
1014 Button state is retrieved as one int that contains each button state mapped to a bit.
1015 You get the state of a button by AND-ing its bit against the returned value, in the form
1018 # assume buttonState is what the stick returned, and buttonBit
1019 # is the bit you want to examine
1021 if (buttonState & ( 1 << buttonBit )) :
1022 # button pressed, do something with it
1025 <p>The problem here is that some OSs return a 32-bit value for up to 32 buttons
1026 (imagine <i>that</i> stick!). Python V2.3 will generate an exception for bit
1027 values over 30. For that reason, this demo is limited to 16 buttons.
1029 <p>Note that more than one button can be pressed at a time, so be sure to check all of them!
1034 POV hats come in two flavors: four-way, and continuous. four-way POVs are restricted to
1035 the cardinal points of the compass; continuous, or CTS POV hats can deliver input in
1036 .01 degree increments, theoreticaly. The data is returned as a whole number; the last
1037 two digits are considered to be to the right of the decimal point, so in order to
1038 use this information, you need to divide by 100 right off the bat.
1040 <p>Different methods are provided to retrieve the POV data for a CTS hat
1041 versus a four-way hat.
1045 The wx.Joystick control is in many ways incomplete at the C++ library level, but it is
1046 not insurmountable. In short, while the joystick interface <i>can</i> be event-driven,
1047 the wx.JoystickEvent class lacks event binders for all event types. Thus, you cannot
1048 rely on wx.JoystickEvents to tell you when something has changed, necessarilly.
1051 <li>There are no events associated with the POV control.
1052 <li>There are no events associated with the Rudder
1053 <li>There are no events associated with the U and V axes.
1056 <p>Fortunately, there is an easy workaround. In the top level frame, create a wx.Timer
1057 that will poll the stick at a set interval. Of course, if you do this, you might as
1058 well forgo catching wxEVT_JOYSTICK_* events at all and rely on the timer to do the
1061 <p>Ideally, the timer should be a one-shot; after it fires, collect and process data as
1062 needed, then re-start the timer, possibly using wx.CallAfter().
1068 #----------------------------------------------------------------------------
1070 if __name__
== '__main__':
1073 run
.main(['', os
.path
.basename(sys
.argv
[0])])