Simple UI to capture data - python

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.

Related

How to move from one screen to another, with any input from the user, in pygame? [duplicate]

I'm making a little game and I want to make another window separately from my main one.
I have the the main game in a main window, and I want to open a new window and do a little animation when the user does something.
In my example code below, when the user presses "a" I want it to open a new window and blit to there.
Here I set up the two windows: (I know this doesnt work, its what I'm asking how to do)
SCREEN_X = 400
SCREEN_Y = 400
BSCREEN_X = 240
BSCREEN_Y = 160
BATTLE_SCENE = pygame.display.set_mode((BSCREEN_X, BSCREEN_Y))
SCREEN = pygame.display.set_mode((SCREEN_X, SCREEN_Y))
and then the program:
def run_ani ():
#Do animation, blitting to BATTLE_SCENE
return
def main_game():
ending=False
while ending==False:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT: ending=True
if event.type == KEYDOWN: # key down or up?
if event.key == K_ESCAPE:
ending=True # Time to leave
print("Stopped Early by user")
elif event.key == K_a:
run_ani()
#Normal screen motion, blitting to SCREEN
if ending: pygame.quit()
return
So far what this does is draws the main screen, then when A is pressed, it stops drawing the main screen animations, but still draws the other animations on the main screen and draws in the top left corner.
I'm pretty sure it does this because I am setting BATTLE_SCENE to be smaller than the main screen, thus when blitting to BATTLE_SCENE it blits to the area I created (240x160) in the top corner of the main screen.
However I want BATTLE_SCENE to be a seperate window, so that when I press 'a' it will pop up, do its thing, then close or at least go behind the main screen.
How to do this? Is it even possible?
Do you really need multiple windows? I mean, do you really need them?
If yes, then you should probably use pyglet/cocos2d instead.
To have multiple windows in pygame, you need multiple processes (one for each window). While this is doable, it's not worth the efford. You'll need IPC to exchange data between the windows, and I guess your code will become error-prone and ugly.
Go with pyglet when you need more than one window.
The better solution is probably to divide your game into scenes. Create multiple scenes so that each one represent one stage of the game, something like MenuScene, MainScene, BattleScene, GameOverScene, OptionScene etc.
Then let each of those scenes handle input/drawing of that very part of the game.
MenuScene handles drawing and input etc. of the game's menu
MainScene handles drawing and input etc. of the running game
BattleScene handles drawing and input etc. of whatever you do in run_ani
In your mainloop, just pass control over to the current scene by implementing the methods draw(), handle_event(), and update().
Some example code to get the idea:
scenes = {'Main': MainScene(),
'Battle': BattleScene()} #etc
scene = scenes['Main']
class MainScene():
...
def handle_event(self, event):
if event.type == KEYUP:
if event.key == K_a:
scene = scenes['Battle']
...
class BattleScene():
...
def draw(self):
# draw your animation
def update(self):
# if animation is over:
scene = scenes['Main']
...
def main_game():
ending=False
While Not ending:
clock.tick(30)
for event in pygame.event.get():
scene.handle_event(event)
scene.update()
scene.draw()
This is an easy way to cleanly seperate the game logic and allow context switching.
======================================Edit=========================================
Actually it won't work. Apperantly pygame only supports one display screen, and when you initialize another, it will close the first. You will stay with two varibles, which in fact are the same surface. You can have instead the game increasing the window size and playing the battle scene on the side of it, to do this, you can call the pygame.display.set_mode() again with different values. The varible which references the display screen will still be usable, as it change its reference to the new one. After the scene is over you can decrease the window back the same way.
==================================================================================
What basically happens is you run a loop, and each iteration of it is rendering and displaying a new frame.
When you call a function inside a loop, it doesn't continue to run until you finish running the function.
One way to solve this problen is just keep calling the function that updates the battle scene in the main loop.
Another way is by using threading. Threading is basically running multiple scripts ("Threads") in the same time.
Luckily, python already implemented this for us with the threading module.
It's too long for me to explain the module here, but you can learn it here. It might be a little complex if you haven't use threads before, but after some time it will be easier.
And If you want to learn more about threading you can go here.
Specificly here, you can have two threads, one for each loop/window, and run them in the same time.
Hope I helped you!
Yes, that is possible. SDL2 is able to open multiple windows. In the example folder you can take a look at "video.py".
https://github.com/pygame/pygame/blob/main/examples/video.py
"This example requires pygame 2 and SDL2. _sdl2 is experimental and will change."

How can `sdl2.SDL_GetKeyboardState` be used correctly?

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

How to create automated Tetris bot in Python?

So I've written a tetris bot in python, and it uses the pygame events to respond to keyboard input. Now I'm trying to create an AI to play this game. So I want to basically determine what the best move is, given a piece, and a board. I want to iterate over every possible move, and evaluate the board state (I have a method to evaluate how good a given board is), and pick the move that creates the best board state. My current main method is below.
def main():
pygame.init()
pygame.mixer.music.load("music.ogg")
#pygame.mixer.music.play(-1)
pygame.key.set_repeat(200,100)
game_state = state.GameState()
while True:
game_state.apply_gravity()
game_state.check_collision(place_if_collision=True)
game_state.time += 1
if game_state.hardDrop:
continue
game_state.check_for_full_rows()
game_state.print_state()
pygame.display.flip()
for event in pygame.event.get():
if event.type == QUIT:
terminate()
elif event.type==KEYDOWN:
if event.key==K_ESCAPE:
terminate()
if event.key==K_LEFT:
game_state.save_state()
game_state.piece_x -= 1
game_state.check_collision()
if event.key==K_RIGHT:
game_state.save_state()
game_state.piece_x += 1
game_state.check_collision()
if event.key==K_DOWN:
game_state.save_state()
game_state.piece_y += 1
game_state.check_collision(place_if_collision=True)
if event.key==K_UP:
game_state.save_state()
game_state.curr_piece.rotate_cw()
game_state.check_collision()
game_state.print_state()
if event.key==K_SPACE:
game_state.hardDrop = True
How do I figure out what a state would look like without actually modifying the state/rewriting a bunch of code? I can provide more code as needed. The purpose of this is so that I may use a genetic algorithm to train a neural network, to play tetris.
Very interesting and unique problem, couldn't you just create an independent copy and run your tests on that copy and delete it once you are done.
from copy import deepcopy
#some other code...
temp_state = deepcopy(original_state)
You then run your tests on temp_state and once you are done using it:
del temp_state
As for your second problem, you could make the bot analyze a piece's placement once it have reached 2 blocks down or whatever to solve for your problem. Or, you could have an unseeable few extra lines at the top (beyond the screen) that the player cannot see but the bot can use for making decisions.
Furthermore, and I'm sure you have already done this, you can use itertools to create the list of strings such as lllllus,llllluus (quoting your comment). In specific, try itertools.product and itertools.combinations.

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.

Adding an extra loop causes unresponsiveness

I'm making a simple game using Pygame where you have to get (by moving the cursor over them) all the circles that will appear on the screen (more will appear every second). The code is quite long so I made an example code. This code works fine, the Pygame window doesn't become unresponsive at all:
import pygame, random, sys
pygame.init()
window=pygame.display.set_mode((480,360))
end_program=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
pass
pygame.quit()
sys.exit()
However, in my game, in order to give the user the choice to play again, I need to wrap everything inside end_program in another loop. In the example shown, this is break_from_second_loop:
import pygame, random, sys
pygame.init()
window=pygame.display.set_mode((480,360))
end_program=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
break_from_second_loop=False
while not break_from_second_loop:
pass
pygame.quit()
sys.exit()
Now if this is run, the window becomes unresponsive! Anyone know why something as simple as wrapping the code in another loop (without altering the code at all) does this?
The problem is that the game can't respond, or do anything at all, if you're not running the event loop. And in the other loop, you're not running the event loop.
This is a general problem with event-loop-based programming. You can't do anything that takes a long time, and you can't do anything that has to run across multiple events.
So, you have to break your loop apart into steps, and do just one step (or a few of them) each time through the event loop.
In this particular case, it's actually pretty simple: just change that while to an if (and move the has_got_all_circles=False outside the main loop), and your logic now runs exactly once each time through the event loop.
Alternatively, change it to an if and also move it inside the for, so now it runs exactly once per event, instead of once per event loop iteration.
A third alternative is to factor out the whole thing into a function and set it as an idle or timer function, that runs whenever the event loop is idle, or once every frame, or once every 20ms, or whatever.
It's hard to know which of the three is appropriate in your case, but the basic idea is the same in all of them, so I'll just show the second one:
end_program=False
break_from_second_loop=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
if not break_from_second_loop:
pass
This blog post explains the general problem in more detail—although most of it isn't really appropriate to this specific problem.
The issue you're having is that you're not nesting your event loop code within the while loop that does your game logic. Here's the general structure of what you want:
while not end_program:
while not end_game:
handle_events()
do_one_frame_of_game_logic()
offer_another_game()
It's possible that offer_another_game will also need to be run in its own loop, with its own event handling code.
Indeed, you might want to encapsulate the logic you want to use into a state machine system. You'd have states like PlayingGame, GameOver, and DoYouWantToPlayAgain, each of which would run for a while, then hand off to another state. Your main loop would then be something like:
state = StartState()
while state:
state.handle_events()
state.update()
state.draw()
state = state.next_state() # most of the time, the state will return itself

Categories