Simple keyboard program with issues - python

This program sometimes counts one key press as 2 presses or as 0,5 press.
Program:
import keyboard
while True:
if keyboard.read_key() == 'h':
print("1")
elif keyboard.read_key() == 'j':
print("2")
elif keyboard.read_key() == 'k':
print("3")
I want this program to count 1 key press as one action. I have no clue how to solve this issue.

The keys (events register) are double because you press the "h" key and release the "h" key. The answer is here:
keyboard.read_key() records 2 events
try this:
import keyboard
while True:
event = keyboard.read_event()
if event.event_type == keyboard.KEY_DOWN and event.name == 'h':
print('1')
elif event.event_type == keyboard.KEY_DOWN and event.name == 'j':
print('2')
elif event.event_type == keyboard.KEY_DOWN and event.name == 'k':
print('3')

Related

Python, check if arrow key pressed

How to check if user presses an arrow key in python? I want something like this:
if right_key.pressed():
do_some_shit()
elif left_key.pressed():
do_other_stuff()
In your terminal (or anacoonda prompt) run this command to install pynput library:
pip install pynput
And, in your editor
from pynput import keyboard
from pynput.keyboard import Key
def on_key_release(key):
if key == Key.right:
print("Right key clicked")
elif key == Key.left:
print("Left key clicked")
elif key == Key.up:
print("Up key clicked")
elif key == Key.down:
print("Down key clicked")
elif key == Key.esc:
exit()
with keyboard.Listener(on_release=on_key_release) as listener:
listener.join()
More info
You can use this code:
import keyboard
import time
while True:
try:
if keyboard.is_pressed('left'):
print('You Pressed left!')
time.sleep(0.1)
if keyboard.is_pressed('right'):
print('You Pressed right!')
time.sleep(0.1)
if keyboard.is_pressed('down'):
print('You Pressed down!')
time.sleep(0.1)
if keyboard.is_pressed('up'):
print('You Pressed up!')
time.sleep(0.1)
except:
break
By using listeners you will not have to play an infinite loop. Which I think is more elegant. This code will help you:
from pynput import keyboard
def on_press(key):
if key == keyboard.Key.up:
print('PRESSED')
if key == keyboard.Key.esc:
listener.stop()
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
Note that with 'keyboard.Key' you can detect the key that you want. You can even reproduce the case of holding two keys at the same time and detecting a combination!
This is the code I made, but it's only for a Pygame project with a window
If you need the arrows in pygame I recommend this code:
from pygame.locals import *
import pygame
import sys
window_height = 100
window_width = 100
window = pygame.display.set_mode((window_width, window_height))
# This is the code to check if a player is pushing the arrows
while True:
for evenement in pygame.event.get():
if evenement.type == QUIT or (evenement.type == KEYDOWN and
evenement.key == K_ESCAPE):
print('QUIT')
pygame.quit()
sys.exit()
if evenement.type == KEYDOWN and evenement.key == K_RIGHT:
print("Clicked on the right arrow")
if evenement.type == KEYDOWN and evenement.key == K_LEFT:
print("Clicked on the left arrow")
if evenement.type == KEYDOWN and evenement.key == K_UP:
print("Clicked on the up arrow")
if evenement.type == KEYDOWN and evenement.key == K_DOWN:
print("Clicked on the down arrow")

Require holding key down for movement in pacman pygame with game pad

I've been modifying the PACMAN pygame code from here. Currently, I have a function that requires players to hold down a direction key in order to keep moving in that direction. The code for that is:
def process_events(self):
for event in pygame.event.get(): # User did something
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: # exit program entirely
self.run_over = True
return True
elif event.key == pygame.key_r:
self.player.move_right()
elif event.key == pygame.key_l:
self.player.move_left()
elif event.key == pygame.key_u:
self.player.move_up()
elif event.key == pygame.key_d:
self.player.move_down()
elif event.type == pygame.KEYUP:
if event.key == pygame.key_r:
self.player.stop_move_right()
elif event.key == pygame.key_l:
self.player.stop_move_left()
elif event.key == pygame.key_u:
self.player.stop_move_up()
elif event.key == pygame.key_d:
self.player.stop_move_down()
return False
While this works perfectly for keyboard button presses, I'm trying to incorporate a gamepad, which uses capital letters to designate a key down button press and lower case letters for keyup button presses. For example, when moving in the downward direction, keypress down is "D" and keypress up/release is "d". Using the same function from above with the gamepad results in me having to button mash to barely move. I, therefore, modified the function to this:
def process_events(self):
for event in pygame.event.get(): # User did something
try:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: # exit program entirely
self.run_over = True
return True
elif event.key == pygame.key_r:
self.player.move_right()
elif event.key == pygame.key_l:
self.player.move_left()
elif event.key == pygame.key_u:
self.player.move_up()
elif event.key == pygame.key_d:
self.player.move_down()
except:
self.player.stop_move_right()
self.player.stop_move_left()
self.player.stop_move_up()
self.player.stop_move_down()
return False
However, with this new code, I simply tap a direction button and I move in that direction indefinitely until I press a different button. Is there a way for me to modify my code so that the player needs to hold down the gamepad direction button to move and they stop once the button is released?
Thanks to #qouify, I realized that my modified function was incorrectly set up. I therefore adjusted it to this, which now performs the correct functionality:
def process_events(self):
counter = []
for event in pygame.event.get(): # User did something
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: # exit program entirely
self.run_over = True
return True
else: # direction movement
if event.key in [pygame.K_LSHIFT, pygame.K_r, pygame.K_l, pygame.K_u, pygame.K_d]:
counter.append(pygame.key.name(event.key))
# right
if len(counter) == 2 and counter[0] == "left shift" and counter[1] == "r":
self.player.move_right()
elif len(counter) == 1 and counter[0] == "r":
self.player.stop_move_right()
# left
if len(counter) == 2 and counter[0] == "left shift" and counter[1] == "l":
self.player.move_left()
elif len(counter) == 1 and counter[0] == "l":
self.player.stop_move_left()
# up
if len(counter) == 2 and counter[0] == "left shift" and counter[1] == "u":
self.player.move_up()
elif len(counter) == 1 and counter[0] == "u":
self.player.stop_move_up()
# down
if len(counter) == 2 and counter[0] == "left shift" and counter[1] == "d":
self.player.move_down()
elif len(counter) == 1 and counter[0] == "d":
self.player.stop_move_down()
return False
I realize this may not be the most elegant or "pythonic" solution, in which case I'm happy for anyone to offer suggestions. Otherwise, I'll consider this solved.

How to have python function run only while key is pressed?

I am trying to make a simple program where when you hold 'w' it goes forward using forward () but when you let go, it stops the motors using stop(). Currently, I can only get it to continuously go forward when 'w' is pressed and only stop when another key is pressed.
Here is my code
#!/usr/bin/env python3
# so that script can be run from Brickman
import termios, tty, sys, time
from ev3dev.ev3 import *
# attach large motors to ports B and C, medium motor to port A
motor_left = LargeMotor('outA')
motor_right = LargeMotor('outD')
motor_a = MediumMotor('outC')
#==============================================
def getch():
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
tty.setcbreak(fd)
ch = sys.stdin.read(1)
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
#==============================================
def fire():
motor_a.run_timed(time_sp=3000, speed_sp=600)
#==============================================
def forward():
motor_left.run_forever(speed_sp=1050)
motor_right.run_forever(speed_sp=1050)
#==============================================
def back():
motor_left.run_forever(speed_sp=-1050)
motor_right.run_forever(speed_sp=-1050)
#==============================================
def left():
motor_left.run_forever(speed_sp=-1050)
motor_right.run_forever(speed_sp=1050)
#==============================================
def right():
motor_left.run_forever(speed_sp=1050)
motor_right.run_forever(speed_sp=-1050)
#==============================================
def stop():
motor_left.run_forever(speed_sp=0)
motor_right.run_forever(speed_sp=0)
#==============================================
print("ready")
k = getch()
print(k)
if k == 'w':
forward()
if k == 's':
back()
if k == 'a':
left()
if k == 'd':
right()
if k == 'f':
fire()
if k == ' ':
stop()
if k == 'q':
stop()
exit()
Any idea on how to make it so stop() runs when 'w' is unpressed?
You can use pygame's KEYUP and KEYDOWN functionality. Following is a snippet of code that uses pygame to that:
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
finished = False
isKeyPressed = False
while not finished:
for event in pygame.event.get():
if isKeyPressed:
print "Key is currently pressed...Move forward!"
if event.type == pygame.QUIT:
finished = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
isKeyPressed = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_w:
isKeyPressed = False
#Perform action (here) when 'w' is unpressed
pygame.display.flip()
clock.tick(60)
pygame.quit()

Pygame: How do I get KEYDOWN to input only once?

Hi I'm creating a game where I have multiple stages. I want to make it so that every time the user presses the key a, the next stage will trigger. Here is a sample of my code.
gameStage = 0 ## outside while loop
##INSIDE whileloop
if gameStage == 0:
##insert drawings,music, etc
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
gameStage += 1
if gameStage == 1:
##insert drawings,music, etc
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
gameStage += 1
my problem is that when the user presses the a key, a will be input more than once depending how long the key is held. Therefore, it will just skip all the way to my last stage. How do I make it so that the gameStage is +=1 only when the key has been pressed AND lifted? Please tell me if I'm being unclear. Appreciate any help. Thanks.
You could use the pygame.KEYUP event.
e.g.
if event.type == pygame.KEYUP:
But you should not be getting repeated KEYDOWN messages, unless you have called pygame.key.set_repeat and set a non zero repeat value.
The fact that you get the repeated increments of GameStage even when you capture only KEYUP messages would indicate that there is some other issue in your code.
When keyboard buttons are pressed or released a pygame.KEDOWN or pygame.KEYUP event appears only ones on the event queue1
As you did, you need to set a global variable GameStage, which indicates the current game state:
GameStage = 0 ## outside while loop
After this we run our main game loop and fetch all events form the even queue using the pygame.event.get() function, which reads and removes events from the queue:
while True:
#get all events from the event queue
for ev in pygame.event.get():
If the read event represents a key-down-event of a the program logic updates the GameStage variable, similar to a so-called state-machine:
if ev.type == pygame.KEYDOWN:
if ev.key == pygame.K_a:
if GameStage == 0:
GameStage += 1
#do something
elif GameStage == 1:
GameStage += 1
#do something great
# and so on ;)
The complete program block looks like this:
#global variable GameStage for state-machine
GameStage = 0
#main game loop
while True:
#get all events from the event queue
for ev in pygame.event.get():
if ev.type == pygame.KEYDOWN:
if ev.key == pygame.K_a:
if GameStage == 0:
GameStage += 1
#do something
elif GameStage == 1:
GameStage += 1
#do something great
elif GameStage == 2:
GameStage += 1
#do something great again
elif GameStage == 3:
#this is the last stage, so you cloud go back to stage #0
GameStage = 0
#for debugging print current GameStage
print(GameStage)
Only when you press -- no matter how long you hold down -- a the GameStage will be updated only once.
Hope this helps :)
1 As #sloth noted, this can be changed be calling pygame.key.set_repeat(), which will generate multiple pygame.KEYDOWN events when keys are held down.
You can either use key up event, as suggested above, or stop reacting to keydown event for a few ticks after it was issued.

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()

Categories