Getting keyboard input in pygame - python

I have been searching around and trying to fix this for hours. I have two scripts, one labeled main.py, and the other menu.py. I am trying to use a variable from main.py to control the actions of menu.py. The variable is set to the last keyboard input, in the main.py script, it does what it's supposed to. However, when I try to do it in menu.py, nothing happens, not error thrown, nor does the original animation I was trying to use happen. I have omitted parts of the code that I don't think are part of the problem to make it easier to read.
main.py code:
#!/usr/bin/env python
WIDTH = 512
HEIGHT = 416
FPS = 20
VERSION = "0.0.3"
FULLSCREEN = False
import pygame,sys,os,time
pygame.init()
display = pygame.display.set_mode((WIDTH,HEIGHT), pygame.DOUBLEBUF)
alpha_display = display.convert_alpha()
fpsClock = pygame.time.Clock()
keyPressed = 0
sys.path.insert(0, "scripts/")
from pygame.locals import *
from media import *
import color
import menu
def run():
display.fill(color.BLACK)
if (menu.active == True):
menu.update()
else:
pass
while (True): # Code to run loop
for event in pygame.event.get():
if (event.type == QUIT):
pygame.quit()
sys.exit()
if (event.type == pygame.KEYDOWN):
time.sleep(0.1)
keyPressed = event.key
elif (event.type == pygame.KEYUP):
keyPressed = 0
run()
pygame.display.update()
fpsClock.tick(FPS)
menu.py code:
from __main__ import *
page = "main"
selObj = 1
active = True
class o:
dee = False
def update():
if o.dee == False:
o.dee = True
global page
global selObj
global active
if page == "main": # Main menu
if (keyPressed == pygame.K_RETURN):
if (selObj == 1):
page = "singleplayer"
elif (selObj == 2):
pygame.quit()
sys.exit()
if (keyPressed == pygame.K_UP):
selObj -= 1
elif (keyPressed == pygame.K_DOWN):
selObj += 1
if (selObj >= 3):
selObj = 1
elif (selObj <= 0):
selObj = 2
if (selObj == 1):
print("test1"
elif (selObj == 2):
print("test2")

I believe I have found your problem. You are looking for pygame events in the update() function (by using keypressed), but you never call that function in the pygame events loop. I don't know if somewhere in the lines you left out you initialize keyPressed, but from what I see you don't (which should raise a ValueError exception). If you did initialize keyPressed in the menu.py file, you are not transferring the changed value from main.py to menu.py. The way I would recommend to fix that would be to call menu.update() within the event loop and have keyPressed as an argument.
from __main__ import *
page = "main"
selObj = 1
active = True
class o:
dee = False
def update(keyPressed):#<----#
if o.dee == False:
o.dee = True
global page
global selObj
global active
if page == "main": # Main menu
if (keyPressed == pygame.K_RETURN):
...
and in the main file:
...
def run():
display.fill(color.BLACK)
if (menu.active == True):
menu.update(keyPressed)#<-------#
else:
pass
while (True): # Code to run loop
for event in pygame.event.get():
if (event.type == QUIT):
pygame.quit()
sys.exit()
if (event.type == pygame.KEYDOWN):
time.sleep(0.1)
keyPressed = event.key
elif (event.type == pygame.KEYUP):
keyPressed = 0
menu.update(keyPressed)#<----#
run()
pygame.display.update()
fpsClock.tick(FPS)
...
You may have to modify your code further to make sure that your menu does not reset each time the mouse moves.
Hope this helped.

Related

How to make my Pygame window always active and listening for input?

I spent the last 3 hours searching for a way to create a simple toggle button script that lets me know if a button on my Thrustmaster HOTAS has been pressed. As I am a bloody beginner I didn't write this code myself. I found one that suits my means and configured it to my liking.
Now I want this script to be active and running when I am ingame. Preferably also on top of other windows. Because it doesn't recognize the inputs if the window isn't active.
Bring a pygame window to front
How to make python window run as "Always On Top"?
Pygame set window on top without changing its position
These were the most promising ones I found beside many others. Tried all of them even though the second one is for linux.
pygame capture keyboard events when window not in focus
Also found this one, but they explain how to hook Keyboard inputs, not Joystick inputs.
I am using Win10 and the newest python version.
Update: I added these lines and now the window stays on top all the time but it's still not always active.
import win32gui
import win32con
hwnd = win32gui.GetForegroundWindow()
win32gui.SetWindowPos(hwnd,win32con.HWND_TOPMOST,100,100,200,200,0)
That's the whole script I am using
import sys
import pygame
import win32gui
import win32con
from pygame.locals import *
pygame.init()
pygame.display.set_caption('ToggleCheck')
screen = pygame.display.set_mode((180, 110), 0, 32)
hwnd = win32gui.GetForegroundWindow()
win32gui.SetWindowPos(hwnd,win32con.HWND_TOPMOST,100,100,200,200,0)
clock = pygame.time.Clock()
pygame.mixer.init(frequency=44100, size=32, channels=2, buffer=4096)
pygame.joystick.init()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
for joystick in joysticks:
print(joystick.get_name())
my_square = pygame.Rect(30, 30, 50, 50)
my_square2 = pygame.Rect(100, 30, 50, 50)
my_square_color = 0
my_square_color2 = 0
colors = [(255, 0, 0), (0, 255, 0)]
motion = [0, 0]
while True:
screen.fill((0, 0, 0))
pygame.draw.rect(screen, colors[my_square_color], my_square)
pygame.draw.rect(screen, colors[my_square_color2], my_square2)
for event in pygame.event.get():
if event.type == JOYBUTTONDOWN:
print(event)
if event.button == 8:
if (my_square_color % 2) == 0:
my_square_color = (my_square_color + 1) % len(colors)
on = pygame.mixer.Sound("Desktop\DCS\checker\sounds\on.wav")
pygame.mixer.Sound.play(on)
else:
my_square_color = (my_square_color + 1) % len(colors)
on = pygame.mixer.Sound("Desktop\DCS\checker\sounds\off.wav")
pygame.mixer.Sound.play(on)
if event.type == JOYBUTTONDOWN:
print(event)
if event.button == 9:
if (my_square_color2 % 2) == 0:
my_square_color2 = (my_square_color2 + 1) % len(colors)
on = pygame.mixer.Sound("Desktop\DCS\checker\sounds\on.wav")
pygame.mixer.Sound.play(on)
else:
my_square_color2 = (my_square_color2 + 1) % len(colors)
on = pygame.mixer.Sound("Desktop\DCS\checker\sounds\off.wav")
pygame.mixer.Sound.play(on)
if event.type == JOYDEVICEADDED:
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
for joystick in joysticks:
print(joystick.get_name())
if event.type == JOYDEVICEREMOVED:
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
pygame.display.update()
clock.tick(60)
Try using this py-joystick module
https://pypi.org/project/pyjoystick/
from pyjoystick.sdl2 import Key, Joystick, run_event_loop
def print_add(joy):
print('Added', joy)
def print_remove(joy):
print('Removed', joy)
def key_received(key):
print('received', key)
if key.value == Key.HAT_UP:
#do something
elif key.value == Key.HAT_DOWN:
#do something
if key.value == Key.HAT_LEFT:
#do something
elif key.value == Key.HAT_UPLEFT:
#do something
elif key.value == Key.HAT_DOWNLEFT:
#do something
elif key.value == Key.HAT_RIGHT:
#do something
elif key.value == Key.HAT_UPRIGHT:
#do something
elif key.value == Key.HAT_DOWNRIGHT:
#do something
run_event_loop(print_add, print_remove, key_received)

Scrolling infinite background in Pygame [duplicate]

This question already has answers here:
Making the background move sideways in pygame
(2 answers)
How to scroll the background surface in PyGame?
(1 answer)
Why is my pygame application loop not working properly?
(1 answer)
Closed 2 years ago.
Hi I am trying to render out the menu_background in my menu screen with my menu options showing. When I run my code all I get is the image scrolling but my menu options and audio do not play and show. Anyone can help write code that makes it work where the audio plays and my menu options show up?
import pygame, sys, random, time, os, math
from pygame.locals import *
fps = pygame.time.Clock()
global screen
screen = pygame.display.set_mode((WIDTH, HEIGHT),pygame.FULLSCREEN)
display = pygame.Surface((400,250))
def menu_background():
menu_bg = pygame.image.load("assets/images/menu_bg.png").convert()
x = 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
rel_x = x % menu_bg.get_rect().width
screen.blit(menu_bg, (rel_x - menu_bg.get_rect().width, 0))
if rel_x < WIDTH:
screen.blit(menu_bg, (rel_x, 0))
x -= 1
pygame.display.update()
fps.tick(120)
def menu():
bg = menu_background()
menu_options = ['Play','Controls','Highscores','Settings','Credits','Quit']
menu_choice = 0
in_menu = True
pygame.mixer.music.load('assets/audio/mainmenu.wav')
while in_menu:
bg(display)
n = 0
for option in menu_options:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_UP:
menu_choice -= 1
if menu_choice < 0:
menu_choice = len(menu_options)-1
if event.key == K_DOWN:
menu_choice += 1
if menu_choice >= len(menu_options):
menu_choice = 0
if event.key == K_SPACE:
choice = menu_options[menu_choice]
if choice == 'Play':
play()
if choice == 'Controls':
controls()
screen.blit(pygame.transform.scale(display,(WIDTH,HEIGHT)),(0,0))
pygame.display.update()
fps.tick(60)
The major issue is that you have implemented 2 main application loops. The loops are executed one after the other.
(By the way, the first of this loops never terminates)
Put all the implementation in one loop. The application loop has to
handle the events and change states
draw the background
draw the scene and menu
update the display
e.g.:
def menu():
x = 0
menu_bg = pygame.image.load("assets/images/menu_bg.png").convert()
menu_options = ['Play','Controls','Highscores','Settings','Credits','Quit']
menu_choice = 0
in_menu = True
# load and play music
pygame.mixer.music.load('assets/audio/mainmenu.wav')
pygame.mixer.music.play()
while in_menu:
fps.tick(120)
# handle events
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_UP:
menu_choice -= 1
if menu_choice < 0:
menu_choice = len(menu_options)-1
if event.key == K_DOWN:
menu_choice += 1
if menu_choice >= len(menu_options):
menu_choice = 0
if event.key == K_SPACE:
choice = menu_options[menu_choice]
if choice == 'Play':
play()
if choice == 'Controls':
controls()
# draw background
rel_x = x % menu_bg.get_rect().width
screen.blit(menu_bg, (rel_x - menu_bg.get_rect().width, 0))
if rel_x < WIDTH:
screen.blit(menu_bg, (rel_x, 0))
x -= 1
# draw menu
#n = 0
#for option in menu_options:
# [...]
# update disaplay
pygame.display.update()
menu()

pygame continuous and simultaneous key inputs

I am stuck again and cannot find any valid solutions online. I am trying to use pygame and its key inputs to control various things. Now I need to use several keys simultaneously. My code is as follows:
pygame.key.set_reapeat(50,50)
bProgramLoop = True
while (bProgramLoop == True):
for event in pygame.event.get():
if (event.type == pygame.QUIT):
bProgramLoop = False
if (pygame.key.get_pressed()[pygame.K_LEFT]):
EXECUTE_FUNCTION1()
print "left"
if (pygame.key.get_pressed()[pygame.K_RIGHT]):
EXECUTE_FUNCTION2()
print "right"
Now the problem that I have is:
When I hold down "LEFT of RIGHT" it correctly and continuously registers that I pressed left/right. BUT when I hold in "LEFT" and just tap "RIGHT", it registers that left and right were pressed but it then stops to register that "LEFT" is still being pressed.
Any ideas anyone?
Any help would be greatly appreciated.
Misha
In my code the "repeat" is correctly spelt.
I found the work around for my problem. The above code needs to be modified.
pygame.key.set_repeat(50,50)
bProgramLoop = True
while (bProgramLoop == True):
for event in pygame.event.get():
if (event.type == pygame.QUIT):
bProgramLoop = False
if (event.type == pyame.KEYDOWN):
if (event.key == pygame.K_a) # if A is pressed
bKeyA = True # set the Boolean True
if (event.key == pygame.K_s)
bKeyS = True
if (event.type == pyame.KEYDOWN):
if (event.key == pygame.K_a) # if A is released
bKeyA = False# set the Boolean False
if (event.key == pygame.K_s)
bKeyS = False
if (bKeyA == True):
Execute_function1()
if (bKeyB == True):
Execute_function2()
I double checked, the repeat is correctly spelt and it would not continue a keyboard input once another one was tapped. The problem is, as far as I can figure it out, and even occurs once at the point when a key is pressed. When another key is simultaneously pressed the event is lost.
Thus the solution is to set a variable true until the key is lifted up, and thus the variable is set false.
You have misspelled repeat in pygame.key.repeat(). I corrected this and it worked for me.
def main():
while Running:
check_events()
update()
clock.tick(FPS)
def check_events():
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
sys.exit()
if key == pygame.K_q:
Running = False
return
if (pygame.key.get_pressed()[pygame.K_LEFT]):
#EXECUTE_FUNCTION1()
print "left"
if (pygame.key.get_pressed()[pygame.K_RIGHT]):
#EXECUTE_FUNCTION2()
print "right"
If you want to use continuous inputs than try this, it is my code.
import pygame as py
import time
sc = py.display.set_mode((800, 600))
x = 350
y = 300
blue = (0, 0, 255)
last = 0
while True:
key = py.key.get_pressed()
for event in py.event.get():
if event.type == py.KEYDOWN:
last = event.key
else:
last = 0
if last == py.K_UP:
y -= 0.1
if last == py.K_DOWN:
y += 0.1
if last == py.K_LEFT:
x -= 0.1
if last == py.K_RIGHT:
x += 0.1
sc.fill((0,255,0))
py.draw.rect(sc, blue, (x,y,50,50))
py.display.flip()
If you want to use simultaneous input, then here:
import pygame as py
import time
sc = py.display.set_mode((800, 600))
x = 350
y = 300
blue = (0, 0, 255)
last = 0
def move(times, yspeed, xspeed):
for i in range(times):
global x, y
x += (xspeed / times)
y += (yspeed / times)
time.sleep((xspeed / times / 10) + (yspeed / times / 10))
while True:
key = py.key.get_pressed()
for event in py.event.get():
if event.type == py.KEYDOWN:
last = event.key
else:
last = 0
if event.key == py.K_UP and event.key == py.K_l:
y -= 0.1
sc.fill((0,255,0))
py.draw.rect(sc, blue, (x,y,50,50))
py.display.flip()

Detecting duration of a keypress python/pygame

In my program I'd like users to be able to hold down a button. Upon releasing the button I wish to print the duration that they held down the key. I have been trying to use the pygame clock function but have run into some trouble. The program works fine for the first keypress, but on later keypresses records any downtime between keypresses. Any help would be appreciated, here is my code:
import pygame
from pygame.locals import *
def main():
key = 0
pygame.init()
self = pygame.time.Clock()
surface_sz = 480
main_surface = pygame.display.set_mode((surface_sz, surface_sz))
small_rect = (300, 200, 150, 90)
some_color = (255, 0, 0)
while True:
ev = pygame.event.poll()
if ev.type == pygame.QUIT:
break;
elif ev.type == KEYUP:
if ev.key == K_SPACE: #Sets the key to be used
key += 1 #Updates counter for number of keypresses
while ev.type == KEYUP:
self.tick_busy_loop()
test = (self.get_time()/1000.0)
print "number: ", key, "duration: ", test
ev = pygame.event.poll()
main()
You can use keyboard library for this.
Here is a sample code that I made:
import keyboard, time
while True:
a = keyboard.read_event() #Reading the key
if a.name == "esc":break #Loop will break on pressing esc, you can remove that
elif a.event_type == "down": #If any button is pressed (Not talking about released) then wait for it to be released
t = time.time() #Getting time in sec
b = keyboard.read_event()
while not b.event_type == "up" and b.name == a.name: #Loop till the key event doesn't matches the old one
b = keyboard.read_event()
print('Pressed Key "'+ b.name + '" for ' + str(time.time()-t))
If you are looking for more solutions (for Pygame or Pynput), then you can find them on my answer on other related question.
I recommend using get_ticks() instead of get_time(). You should read about the differences, but I feel it may not work as desired since you are not explicitly calling self.tick().
The problem is your code is outputting the time between each KEYUP event. There is another way you make the code work by going over the events once per loop and continuing onward without having the nested while loop.
time_down = 0.0
time_elapsed = 0.0
while True:
for ev in pygame.event.get():
if ev.type == QUIT:
break # you had a semicolon here, there is no need for it
elif ev.type == KEYDOWN:
if ev.key == K_SPACE:
time_down = pygame.time.get_ticks()
elif ev.type == KEYUP:
if ev.key == K_SPACE:
key += 1
time_elapsed = (pygame.time.get_ticks() - time_down)/1000.0
print "number: ", key, "duration: ", time_elapsed
self.tick()
pygame.display.update()

Variables has the same value, pygame

I'm trying to make a game in pygame, but for some reason actor and inp has the same value.
I tried to have them as arrays instead of classes, but it didn't solve the problem.
import pygame, sys
from pygame.locals import *
pygame.init()
screen=pygame.display.set_mode((640,360),0,32)
a=pygame.image.load('a.png')
class xy:
x=0
y=0
jump=0
actor=xy
inp=xy
def events():
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
return
elif event.type == KEYDOWN:
if event.key==K_LEFT:
inp.x=-1
elif event.key==K_RIGHT:
inp.x=1
elif event.key==K_UP:
inp.y=-1
elif event.key==K_DOWN:
inp.y=1
elif event.key==K_SPACE:
inp.jump=True
elif event.type==KEYUP:
if event.key==K_LEFT:
inp.x=0
elif event.key==K_RIGHT:
inp.x=0
elif event.key==K_UP:
inp.y=0
elif event.key==K_DOWN:
inp.y=0
elif event.key==K_SPACE:
inp.jump=False
return
def controller():
if inp.x<0:
actor.x-=1
elif inp.x>0:
actor.x+=1
if inp.y<0:
actor.y-=1
elif inp.y>0:
actor.y+=1
## actor.x+=inp.x
## actor.y+=inp.y
return
def screen_update():
pygame.draw.rect(screen, 0x006633, ((0,0),(640,360)),0)
screen.blit(a, (actor.x,actor.y))
pygame.display.update()
if __name__ == '__main__':
while True:
events()
print 'inp='+str(inp.x)+','+str(inp.y)
print 'actor='+str(actor.x)+','+str(inp.y)
controller()
screen_update()
Why can't the things I make work properly? :(
To put it simply, you're doing classes completely wrong.
class xy:
def __init__(self):
self.x = 0
self.y = 0
self.jump = 0
actor = xy()
inp = xy()

Categories