What do I have to do so that the function that I bind with the <KeyPress> event only triggers once, even when I hold the key?
There may be nothing you can do. It could very well be that the keyboard itself is sending multiple events (ie: it's a hardware problem that can't be solved with software).
These events are probably coming very close together -- maybe every 100ms or so. You can use that knowledge to affect how you process events. For example, you can only do something special on a key release if it was at least 200ms after the last press.
Thanks, I hoped tkinter hat something like KeyPressed or KeyPressing Event. I made a kind-off workaround for this problem.
from tkinter import*
m='<KeyPress>'
n='<KeyRelease>'
global kp
kp=0
def press(event):
global kp
if (kp!=event.keysym):
print(event.keysym)
kp=event.keysym
def release(event):
global kp
print(event.keysym)
kp=0
root.bind(m,press)
root.bind(n,release)
Related
import keyboard
import pygame
import mouse
import time
def press_X():
time.sleep(0.2)
keyboard.press('x')
time.sleep(0.6)
keyboard.release('x')
print('Command Executed - press_X')
#SA_R_X V_1.0
#------------------------------------------
while True:
try:
keyboard.add_hotkey('r', press_X)
time.sleep(0.5)
break
except:
keyboard.add_hotkey('r', press_X)
time.sleep(0.5)
break
the problem is the code cannot detect if 'r' is pressed when i am holding 'w' and/or 'space'... (well any key really)
I tried to use a try and except to handle a combination of any key + 'r'. But it did not work. All I need is for the code to be able to detect an 'r' input even if I am pressing/ holding another key at the same time. Then after this the code waits 0.2 seconds before holding down the 'x' key for 0.6 seconds and releasing. Any help is appreciated and it would be very helpful if you included a short explanation on where I went wrong and how you fixed it.
The documentation for this module can be found here. This is where all relevant information can be found.
The best way to do this, from my understanding, is to use an alternative function. If you want the program to continue to run, even though the key has not been pressed, then I suggest using the keyboard.on_press_key() function. This would mean that the rest of your program could still run, and your press_X() function could be run as a callback. Here is an example of how this code could be implemented.
import keyboard
import pygame
import mouse
import time
class App:
running = True
def press_X():
time.sleep(0.2)
keyboard.press('x')
time.sleep(0.6)
keyboard.release('x')
print('Command Executed - press_X')
App.running = False
#SA_R_X V_1.0
#------------------------------------------
keyboard.on_press_key('r', lambda x: press_X()) ## Adds an event listener for the r key
## This will stop execution if there is no code after this point
If you want it to stop the program and wait for the r key to be pressed, then you could use keyboard.wait(). This will basically pause your program until the key is pressed, after which your function would be run. For example, to replace the keyboard.on_press_key('r', lambda x: press_X()):
keyboard.wait('r')
press_X()
From my understanding, keyboard.add_hotkey() does not work in your situation because it is looking for an exact combination of keys being pressed, such as Ctrl+C, and will only go if only the keys in the hotkey are pressed.
I hope this helps, good luck!
I wrote a simple python script that gives control over the cursor to a joystick. My way to find out how this works is documented here. Now that works flawlessly but, as soon as I start the script to use the joystick, the mouse is useless, because my python routine sets the value back to its original, whenever a new joystick event comes in.
Thus I want my joystick events to be ignored as long as a key of the keyboard is pressed. I came across the pygame.key.get_pressed() method but this seems to work only, if the pygame window is in focus. I want this script running in background. Should I start using non-pygame events to listen to the keyboard or are there ways to keep track of the keyboard events analogue to the joystick events, which are recognized in background, via pygame?
I expect pygame sets up its own "sandbox" so that it's hard to detect input from outside its window. Your previous question indicates that you are also using the win32api module. We can use that to detect global key presses.
The correct way to detect key presses at the global scope is to set up a keyboard hook using SetWindowsHookEx. Unfortunately, win32api does not expose that method, so we'll have to use a less efficient method.
The GetKeyState method can be used to determine whether a key is down or up. You can continuously check the state of a key to see if the user has pressed or released it lately.
import win32api
import time
def keyWasUnPressed():
print "enabling joystick..."
#enable joystick here
def keyWasPressed():
print "disabling joystick..."
#disable joystick here
def isKeyPressed(key):
#"if the high-order bit is 1, the key is down; otherwise, it is up."
return (win32api.GetKeyState(key) & (1 << 7)) != 0
key = ord('A')
wasKeyPressedTheLastTimeWeChecked = False
while True:
keyIsPressed = isKeyPressed(key)
if keyIsPressed and not wasKeyPressedTheLastTimeWeChecked:
keyWasPressed()
if not keyIsPressed and wasKeyPressedTheLastTimeWeChecked:
keyWasUnPressed()
wasKeyPressedTheLastTimeWeChecked = keyIsPressed
time.sleep(0.01)
Warning: as with any "while True sleep and then check" loop, this method may use more CPU cycles than the equivalent "set a callback and wait" method. You can extend the length of the sleep period to ameliorate this, but the key detection will take longer. For example, if you sleep for a full second, it may take up to one second between when you press a key and when the joystick is disabled.
when your window gains or looses focus you get an ACTIVEEVENT. It's gain and state attributes tell you which state you've gained or lost. The easisest solution would probably be to catch this events in your main event loop and use them to keep track weather you have focus or not. Then you can just ignore joystick events if you don't have the focus.
This question is NOT a duplicate of this question: Why doesn't the .bind() method work with a frame widget in Tkinter?
As you can see, I set the focus to the current frame in my game_frame() method.
I'm writing a Chip-8 emulator in Python and using Tkinter for my GUI. The emulator is running, but I can't get Tkinter to recognize keypresses. Here is my code:
def game_frame(self):
self.screen = Frame(self.emulator_frame, width=640, height=320)
self.screen.focus_set()
self.canvas = Canvas(self.screen, width=640, height=320, bg="black")
self._root.bind("<KeyPress-A>", self.hello)
for key in self.CPU.KEY_MAP.keys():
print(key)
self.screen.bind(key, self.hello)
self.screen.pack()
self.canvas.pack()
def hello(self, event):
if event.keysym in self.CPU.KEY_MAP.keys():
self.CPU.keypad[self.CPU.KEY_MAP[event.keysym]] = 1
self.CPU.key_pressed = True
self.CPU.pc += 2
sys.exit()
def run_game(self, event):
self.game_frame()
self.CPU.load_rom("TANK")
while True:
self._root.update()
self.after(0, self.CPU.emulate_cycle)
Could you please help me figure out what's going wrong? I think it might have something to do with my game loop interfering with the key bindings, but I'm not sure. The hello method never gets called when I run the game because the program continues to run in an infinite loop and never exits, regardless of what key is pressed. Thank you!
The problem could be due to two things. Without seeing all your code it's impossible to say for sure.
For one, you are binding to a capital "A" rather than a lowercase "a" -- have you testing that the binding works or not when you press a capital A?
Also, you are using after and update incorrectly. You may be starving the event loop, preventing it from processing key presses. The right way to run a function periodically is to have a function that (re)schedules itself.
class CPU_Class():
...
def run_cycle(self):
self.emulate_cycle()
self._root.after(1, self.run_cycle)
Two things to note:
don't use after(0, ...) -- you need to give tkinter at least a ms or so to process other events.
the run_cycle function is responsible for running one cycle, and then scheduling the next cycle to run in the future.
Once you do that, you no longer need your while loop. You can simply call run_cycle once, and that will start the CPU running.
Despite hunting around I can't seem to find an answer to this seemingly simple question:
I'm new to pygame (but not to python), and am trying to get some code to work from continuous button presses - but get_pressed just does not seem to work for me. I made this just to check that I wasn't going insane (I've left out the importing to make it neat for you guys):
def buttonpress():
while True:
keys = pygame.key.get_pressed()
print keys[K_SPACE]
time.sleep(0.5)
buttonpress()
To the best of my knowledge, this should return a '1' when you press the space bar, but no matter what key you change it too - it simply returns an endless string of zeros.
What am I missing?
Thanks
There is no code that processes the input to get all the keys pressed. In order for this to work you need to call event.poll().
So your code will look like this.
import pygame
from pygame.locals import *
import time
pygame.init()
screen = pygame.display.set_mode((640,380))
def buttonpress():
while True:
keys = pygame.key.get_pressed()
print (keys[K_SPACE])
time.sleep(0.5)
pygame.event.poll()
buttonpress()
One more thing, do not use time.sleep(). This pauses the thread, and can cause the OS to think that your application does not respond (since it's not removing events from the event queue).
I would like to know if there is a way of using poll() or get() without removing the events from the queue.
In my game, I check input at different places (not only in the main loop) and sometimes I need to check the same event at different places but when i check it once it removes it from the queue. I tried using peek() but the problem is that I can't get the key corresponding to the event done.
while 1:
event = pygame.event.poll()
if event.type == KEYDOWN:
return event.key
else:
pass
#works but removes event from the queue
This can get the key corresponding to the event but with peek() it can't:
pygame.event.peek(pygame.KEYDOWN).key
#dosent work
However I can't use the first method because removes the event from the queue so I can't check key events elsewhere in the program.
I don't understand well how the queue works so maybe I'm just mistaking but I tried the first one at different location and only the first time i checked the event it worked.
My goal is to check events in different classes in my game.
Thanks for your help
I think a better design would be to check events in a single place - even if in a factored out function or method outside the mainloop code, and keep all the relevnt event data in other objetcts (as attributes) or variables.
For example, you can keep a reference to a Python set with all current pressed keys, current mouse position and button state, and pass these variables around to functions and methods.
Otherwise, if your need is to check only for keys pressed and mouse state (and pointer posistion) you may bypass events entirely (only keeping the calls to pygame.event.pump() on the mainloop). The pygame.key.get_pressed function is my favorite way of reading the keyboard - it returns a sequence with as many positions as there are key codes, and each pressed key has its correspondent position set to True in this vector. (The keycodes are available as constants in pygame.locals, like K_ESC, K_a, K_LEFT, and so on).
Ex:
if pygame.key.get_pressed()[pygame.K_ESCAPE]:
pygame.quit()
The mouse module (documented in http://www.pygame.org/docs/ref/mouse.html) allows you to get the mouse state without consuming events as well.
And finally, if you really want to get events, the possibility I see is to repost events to the Queue if they are not consumed, with a call to pygame.event.post - this call could be placed, for example at the else clause in an if/elif sequence where you check for some state in the event queue.
I don't know if it is good style, but what I did was just saving all the events in a variable and passing it to the objects that used their own event queues to detect "their" events.
while running:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
running = False
self.allS.update(events)
and in the update method:
for event in events:
print("Player ", event)
As far as I can tell there is no one 'right' way to do this, but one option is to save all the events into a variable. Then you can access them as many times as you want.