+#----------------------------------------------------------------------------
+
+
+class JoyGauge(wx.Panel):
+ def __init__(self, parent, stick):
+
+ self.stick = stick
+ size = (100,100)
+
+ wx.Panel.__init__(self, parent, -1, size=size)
+
+ self.Bind(wx.EVT_PAINT, self.OnPaint)
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+ self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
+
+ self.buffer = wx.EmptyBitmap(*size)
+ dc = wx.BufferedDC(None, self.buffer)
+ self.DrawFace(dc)
+ self.DrawJoystick(dc)
+
+
+ def OnSize(self, event):
+ # The face Bitmap init is done here, to make sure the buffer is always
+ # the same size as the Window
+ w, h = self.GetClientSize()
+ self.buffer = wx.EmptyBitmap(w,h)
+ dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
+ self.DrawFace(dc)
+ self.DrawJoystick(dc)
+
+
+ def DrawFace(self, dc):
+ dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+ dc.Clear()
+
+
+ def OnPaint(self, evt):
+ # When dc is destroyed it will blit self.buffer to the window,
+ # since no other drawing is needed we'll just return and let it
+ # do it's thing
+ dc = wx.BufferedPaintDC(self, self.buffer)
+
+
+ def DrawJoystick(self, dc):
+ # draw the guage as a maxed square in the center of this window.
+ w, h = self.GetClientSize()
+ edgeSize = min(w, h)
+
+ xorigin = (w - edgeSize) / 2
+ yorigin = (h - edgeSize) / 2
+ center = edgeSize / 2
+
+ # Restrict our drawing activities to the square defined
+ # above.
+ dc.SetClippingRegion((xorigin, yorigin), (edgeSize, edgeSize))
+
+ # Optimize drawing a bit (for Win)
+ dc.BeginDrawing()
+
+ dc.SetBrush(wx.Brush(wx.Colour(251, 252, 237)))
+ dc.DrawRectangle((xorigin, yorigin), (edgeSize, edgeSize))
+
+ dc.SetPen(wx.Pen(wx.BLACK, 1, wx.DOT_DASH))
+
+ dc.DrawLine((xorigin, yorigin + center), (xorigin + edgeSize, yorigin + center))
+ dc.DrawLine((xorigin + center, yorigin), (xorigin + center, yorigin + edgeSize))
+
+ if self.stick:
+ # Get the joystick position as a float
+ joyx = float(self.stick.GetPosition().x)
+ joyy = float(self.stick.GetPosition().y)
+
+ # Get the joystick range of motion
+ xrange = self.stick.GetXMax() - self.stick.GetXMin()
+ yrange = self.stick.GetYMax() - self.stick.GetYMin()
+
+ # calc a ratio of our range versus the joystick range
+ xratio = float(edgeSize) / xrange
+ yratio = float(edgeSize) / yrange
+
+ # calc the displayable value based on position times ratio
+ xval = int(joyx * xratio)
+ yval = int(joyy * xratio)
+
+ # and normalize the value from our brush's origin
+ x = xval + xorigin
+ y = yval + yorigin
+
+ # Now to draw it.
+ dc.SetPen(wx.Pen(wx.RED, 2))
+ dc.CrossHair((x, y))
+
+ # Turn off drawing optimization
+ dc.EndDrawing()
+
+
+ def Update(self):
+ dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
+ self.DrawFace(dc)
+ self.DrawJoystick(dc)
+
+
+#----------------------------------------------------------------------------
+
+class JoyPanel(wx.Panel):
+ def __init__(self, parent, stick):
+
+ self.stick = stick
+
+ wx.Panel.__init__(self, parent, -1)
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ fn = wx.Font(
+ parent.GetFont().GetPointSize() + 3,
+ parent.GetFont().GetFamily(),
+ parent.GetFont().GetStyle(),
+ wx.BOLD
+ )
+
+ t = wx.StaticText(self, -1, "X - Y Axes", style = wx.ALIGN_CENTRE)
+ t.SetFont(fn)
+ sizer.Add(t, 0, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_HORIZONTAL, 1)
+
+ self.control = JoyGauge(self, self.stick)
+ sizer.Add(self.control, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_HORIZONTAL, 1)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ def Update(self):
+ self.control.Update()
+
+
+#----------------------------------------------------------------------------
+
+class POVGauge(wx.Panel):
+ #
+ # Display the current postion of the POV control
+ #
+ def __init__(self, parent, stick):
+
+ self.stick = stick
+ self.size = (100, 100)
+ self.avail = False
+ self.fourDir = False
+ self.cts = False
+
+ wx.Panel.__init__(self, parent, -1, size=self.size)
+
+ self.Bind(wx.EVT_PAINT, self.OnPaint)
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+ self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
+
+ self.buffer = wx.EmptyBitmap(*self.size)
+ dc = wx.BufferedDC(None, self.buffer)
+ self.DrawFace(dc)
+ self.DrawPOV(dc)
+
+
+ def OnSize(self, event):
+ # calculate the size of our display and make a buffer for it.
+ w, h = self.GetClientSize()
+ s = min(w, h)
+ self.size = (s, s)
+ self.buffer = wx.EmptyBitmap(w,h)
+ dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
+ self.DrawFace(dc)
+ self.DrawPOV(dc)
+
+
+ def DrawFace(self, dc):
+ dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+ dc.Clear()
+
+
+ def OnPaint(self, evt):
+ # When dc is destroyed it will blit self.buffer to the window,
+ # since no other drawing is needed we'll just return and let it
+ # do it's thing
+ dc = wx.BufferedPaintDC(self, self.buffer)
+
+
+ def DrawPOV(self, dc):
+ # draw the guage as a maxed circle in the center of this window.
+ w, h = self.GetClientSize()
+ diameter = min(w, h)
+
+ xorigin = (w - diameter) / 2
+ yorigin = (h - diameter) / 2
+ xcenter = xorigin + diameter / 2
+ ycenter = yorigin + diameter / 2
+
+ # Optimize drawing a bit (for Win)
+ dc.BeginDrawing()
+
+ # our 'raster'.
+ dc.SetBrush(wx.Brush(wx.WHITE))
+ dc.DrawCircle((xcenter, ycenter), diameter/2)
+ dc.SetBrush(wx.Brush(wx.BLACK))
+ dc.DrawCircle((xcenter, ycenter), 10)
+
+ # fancy decorations
+ dc.SetPen(wx.Pen(wx.BLACK, 1, wx.DOT_DASH))
+ dc.DrawLine((xorigin, ycenter), (xorigin + diameter, ycenter))
+ dc.DrawLine((xcenter, yorigin), (xcenter, yorigin + diameter))
+
+ if self.stick:
+ if self.avail:
+
+ pos = -1
+
+ # use the appropriate function to get the POV position
+ if self.fourDir:
+ pos = self.stick.GetPOVPosition()
+
+ if self.cts:
+ pos = self.stick.GetPOVCTSPosition()
+
+ # trap invalid values
+ if 0 <= pos <= 36000:
+ vector = 30
+ else:
+ vector = 0
+
+ # rotate CCW by 90 so that 0 is up.
+ pos = (pos / 100) - 90
+
+ # Normalize
+ if pos < 0:
+ pos = pos + 360
+
+ # Stolen from wx.lib.analogclock :-)
+ radiansPerDegree = math.pi / 180
+ pointX = int(round(vector * math.cos(pos * radiansPerDegree)))
+ pointY = int(round(vector * math.sin(pos * radiansPerDegree)))
+
+ # normalise value to match our actual center.
+ nx = pointX + xcenter
+ ny = pointY + ycenter
+
+ # Draw the line
+ dc.SetPen(wx.Pen(wx.BLUE, 2))
+ dc.DrawLine((xcenter, ycenter), (nx, ny))
+
+ # And a little thing to show the endpoint
+ dc.SetBrush(wx.Brush(wx.BLUE))
+ dc.DrawCircle((nx, ny), 8)
+
+ # Turn off drawing optimization
+ dc.EndDrawing()
+
+
+ def Update(self):
+ dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
+ self.DrawFace(dc)
+ self.DrawPOV(dc)
+
+
+ def Calibrate(self):
+ s = self.stick
+ self.avail = s.HasPOV()
+ self.fourDir = s.HasPOV4Dir()
+ self.cts = s.HasPOVCTS()
+
+
+#----------------------------------------------------------------------------
+
+class POVStatus(wx.Panel):
+ #
+ # Displays static info about the POV control
+ #
+ def __init__(self, parent, stick):
+
+ self.stick = stick
+
+ wx.Panel.__init__(self, parent, -1, size=(100, 100))
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+ sizer.Add((20,20))
+
+ self.avail = wx.CheckBox(self, -1, "Available")
+ sizer.Add(self.avail, 0, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 2)
+
+ self.fourDir = wx.CheckBox(self, -1, "4-Way Only")
+ sizer.Add(self.fourDir, 0, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 2)
+
+ self.cts = wx.CheckBox(self, -1, "Continuous")
+ sizer.Add(self.cts, 0, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 2)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+ # Effectively makes the checkboxes read-only.
+ self.Bind(wx.EVT_CHECKBOX, self.Calibrate)
+
+
+ def Calibrate(self, evt=None):
+ s = self.stick
+ self.avail.SetValue(s.HasPOV())
+ self.fourDir.SetValue(s.HasPOV4Dir())
+ self.cts.SetValue(s.HasPOVCTS())
+
+
+#----------------------------------------------------------------------------
+
+class POVPanel(wx.Panel):
+ def __init__(self, parent, stick):
+
+ self.stick = stick
+
+ wx.Panel.__init__(self, parent, -1, size=(100, 100))
+
+ sizer = wx.BoxSizer(wx.HORIZONTAL)
+ gsizer = wx.BoxSizer(wx.VERTICAL)
+
+ sizer.Add((25,25))
+
+ fn = wx.Font(
+ parent.GetFont().GetPointSize() + 3,
+ parent.GetFont().GetFamily(),
+ parent.GetFont().GetStyle(),
+ wx.BOLD
+ )
+ t = wx.StaticText(self, -1, "POV Control", style = wx.ALIGN_CENTER)
+ t.SetFont(fn)
+ gsizer.Add(t, 0, wx.ALL | wx.EXPAND, 1)
+
+ self.display = POVGauge(self, stick)
+ gsizer.Add(self.display, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER, 1)
+ sizer.Add(gsizer, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER, 1)
+
+ self.status = POVStatus(self, stick)
+ sizer.Add(self.status, 1, wx.ALL | wx.EXPAND | wx.ALIGN_CENTER, 1)
+
+ self.SetSizer(sizer)
+ sizer.Fit(self)
+
+
+ def Calibrate(self):
+ self.display.Calibrate()
+ self.status.Calibrate()
+
+
+ def Update(self):
+ self.display.Update()
+
+
+#----------------------------------------------------------------------------
+
+class LED(wx.Panel):
+ def __init__(self, parent, number):
+
+ self.state = -1
+ self.size = (20, 20)
+ self.number = number
+
+ self.fn = wx.Font(
+ parent.GetFont().GetPointSize() - 1,
+ parent.GetFont().GetFamily(),
+ parent.GetFont().GetStyle(),
+ wx.BOLD
+ )
+
+ wx.Panel.__init__(self, parent, -1, size=self.size)
+
+ self.Bind(wx.EVT_PAINT, self.OnPaint)
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+ self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
+
+ self.buffer = wx.EmptyBitmap(*self.size)
+ dc = wx.BufferedDC(None, self.buffer)
+ self.DrawFace(dc)
+ self.DrawLED(dc)
+
+
+ def OnSize(self, event):
+ # calculate the size of our display.
+ w, h = self.GetClientSize()
+ s = min(w, h)
+ self.size = (s, s)
+ self.buffer = wx.EmptyBitmap(*self.size)
+ dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
+ self.DrawFace(dc)
+ self.DrawLED(dc)
+
+
+ def DrawFace(self, dc):
+ dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+ dc.Clear()
+
+
+ def OnPaint(self, evt):
+ # When dc is destroyed it will blit self.buffer to the window,
+ # since no other drawing is needed we'll just return and let it
+ # do it's thing
+ dc = wx.BufferedPaintDC(self, self.buffer)