Pygame modules for interactive programs
Mouse Movement and Buttons
Like the keyboard, the mouse does not require any special setup for events to appear on the queue. A companion function, pygame.mouse.get_focused()
, works the same as its keyboard counterpart. If the Pygame window is receiving mouse events, this function will return True
; otherwise, it will return False
.
The pygame.mouse
module also allows you to set the position of the mouse pointer, make the pointer visible or invisible, and set the icon displayed as the mouse pointer. You can also use pygame.mouse.get_pos()
to get the current position of the mouse pointer or pygame.mouse.get_rel()
to get the amount of relative movement since the last call to pygame.mouse.get_rel()
. When a mouse event appears on the event queue, both absolute and relative position are provided as additional parameters, so it's often not necessary to call these functions directly.
The underlying OS must support the mouse functions (e.g., hiding the cursor) for the Pygame functions to have any effect.
Joysticks
Pygame supports a wide range of joysticks and their assorted input styles. Gamepads, "classic" controllers, and traditional joysticks all fall into the joystick category in Pygame. A joystick will usually have at least two axes, returning analog values ranging from -1 to 1, and at least one button that provides a True
or False
(pressed or not pressed). The device may also contain trackballs that provide relative movement in x and y directions, as well as hats, which are four-way switches providing digital up/down/left/right input.
Because joysticks can contain any number of these axes, buttons, balls, and hats, the pygame.joystick
module provides a method to query them for their capabilities.
The JoystickTest.py
tool (Listing 6, at the end of the article; Figure 1) examines joysticks and their inputs. When run, it presents a list of joysticks currently connected to the system and allows the user to select the joystick to test. This choice is made in the console window rather than inside Pygame. Once a joystick is selected, the Pygame window is launched, and the test begins.
Listing 6
Joystick Test Utility
001 import pygame 002 import sys 003 004 class mainDisplay: 005 def __init__ ( self , screen , joystick ): 006 # Setup class variables 007 self.screen = screen 008 self.width = screen.get_width() 009 self.height = screen.get_height() 010 self.joystick = joystick 011 012 # Draw dividing lines onto the window 013 pygame.draw.line ( self.screen , ( 0 , 255 , 0 ) , ( self.width / 2 , 0 ) , ( self.width / 2 , self.height ) , 1 ) 014 pygame.draw.line ( self.screen , ( 0 , 255 , 0 ) , ( 0 , self.height / 2 ) , ( self.width , self.height / 2 ) , 1 ) 015 016 # Initialize the font module to label things 017 pygame.font.init() 018 self.labelFont = pygame.font.SysFont ( "" , 15 , False , False ) 019 020 # Get the number of each type of input from the joystick 021 self.axisCount = joystick.get_numaxes() 022 self.buttonCount = joystick.get_numbuttons() 023 self.ballCount = joystick.get_numballs() 024 self.hatCount = joystick.get_numhats() 025 026 # Initialize the plotting functions for each type of input 027 self.joyAxes = dict() 028 for i in range ( self.axisCount ): 029 self.joyAxes [ i ] = 0 030 self.plotAxes() 031 032 self.joyButtons = dict() 033 for i in range ( self.buttonCount ): 034 self.joyButtons [ i ] = False 035 self.plotButtons() 036 037 for i in range ( self.hatCount ): 038 self.plotHat ( i , ( 0 , 0 ) ) 039 040 self.ballLocations = dict() 041 for i in range ( self.ballCount ): 042 self.ballLocations [ i ] = list() 043 self.ballLocations [ i ].append ( 0 ) 044 self.ballLocations [ i ].append ( 0 ) 045 self.plotBalls ( i , ( 0 , 0 ) ) 046 047 def meter ( self , position , size , label , value ): 048 # The meter function plots a value on a horizontal scale 049 # Variables: 050 # position -- a tuple where the meter should be plotted on screen 051 # size -- the width of the meter (height is always 30 pixels) 052 # label -- the text to label this meter 053 # value -- a float ranging from -1 to 1 to show on the meter (0 is center) 054 055 pygame.draw.rect ( self.screen , ( 0 , 0 , 0 ) , ( position [ 0 ] - 2 , position [ 1 ] , size + 4 , 30 ) , 0 ) 056 caption = self.labelFont.render ( label , True , ( 0 , 255 , 0 ) ) 057 058 self.screen.blit ( caption , position ) 059 pygame.draw.rect ( self.screen , ( 0 , 255 , 0 ) , ( position [ 0 ] , position [ 1 ] + 14 , size , 2 ) , 0 ) 060 pointerX = ( size / 2 ) + ( size * ( value / 2 ) ) - 1 + position [ 0 ] 061 pygame.draw.rect ( self.screen , ( 0 , 255 , 0 ) , ( pointerX , position [ 1 ] , 2 , 30 ) , 0 ) 062 063 def plotAxes ( self ): 064 plotLocation = 30 # Initialize the location of the axes plot 065 for axis , value in self.joyAxes.items(): # Loop over all of the joystick axes 066 axisValue = self.joystick.get_axis ( axis ) # Get the value of the axis in question 067 self.meter ( ( 25 , plotLocation ) , 350 , "Axis " + str ( axis ) , axisValue ) # Call the meter function to plot the value 068 plotLocation += 35 # Update the plot location for the next axis 069 pygame.display.flip() # Flip the display 070 071 def plotButtons ( self ): 072 # Initialize the button plots 073 buttonX = 425 074 buttonY = 25 075 076 # Clear the quadrant 077 pygame.draw.rect ( self.screen , ( 0 , 0 , 0 ) , ( buttonX , buttonY , 400 , 274 ) , 0 ) 078 079 # Loop over all of the buttons and plot their current status 080 for i in range ( self.buttonCount ): 081 if self.joyButtons [ i ] == True: 082 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( buttonX + 25 , buttonY + 25 ) , 25 , 0 ) # Button is pressed -- fill the circle 083 else: 084 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( buttonX + 25 , buttonY + 25 ) , 25 , 1 ) # Button is not pressed -- empty circle 085 086 # Label the button 087 label = self.labelFont.render ( "Button " + str ( i ) , True , ( 0 , 255 , 0 ) ) 088 self.screen.blit ( label , ( buttonX , buttonY + 50 ) ) 089 090 # Update the button locations 091 buttonX += 55 092 if buttonX >= 750: 093 buttonX = 425 094 buttonY += 75 095 096 pygame.display.flip() # Flip the display 097 098 def plotHat ( self , hat , value ): 099 # Initialize the hat display 100 hatX = int ( hat / 4 ) * 100 + 405 101 hatY = int ( hat % 4 ) * 125 + 305 102 103 # Erase the last drawing 104 pygame.draw.rect ( self.screen , ( 0 , 0 , 0 ) , ( hatX , hatY , 100 , 130 ) , 0 ) 105 106 # Label the hat 107 label = self.labelFont.render ( "Hat " + str ( hat ) , True , ( 0 , 255 , 0 ) ) 108 self.screen.blit ( label , ( hatX + 30 , hatY + 100 ) ) 109 110 # Hat left / right 111 if value [ 0 ] == -1: 112 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 25 , hatY + 50 ) , 10 , 0 ) # Hat Left Active -- Draw a filled circle 113 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 75 , hatY + 50 ) , 10 , 1 ) # Hat Right Not Active -- Draw an empty circle 114 elif value [ 0 ] == 1: 115 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 25 , hatY + 50 ) , 10 , 1 ) # Hat Left Not Active -- Draw an empty circle 116 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 75 , hatY + 50 ) , 10 , 0 ) # Hat Right Active -- Draw a filled circle 117 else: 118 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 75 , hatY + 50 ) , 10 , 1 ) # Hat Right Not Active -- Draw an empty circle 119 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 25 , hatY + 50 ) , 10 , 1 ) # Hat Left Not Active -- Draw an empty circle 120 121 # Hat up / down 122 if value [ 1 ] == -1: 123 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 50 , hatY + 75 ) , 10 , 0 ) # Hat Down Active -- Draw a filled circle 124 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 50 , hatY + 25 ) , 10 , 1 ) # Hat Up Not Active -- Draw an empty circle 125 elif value [ 1 ] == 1: 126 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 50 , hatY + 75 ) , 10 , 1 ) # Hat Down Not Active -- Draw an empty circle 127 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 50 , hatY + 25 ) , 10 , 0 ) # Hat Up Active -- Draw a filled circle 128 else: 129 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 50 , hatY + 25 ) , 10 , 1 ) # Hat Up Not Active -- Draw an empty circle 130 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( hatX + 50 , hatY + 75 ) , 10 , 1 ) # Hat Down Not Active -- Draw an empty circle 131 132 pygame.display.flip() 133 134 def plotBalls ( self , ball , position ): 135 ballX = int ( ball / 4 ) * 100 + 5 136 ballY = int ( ball / 4 ) * 125 + 5 137 138 # Clear the old plot 139 pygame.draw.rect ( self.screen , ( 0 , 0 , 0 ) , ( ballX , ballY , 100 , 130 ) , 0 ) 140 141 # Update the ball location 142 self.ballLocations [ ball ] [ 0 ] += position [ 0 ] # index 0 is the X axis 143 self.ballLocations [ ball ] [ 1 ] += position [ 1 ] # index 1 is the Y axis 144 145 # Keep the ball locations "in bounds" 146 if self.ballLocations [ ball ] [ 0 ] < -45: self.ballLocations [ ball ] [ 0 ] = -45 147 if self.ballLocations [ ball ] [ 0 ] > 45: self.ballLocations [ ball ] [ 0 ] = 45 148 if self.ballLocations [ ball ] [ 1 ] < -45: self.ballLocations [ ball ] [ 1 ] = -45 149 if self.ballLocations [ ball ] [ 1 ] > 45: self.ballLocations [ ball ] [ 1 ] = 45 150 151 # Draw a bounding box 152 pygame.draw.rect ( self.screen , ( 0 , 255 , 0 ) , ( ballX , ballY , 100 , 100 ) , 1 ) 153 154 # Plot the ball location in the box 155 pygame.draw.circle ( self.screen , ( 0 , 255 , 0 ) , ( ballX + self.ballLocations [ ball ] [ 0 ] , ballY + self.ballLocations [ ball ] [ 1 ] ) , 2 , 0 ) 156 157 # Label the box 158 label = self.labelFont.render ( "Ball " + str ( ball ) , True , ( 0 , 255 , 0 ) ) 159 self.screen.blit ( label , ( ballX + 30 , ballY + 100 ) ) 160 161 # Flip the screen 162 pygame.display.flip() 163 164 def run ( self ): 165 # The Pygame event loop 166 167 running = True # This is the main "loop running" variable -- set to false to exit the loop 168 while running: # Loop until "running" becomes false 169 for event in pygame.event.get(): # Get all of the events from the queue 170 if event.type == pygame.JOYAXISMOTION: # Main axis movement 171 self.joyAxes [ event.axis ] = event.value 172 self.plotAxes() 173 elif event.type == pygame.JOYBUTTONDOWN: # Buttons pressed 174 self.joyButtons [ event.button ] = True 175 self.plotButtons() 176 elif event.type == pygame.JOYBUTTONUP: # Buttons released 177 self.joyButtons [ event.button ] = False 178 self.plotButtons() 179 elif event.type == pygame.JOYHATMOTION: # Change in hat position 180 self.plotHat ( event.hat , event.value ) 181 elif event.type == pygame.JOYBALLMOTION: # Change in trackball position 182 self.plotBalls ( event.ball , event.rel ) 183 184 # Setup the joysticks 185 pygame.joystick.init() 186 stickCount = pygame.joystick.get_count() # How many joysticks are connected? 187 188 for index in range ( stickCount ): # Print the name of each joystick 189 joystick = pygame.joystick.Joystick ( index ) 190 print ( "{0}) {1}".format ( index , joystick.get_name() ) ) 191 192 # Get the user's selection, and exit if they just press enter 193 selected = input ( "Enter a joystick number or just Enter to exit:" ) 194 if selected == "": sys.exit 195 196 # Convert the selection into an integer 197 index = int ( selected ) 198 199 # Initialize the selected joystick 200 joystick = pygame.joystick.Joystick ( index ) 201 joystick.init() 202 203 # Initialize the pygame display 204 pygame.display.init() 205 screen = pygame.display.set_mode ( ( 800 , 600 ) ) 206 pygame.display.set_caption ( "Joystick tester" ) 207 208 # Initialize the display class 209 window = mainDisplay ( screen , joystick ) 210 211 # Start the main loop 212 window.run()
This tool is useful not only to test the functionality of hardware but to see how Pygame interprets each type of input. Sometimes it can be difficult to tell whether a set of buttons on a joystick operate as buttons or as a hat input. The tool also shows you the index number of each axis, button, hat, or trackball.
Each quadrant of the screen represents different types of joystick inputs. Axes are shown in the upper left quadrant as meters with 0 in the center. Moving each axis of the joystick adjusts the meters appropriately. If an axis does not cause its associated meter to move, you likely have a hardware problem with the joystick.
Joystick buttons are shown in the upper right quadrant. Solid circles represent pressed buttons, and empty circles represent non-pressed buttons.
Hats are in the lower right quadrant. The display mirrors the up/down/left/right diamond configuration of the four buttons as they appear on the joystick. Filled circles represent the hat pushed in that direction. Note that it is possible for a hat to display a combination, such as up and right or down and left, but not up and down or left and right simultaneously.
Trackballs are plotted in the lower left quadrant as squares with a small dot cursor. Each ball allows the cursor to be positioned within its box. Because I do not have any joysticks or gamepads with trackballs, this portion of the code is not tested beyond proper syntax. Multiple joysticks, even if identical (Figure 2), still show up as separate devices.
Buy this article as PDF
Pages: 2
(incl. VAT)