How can `sdl2.SDL_GetKeyboardState` be used correctly? - python

I'm attempting to use the python library pysdl2 to construct an emulator. The library has been working well so far, however I've been having problems receiving keyboard input.
What I essentially needed to do is test if certain keys are pressed. After doing a bit of research, I discovered sdl2.SDL_GetKeyboardState which supposedly is the same SDL function as SDL_GetKeyboardState. Following the previously linked documentation and this article on the Lazy Foo' Productions website, I constructed the following script:
import sdl2
sdl2.ext.init()
window = sdl2.ext.Window('Test', size=(640, 480))
window.show()
key_states = sdl2.SDL_GetKeyboardState(None)
running = True
while running:
for event in sdl2.ext.get_events():
if event.type == sdl2.SDL_QUIT:
running = False
break
if key_states[sdl2.SDL_SCANCODE_A]:
print('A key pressed')
window.refresh()
The above code is suppose to detect if the a key is pressed and if so print a message out. When the above program is run, a window does appear, but when the a key is pressed, 'A key pressed' is printed over four thousand times. It does not continue to print the message, it only prints it once thousands of times, then stops.
At first, I consider that the problem may have been that the key deduction code (lines 15-16) should be inside of the the event loop (lines 11-14). It worked to some degree. Rather than 'A key pressed' being printed out thousands of times per key press, it was only being printed out twice per key press.
Is there a problem with my code? Am I missing something about how to correctly use the sdl2.SDL_GetKeyboardState function? How can I properly detect key presses?

Sounds like it's working the way it's supposed to. key_states[sdl2.SDL_SCANCODE_A] will return true whenever a is pressed. And there's not much processing going on in your loop, so it'll loop as fast as your CPU allows, printing out "A key pressed" hundreds or thousands of times per second, until you release the key.
You could check for a different event type, such as SDL_KEYDOWN, which operates more like how you're wanting, or you can keep track of the keypress with a variable, for example:
key_down = False
while running:
for event in sdl2.ext.get_events():
if event.type == sdl2.SDL_QUIT:
running = False
break
if key_states[sdl2.SDL_SCANCODE_A] and not key_down:
print('A key pressed')
key_down = True
elif not key_states[sdl2.SDL_SCANCODE_A] and key_down:
print('A key released')
key_down = False
window.refresh()

Related

Single key detection to work with the possibility of adding long inputs

I have been facing this problem for the last week,I thought it would be trivial but after trying many different approaches I don't know what else to try.
I have an application where I need to have key detection (to move a robot arm with the keyboard) but when I press enter I need to add some inputs, which should be as long as I want, just some normal input("insert here").
I know about the python libraries to get key detection, I got pynput to work successfully but it crashes my raspberry pi when I start and stop the threads a few times,I tried the Keyboard library but the whole root requirement is a let down, I also got curses to work and this seems to be solid and is (almost) not causing any issues, so detecting 1 key is not a problem.
I of course know how to name my files and get all the information that I need by doing input(), so if I had to use one of those options the job would be rather simple, the challenge comes when I try to apply both approaches together, basically detect the keys to do everything I need, and use python Input to get all the inputs from the user as soon as enter is pressed, all the libraries to detect key seems to take full control and they don't want to release it without a fight. They seem to expect the user to always require single key detection but in my case I would need to constantly turn it on and off, I couldn't figure out any efficient (or not) way to get it to work properly.
My question is:
What is the best approach to have key detection + full user input when needed with curses (or any alternative) in a non blocky way (as my code need to do some other things while listening for keys), is creating and destroying the whole thing the only alternative?
This is my current test code that I created for simplicity (which works but blocks everything while listening for keys):
import curses
import time
import os
stdscr = None
addInput = False
def SetupCurses():
global stdscr
stdscr = curses.initscr()
curses.cbreak()
stdscr.keypad(1)
def StartCurse():
global addInput
key = ''
while key != ord('q'):
key = stdscr.getch()
stdscr.addstr(str(key))
if key == ord('a'):
print("\nyou pressed a\n")
if key == 10:
print("\nyou pressed enter!\n")
addInput = True
break
def EndCurse():
curses.endwin()
while(True):
SetupCurses()
StartCurse()
EndCurse()
if addInput:
theinput = input("add your input\n")
print(theinput)
time.sleep(4)
addInput = False
#if there isn't any input to add I want the code to continue because there is non-related keys stuff to do, but of course it stopped at "StartCurse"
#if there is something to add the code can stop at addInput
The reason for the loop is because the user can save as many positions as he want, so after adding some inputs the possibility of adding more is there.
I saw people making this non-blocking by closing the curses loop after a few seconds (which stops everything anyway...) kind of getting the input by luck...something like:
def ExecuteCurses():
global AddInput
#open it and close it very quickly to grab a key if it is pressed
c = stdscr.getch()
if c == ord('a'):
print("you pressed a")
AddInput = True
time.sleep(1)
curses.endwin()
If you want a full and long user input you will need to use the curses.echo() and then use the stdscr.getstr(). That will wait for the user to press enter().
And to not block the program while getting input you need threading which you will have to import at the top of your program
And for the threading here is a link so you can find out more about threading.
I hope it answers your question

Resize pygame window without losing keyboard state

I'm facing a problem with pygame 1.9.2 based on SDL 1.2.15: the only mean of resizing window programmatically I see is pygame.display.set_mode(...). But when I call this method my keyboard state is reset, so all currently pressed keys and modifiers send KEYUP event, repeated keys are stopped too.
When the window is resized manually by user keyboard state is perfectly normal. The event queue is paused for the time of resizing, then VIDEORESIZE and VIDEOEXPOSE events come up and keys are still pressed, if they were so.
There are few questions:
Is there a way to keep normal keyboard state while resizing window?
If not, I would like to at least keep modifier keys but I can not just use key.set_mods() because I am unable to get the moment when user releases key.
At last is there a way to invoke resizing of SDL window directly?
Example program demonstrates undesired behaviour. Left and right arrows resize window to same size, yet if they are pressed, there is only one keypress. Other keys are repeated normally if held. Output is event log for everything that happens.
import pygame
import sys
# same size all the time
size = (200, 200)
pygame.init()
pygame.display.set_mode(size, pygame.RESIZABLE)
# if left or right arrow key is held nothing will happen despite this line
pygame.key.set_repeat(500, 200)
run = True
while run:
for event in pygame.event.get():
out = '%s\t%s'%(event.type, str(event.dict))
if event.type == pygame.QUIT:
pygame.display.quit()
run = False
if event.type == pygame.KEYDOWN:
# mods are also reset on set_mode() call
out += ', mods = %i' % (pygame.key.get_mods())
if event.key == pygame.K_LEFT:
pygame.display.set_mode(size, pygame.RESIZABLE)
if event.key == pygame.K_RIGHT:
pygame.display.set_mode(size, pygame.RESIZABLE)
print(out)
pygame.time.Clock().tick(60)
Tested on Win machine but soon will report if linux fails too.
UPD: I've been digging into source of pygame and can not see why, but the documentation bundled with sources of pygame states, that Pygame can only have a single display active at any time. Creating a new one with pygame.display.set_mode() will close the previous display which implies that there is no way to resize window, only to recreate it. Seems a little strange to me.
UPD: I have changed my progam behaviour so that resize does not actually happen until user releases keyboard completely. It works well and does not break usability.
Nevertheless, it would be great to know if there exists a solution to stated problem.
One solution is, like self. has mentioned, to store the states of the keys so that instead of using only the events, you could ignore this particular key up event. This could, however, have one problem. If you are trying to create a key control to resize the screen, which you probably are doing, then you will also be ignoring the key up event that is real. Because this kind of control is rarely implemented, there does not seem to be any kind of clean solution, like a function to just change the screen, or one to suppress the false key events. However, you could find a way around the key event problem. There are really two viable solutions. The first one (and probably the worse of the two) is to create a way to identify the fake events, and selectively ignore the key up events. Probably the easiest way to do this is to resize the screen periodically during a fixed amount of time, so that you can expect a false keyboard event at this time, and ignore it. Here is an example of the code to block a resize every second (specifically, it assumes a screen update occurs every time the system time has an exact second value, no fractions of a second) for the a key:
import pygame
import datetime
a_keydown = False
for event in pygame.event.get():
if item.type == pygame.KEYDOWN and item.key == pygame.K_a:
a_keydown = True
print "keydown event"
if item.type == pygame.KEYUP and item.key == pygame.K_a:
if datetime.datetime.now().time()[4] > 15: #leaves a window of fifteen microseconds for the screen resize, this may need to be adjusted
a_keydown = False
print "key up event"
The better of the two solutions is not this, however. Assuming the keyup event created by set_mode exists soley in python, you could get key events from elsewhere, like pywin32 for example. Try using the win32api module, and something like the GetKeyState() function for what you need. This will give you the last known state of a key. Not quite as convenient as pythons event stream, but it may be your best solution, as pygame doesn't supply an elegant solution to this.

How can I have my program wait for input, without being in a loop

I'm writing a Pygame program that takes user input to write music. It waits for a specific event (ie keyboard or mouse input) and immediately analyzes the input without the user pressing enter with each entry.
while (running == 1):
for event in pygame.event.get():
# If event is a keypress
if(event.type == pygame.KEYDOWN):
key = pygame.key.get_pressed() # get_pressed returns an boolean array
# of every key showing pressed or not
# Find which key is pressed
for x in range(len(key)):
if(key[x] == 1):
break
# If a number key is pressed (0-9)
if(x >= 48 and x <=57):
# Set the octave to keypress
gui.setOctave(x-48) # gui is an instance of a class
# that controlls pygame display
The only way I know to do this is using the infinite while loop. This, however, takes up nearly all CPU power to run. Is there another efficient way to go about this without using the loop?
You could use event = pygame.event.wait() to wait for an event.

Simple UI to capture data

I know that this is a vague question, but I was hoping to get some help. I know VBA pretty well, and have been able to accomplish some simple tasks in python as well as the statistical programming language in R.
What I am looking to do is create a simple application that lets me capture data, some of which is captured from the keyboard. Every time there is a keystroke, I wanted to create a new record in my dataset.
For some context, think about creating a simple interface that lets me track the location (and duration) of the puck in an NHL hockey game.
I am not really a programmer, but know just enough to get in trouble and am not really sure where to get started. I simply am looking for some thoughts on a very basic (non-commercial) solution.
Many thanks in advance.
EDIT: I want to capture how long the puck is each zone. I plan on using the directional keys left/right to "follow" the puck from zone to each. Each time the puck changes to a zone, I want to "close" the active record and start a new one. The start and end times will let me calculate how long the puck was in the zone. I also need a way to stop the creation of a new record for things like faceoffs, tv time outs, and end of period. I was planning on using the spacebar. My thought is that if I do this correctly, when I follow along, the times recorded should match up with what is posted on the game clock found on tv. Yes, this is a crazy idea.
If you choose to program in Python:
You could use the pygame package to easily capture keyboard events. The library was built to write games, but would probably give you the functionality that you are looking for with keydown/keyup events. It also handles mouse events and (since it is intended for games) has the ability to do graphics/text. The documentation is really good and it is cross platform. A possible downside is that you have to have a "screen" and it has to have focus. Here is a small example:
import pygame
def main():
"""
Pygame Example
"""
pygame.init()
screen = pygame.display.set_mode((200, 200))
app_running = True
while app_running:
# Get all key/mouse events from system.
events = pygame.event.get()
# Loop thru each event...
for e in events:
# Handle when the program is killed.
if e.type == pygame.QUIT:
app_running = False
break
# Handle key events.
elif e.type == pygame.KEYDOWN:
# Exit if escape is pressed.
if e.key == pygame.K_ESCAPE:
app_running = False
# Do something when the right arrow
# is pressed.
elif e.key == pygame.K_RIGHT:
print "right arrow pressed"
# Do something when the left arrow
# is pressed.
elif e.key == pygame.K_LEFT:
print "left arrow pressed"
# and so on ...
# Fill the screen to blank it.
#screen.fill(mycolor)
# Write someting to the screen to display.
#screen.blit(some_image, some_position)
# Flip to display.
#screen.flip()
pygame.quit()
if __name__ == '__main__':
main()
If you are using a version of Windows you could use the msvcrt library but the event handling is not as nice as pygame: instead of events, you have to deal with raw keyboard output and it is a little less intuitive. Here is a small code snippet from Robert Gillies on ActiveState:
import msvcrt
def funkeypress():
"""
Waits for the user to press any key including function keys. Returns
the ascii code for the key or the scancode for the function key.
"""
while 1:
if msvcrt.kbhit(): # Key pressed?
a = ord(msvcrt.getch()) # get first byte of keyscan code
if a == 0 or a == 224: # is it a function key?
b = ord(msvcrt.getch()) # get next byte of key scan code
x = a + (b*256) # cook it.
return x # return cooked scancode
else:
return a # else return ascii code
Look at scan() for keyboard input in R. And you didn't ask about mouse input, but consider locator() for that.
Put it a loop if you want the output immediately.
Is it necessary that you program it yourself? There is a free program called jwatcher Designed for scoring animal behavior in ethological studies. It seems like that would be well-suited to your task.

How do I check if more than one key is being pushed at a time?

I am creating a keylogger to monitor my PC and I want to have a key combination that shuts the keylogger off or turns on the user interface, not sure yet. But the problem is that I can't figure out how to check if two or three buttons are being pressed at the same time ? How do I do that ?
Here's my source so far :
http://paste.pocoo.org/show/232233/
You should be able to get a keyup/keydown event, rather than a keypress event.
Then all you do is keep a list of buttons that are down, and remove the button when keyup is called.
Use GetKeyState to see if another key is pressed. Try this to quit when ctrl-shift-q is pressed:
import win32con
def OnKeyboardEvent(event):
if event.Ascii == 81 and
win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000 and
win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000:
LogFile.close()
exit()
LogFile.write(str(event.Key))
return True

Categories