From: Robin Dunn Date: Wed, 7 Jan 2004 02:57:40 +0000 (+0000) Subject: updated Joystick demo, is now wxDesigner-less X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/2421eb827857325ff4886131c337391365986d6b updated Joystick demo, is now wxDesigner-less git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@25078 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/wxPython/demo/Joystick.py b/wxPython/demo/Joystick.py index 2c4113adf0..03ba8e98c7 100644 --- a/wxPython/demo/Joystick.py +++ b/wxPython/demo/Joystick.py @@ -1,206 +1,981 @@ +#---------------------------------------------------------------------------- +# Name: Joystick.py +# Purpose: Demonstrate use of wx.Joystick +# +# Author: Jeff Grimmett (grimmtoo@softhome.net), adapted from original +# .wdr-derived demo +# +# Created: 01/02/04 +# RCS-ID: $Id$ +# Copyright: +# Licence: wxWindows license +#---------------------------------------------------------------------------- +# + +import math +import wx #---------------------------------------------------------------------------- -from wxPython.wx import * -from joystick_wdr import * +# For convenience +spacer = (10, 10) +MAX_BUTTONS = 16 +#---------------------------------------------------------------------------- -class JoystickTestPanel(wxPanel): - def __init__(self, parent, id, - pos = wxDefaultPosition, size = wxDefaultSize, - style = wxTAB_TRAVERSAL ): - wxPanel.__init__(self, parent, id, pos, size, style) +class Label(wx.StaticText): + # A derived StaticText that always aligns right and renders + # in a bold font. + def __init__(self, parent, label): + wx.StaticText.__init__(self, parent, -1, label, style=wx.ALIGN_RIGHT) - MakeJoystickTestPanel( self, True ) + self.SetFont( + wx.Font( + parent.GetFont().GetPointSize(), + parent.GetFont().GetFamily(), + parent.GetFont().GetStyle(), + wx.BOLD + )) + +#---------------------------------------------------------------------------- + + +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() - try: - self.stick = wxJoystick() - self.stick.SetCapture(self) - EVT_JOYSTICK_EVENTS(self, self.OnJoystick) - self.UpdateFields() - except NotImplementedError, v: - wxMessageBox(str(v), "Exception Message") +#---------------------------------------------------------------------------- + +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))) - def UpdateFields(self): + # 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.GetXPositionCtrl().SetValue(str(s.GetPosition().x)) - self.GetYPositionCtrl().SetValue(str(s.GetPosition().y)) - self.GetZPositionCtrl().SetValue(str(s.GetZPosition())) - self.GetPovCtsPosCtrl().SetValue(str(s.GetPOVPosition())) - self.GetRudderPosCtrl().SetValue(str(s.GetRudderPosition())) - self.GetHasRudderCtrl().SetValue(str(s.HasRudder())) - self.GetHasZCtrl().SetValue(str(s.HasZ())) - self.GetHasPovCtrl().SetValue(str(s.HasPOV())) - self.GetHasPov4dirCtrl().SetValue(str(s.HasPOV4Dir())) - self.GetMfgIdCtrl().SetValue(str(s.GetManufacturerId())) - self.GetProdNameCtrl().SetValue(str(s.GetProductName())) - self.GetZMinCtrl().SetValue(str(s.GetZMin())) - self.GetXMaxCtrl().SetValue(str(s.GetXMax())) - self.GetNumButtonsCtrl().SetValue(str(s.GetNumberButtons())) - self.GetNumAxesCtrl().SetValue(str(s.GetNumberAxes())) - self.GetPollingMinCtrl().SetValue(str(s.GetPollingMin())) - self.GetPollingMaxCtrl().SetValue(str(s.GetPollingMax())) - self.GetUMinCtrl().SetValue(str(s.GetUMin())) - self.GetUMaxCtrl().SetValue(str(s.GetUMax())) - self.GetButtonStateCtrl().SetValue(str(s.GetButtonState())) - self.GetPovPositionCtrl().SetValue(str(s.GetPOVPosition())) - self.GetUPositionCtrl().SetValue(str(s.GetUPosition())) - self.GetVPositionCtrl().SetValue(str(s.GetVPosition())) - self.GetHasUCtrl().SetValue(str(s.HasU())) - self.GetHasVCtrl().SetValue(str(s.HasV())) - self.GetHasPovCtsCtrl().SetValue(str(s.HasPOVCTS())) - self.GetNumSticksCtrl().SetValue(str(s.GetNumberJoysticks())) - self.GetXMinCtrl().SetValue(str(s.GetXMin())) - self.GetYMinCtrl().SetValue(str(s.GetYMin())) - self.GetYMaxCtrl().SetValue(str(s.GetYMax())) - self.GetZMaxCtrl().SetValue(str(s.GetZMax())) - self.GetMaxButtonsCtrl().SetValue(str(s.GetMaxButtons())) - self.GetMaxAxesCtrl().SetValue(str(s.GetMaxAxes())) - self.GetRudderMinCtrl().SetValue(str(s.GetRudderMin())) - self.GetRudderMaxCtrl().SetValue(str(s.GetRudderMax())) - self.GetVMinCtrl().SetValue(str(s.GetVMin())) - self.GetVMaxCtrl().SetValue(str(s.GetVMax())) + self.avail.SetValue(s.HasPOV()) + self.fourDir.SetValue(s.HasPOV4Dir()) + self.cts.SetValue(s.HasPOVCTS()) - def OnJoystick(self, evt): - self.UpdateFields() +#---------------------------------------------------------------------------- +class POVPanel(wx.Panel): + def __init__(self, parent, stick): - # WDR: methods for JoystickTestPanel + self.stick = stick - def GetYPositionCtrl(self): - return self.FindWindowById(ID_Y_Position_Ctrl) + wx.Panel.__init__(self, parent, -1, size=(100, 100)) - def GetXPositionCtrl(self): - return self.FindWindowById(ID_X_Position_Ctrl) + 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) - def GetVMaxCtrl(self): - return self.FindWindowById(ID_V_Max_Ctrl) + self.SetSizer(sizer) + sizer.Fit(self) - def GetVMinCtrl(self): - return self.FindWindowById(ID_V_Min_Ctrl) - def GetRudderMaxCtrl(self): - return self.FindWindowById(ID_Rudder_Max_Ctrl) + def Calibrate(self): + self.display.Calibrate() + self.status.Calibrate() - def GetRudderMinCtrl(self): - return self.FindWindowById(ID_Rudder_Min_Ctrl) - def GetMaxAxesCtrl(self): - return self.FindWindowById(ID_Max_Axes_Ctrl) + def Update(self): + self.display.Update() - def GetMaxButtonsCtrl(self): - return self.FindWindowById(ID_Max_Buttons_Ctrl) - def GetZMaxCtrl(self): - return self.FindWindowById(ID_Z_Max_Ctrl) +#---------------------------------------------------------------------------- - def GetYMaxCtrl(self): - return self.FindWindowById(ID_Y_Max_Ctrl) +class LED(wx.Panel): + def __init__(self, parent, number): - def GetYMinCtrl(self): - return self.FindWindowById(ID_Y_Min_Ctrl) + self.state = -1 + self.size = (20, 20) + self.number = number - def GetXMinCtrl(self): - return self.FindWindowById(ID_X_Min_Ctrl) + self.fn = wx.Font( + parent.GetFont().GetPointSize() - 1, + parent.GetFont().GetFamily(), + parent.GetFont().GetStyle(), + wx.BOLD + ) - def GetNumSticksCtrl(self): - return self.FindWindowById(ID_Num_Sticks_Ctrl) + wx.Panel.__init__(self, parent, -1, size=self.size) - def GetHasPovCtsCtrl(self): - return self.FindWindowById(ID_Has_POV_CTS_Ctrl) + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None) - def GetHasVCtrl(self): - return self.FindWindowById(ID_Has_V_Ctrl) + self.buffer = wx.EmptyBitmap(*self.size) + dc = wx.BufferedDC(None, self.buffer) + self.DrawFace(dc) + self.DrawLED(dc) - def GetHasUCtrl(self): - return self.FindWindowById(ID_Has_U_Ctrl) - def GetVPositionCtrl(self): - return self.FindWindowById(ID_V_Position_Ctrl) + 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 GetUPositionCtrl(self): - return self.FindWindowById(ID_U_Position_Ctrl) - def GetPovPositionCtrl(self): - return self.FindWindowById(ID_POV_Position_Ctrl) + def DrawFace(self, dc): + dc.SetBackground(wx.Brush(self.GetBackgroundColour())) + dc.Clear() - def GetButtonStateCtrl(self): - return self.FindWindowById(ID_Button_State_Ctrl) - def GetUMaxCtrl(self): - return self.FindWindowById(ID_U_Max_Ctrl) + 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 GetUMinCtrl(self): - return self.FindWindowById(ID_U_Min_Ctrl) - def GetPollingMaxCtrl(self): - return self.FindWindowById(ID_Polling_Max_Ctrl) + def DrawLED(self, dc): + # bitmap size + bw, bh = self.size - def GetPollingMinCtrl(self): - return self.FindWindowById(ID_Polling_Min_Ctrl) + # center of bitmap + center = bw / 2 - def GetNumAxesCtrl(self): - return self.FindWindowById(ID_Num_Axes_Ctrl) + # calc the 0, 0 origin of the bitmap + xorigin = center - (bw / 2) + yorigin = center - (bh / 2) - def GetNumButtonsCtrl(self): - return self.FindWindowById(ID_Num_Buttons_Ctrl) + # Optimize drawing a bit (for Win) + dc.BeginDrawing() - def GetXMaxCtrl(self): - return self.FindWindowById(ID_X_Max_Ctrl) + # our 'raster'. + if self.state == 0: + dc.SetBrush(wx.Brush(wx.RED)) + elif self.state == 1: + dc.SetBrush(wx.Brush(wx.GREEN)) + else: + dc.SetBrush(wx.Brush(wx.BLACK)) - def GetZMinCtrl(self): - return self.FindWindowById(ID_Z_Min_Ctrl) + dc.DrawCircle((center, center), bw/2) - def GetProdNameCtrl(self): - return self.FindWindowById(ID_Prod_Name_Ctrl) + txt = str(self.number) - def GetMfgIdCtrl(self): - return self.FindWindowById(ID_Mfg_ID_Ctrl) + # Set the font for the DC ... + dc.SetFont(self.fn) + # ... and calculate how much space our value + # will take up. + fw, fh = dc.GetTextExtent(txt) - def GetHasPov4dirCtrl(self): - return self.FindWindowById(ID_Has_POV_4DIR_Ctrl) + # Calc the center of the LED, and from that + # derive the origin of our value. + tx = center - (fw/2) + ty = center - (fh/2) - def GetHasPovCtrl(self): - return self.FindWindowById(ID_Has_POV_Ctrl) + # I draw the value twice so as to give it a pseudo-shadow. + # This is (mostly) because I'm too lazy to figure out how + # to blit my text onto the gauge using one of the logical + # functions. The pseudo-shadow gives the text contrast + # regardless of whether the bar is under it or not. + dc.SetTextForeground(wx.WHITE) + dc.DrawText(txt, (tx, ty)) - def GetHasZCtrl(self): - return self.FindWindowById(ID_Has_Z_Ctrl) + # Turn off drawing optimization + dc.EndDrawing() - def GetHasRudderCtrl(self): - return self.FindWindowById(ID_Has_Rudder_Ctrl) - def GetRudderPosCtrl(self): - return self.FindWindowById(ID_Rudder_Pos_Ctrl) + def Update(self): + dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) + self.DrawFace(dc) + self.DrawLED(dc) - def GetPovCtsPosCtrl(self): - return self.FindWindowById(ID_POV_CTS_Pos_Ctrl) - def GetZPositionCtrl(self): - return self.FindWindowById(ID_Z_Position_Ctrl) +#---------------------------------------------------------------------------- - # WDR: handler implementations for JoysticktestPanel +class JoyButtons(wx.Panel): + def __init__(self, parent, stick): + self.stick = stick + self.leds = {} -#---------------------------------------------------------------------- + wx.Panel.__init__(self, parent, -1) + + tsizer = wx.BoxSizer(wx.VERTICAL) + + fn = wx.Font( + parent.GetFont().GetPointSize() + 3, + parent.GetFont().GetFamily(), + parent.GetFont().GetStyle(), + wx.BOLD + ) + + t = wx.StaticText(self, -1, "Buttons", style = wx.ALIGN_LEFT) + t.SetFont(fn) + tsizer.Add(t, 0, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 1) + + sizer = wx.FlexGridSizer(4, 16, 2, 2) + + fn.SetPointSize(parent.GetFont().GetPointSize() + 1) + + for i in range(0, MAX_BUTTONS): + t = LED(self, i) + self.leds[i] = t + sizer.Add(t, 1, wx.ALL|wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL, 1) + sizer.AddGrowableCol(i) + + tsizer.Add(sizer, 1, wx.ALL | wx.EXPAND | wx.ALIGN_LEFT, 1) + + self.SetSizer(tsizer) + tsizer.Fit(self) + + def Calibrate(self): + for i in range(0, MAX_BUTTONS): + self.leds[i].state = -1 + + t = self.stick.GetNumberButtons() + + for i in range(0, t): + self.leds[i].state = 0 + + def Update(self): + t = self.stick.GetButtonState() + + for i in range(0, MAX_BUTTONS): + if self.leds[i].state == 1: + self.leds[i].state = 0 + + if (t & (1<