pygame selection menu navigation using arrow keys and return - python

I am trying to make it so I can generate a group of options to select from and using the up and down arrows the user can navigate between them the current option being highlighted and if you press enter it calls the function that option contains.
Excuse the mess, I had it cleaner, but was trying several things to try to get the intended result.
The selection function works fine by itself
def selection(text, selected_status, x, y, function):
if selected_status:
fill_rect(black, x, y, display_width - x, font_size + 2)
fill_rect( settings[3], x, y, display_width - x, font_size +2)
message_to_screen("["+ text + "]", settings[3], black, x, y)
if event_handler() == pygame.K_RETURN:
function()
return True
elif not selected_status:
fill_rect(black, x, y, display_width - x, font_size + 2)
message_to_screen("["+ text + "]", black, settings[3], x, y)
return False
The selection handler is the issue. it seems I have to hit the arrow keys at certain times in order for them to work. the event_handler just grabs the currently pressed key and returns it. overall it's just a broken mess.
def selection_handler(selection_param_array, x, y):
text_array = selection_param_array[0]
selected_status_array = selection_param_array[1]
function_array = selection_param_array[2]
index = 0
chosen = False
original_y = y
while not chosen:
y = original_y
for i in range(len(selected_status_array)):
if selected_status_array[i] == selected_status_array[index]:
selected_status_array[i] = True
else:
selected_status_array[i] = False
selection(text_array[i], selected_status_array[i], x, y, function_array[i])
y += font_size
if event_handler() == pygame.K_UP and index > 0:
index -= 1
elif event_handler() == pygame.K_DOWN and index < len(selection_param_array):
index += 1
elif event_handler() == pygame.K_RETURN:
chosen = True
function_array[index]()
pygame.display.update()

Instead of just checking whether a key is pressed down right now, you need to use pygame.event.get() to obtain all events that have occurred since the last pygame.event.get() call. The usual syntax is this:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
index += 1
elif event.key == pygame.K_DOWN:
index -= 1
elif event.key == pygame.K_RETURN:
chosen = True
function_array[index]()
break
index %= len(selection_param_array)
Also, if you call from pygame.locals import * at the start of your program, you can omit pygame. from all of the keys and event types, so you get more readable code:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_UP:
index += 1
elif event.key == K_DOWN:
index -= 1
elif event.key == K_RETURN:
chosen = True
function_array[index]()
break
index %= len(selection_param_array)
This replaces your if event_handler() == pygame.K_UP and index > 0: ... function_array[index]() code.
Note that the code above will loop from the top to the bottom of the list when you are selecting things, I don't know if you want this. You can stop it from doing this by reintroducing your index comparisons on the event.key == lines and removing index %= len(selection_param_array).

Related

Created a sudoku on pygame, stuck with creating a function that allows player to insert value into selected grid [duplicate]

I want to insert values on the sudoku grid, using pygame, editing an internal matrix. If the user clicks an empty cell, I want them to be able to choose a keyboard number and the internal matrix would be updated, with this number in the respective cell. For now, my loop code looks like this:
while custom:
pygame.display.flip()
screen.blit(bgCustom, (0, 0))
for event in pygame.event.get():
if (event.type == pygame.MOUSEBUTTONDOWN):
setGrid(board)
and setGrid looks like this:
def setGrid(board):
position = pygame.mouse.get_pos()
x = position[0]
y = position[1]
#print(y, x)
line = x // 92
col = y // 80
#print(col, line)
#print(board)
for event in pygame.event.get():
if event.type == pygame.KEYUP :
if event.key == pygame.K_1:
board[line][col] = 1
print(board)
elif event.key == pygame.K_2:
board[line][col] = 2
elif event.key == pygame.K_3:
board[line][col] = 3
elif event.key == pygame.K_4:
board[line][col] = 4
elif event.key == pygame.K_5:
board[line][col] = 5
elif event.key == pygame.K_6:
board[line][col] = 6
elif event.key == pygame.K_7:
board[line][col] = 7
elif event.key == pygame.K_8:
board[line][col] = 8
elif event.key == pygame.K_9:
board[line][col] = 9
There is no syntax error, but the board remains uneditaded. My guess is that, when the user activates setGrid, the computer immediately tries to detect keyboard input, but the user is not "fast enough" to make the function work. I thought about making some kind of wait function, to wait for keyboard input, but I don't want the user to get stuck in setGrid. Any thoughts?
Thanks in advance
You have to set a variable (clicked_cell) which is initialized by None. Assign a tuple with the line and column to the cell when it is clicked. Reset the variable after the button was pressed:
clicked_cell = None
while custom:
# [...]
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
line = event.pos[0] // 92
col = event.pos[1] // 80
clicked_cell = (line, col)
if event.type == pygame.KEYUP:
if clicked_cell != None:
if pygame.K_1 <= event.key <= pygame.K_9:
line, col = clicked_cell
clicked_cell = None
number = int(event.unicode)
board[line][col] = number
# [...]

Events in pygame do not work

I'm using python 3.4.3.
Having some trouble with pygame events.
After pressing mentioned keys (Up, Down, Left or Right), 99% of the time, program does not react. Spamming one (or multiple) keys seems to be the only way to remedy this, but even that is unreliable.
Code:
import pygame
pygame.init()
size = width, height = 1024, 768
screen = pygame.display.set_mode(size,pygame.FULLSCREEN)
g = pygame.image.load(
'X:\\Projects\\Rogue\\Game Project\\src\\img\\Tiles\grass.JPG')
player = pygame.image.load('untitled.PNG')
pygame.display.set_caption('WINDOW_NAME')
run = True
def resolve(x,y):#x=32 y=20
return (x*32, y*32)
def get_tiles():
tiles = []
for y in range(0,20):
for x in range(0,32):
tiles.append(resolve(x,y))
return tiles
p = [5,5]
def draw_player(p):
screen.blit(player, resolve(p[0],p[1]))
clock = pygame.time.Clock()
def move(direction):
if direction == 'U':
if p[1]-1 < 0:
print('fail')
else:
p[1] = p[1] - 1
elif direction == 'D':
if p[1] + 1 > 20:
print('fail')
else:
p[1] = p[1] + 1
elif direction == 'L':
if p[0] - 1 < 0:
print('fail')
else:
p[0] = p[0] - 1
elif direction == 'R':
if p[0] + 1 > 32:
print('fail')
else:
p[0] = p[0] + 1
def event_handler():
for event in pygame.event.get():
print('event:',event)
if event.type == pygame.KEYDOWN or event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
quit()
elif event.key == pygame.K_p:
print('fish')
elif event.key == pygame.K_UP:
move('U')
elif event.key == pygame.K_DOWN:
move('D')
elif event.key == pygame.K_LEFT:
move('L')
elif event.key == pygame.K_RIGHT:
move('R')
elif event.type == pygame.MOUSEBUTTONDOWN:
print('a')
print(pygame.display.get4_driver())
for item in pygame.display.list_modes():
print(item)
def render(sprites):
for item in sprites:
screen.blit(g,item)
from random import randint
while run:
#if len(pygame.event.get()) > 0:
print(p)
print(pygame.event.get())
event_handler()
render(get_tiles())
draw_player(p)
#screen.fill(255,255,255)#wipe to black
pygame.display.flip()#screen updates all areas that have been blited
clock.tick(30)#30 FPS for now
pygame.quit()
Also, pygame.quit() does nothing for me, hence the part where escape kills the program straight away.
Get rid of the print(pygame.event.get()). Get rid of the or in the event loop because you are just checking the same thing again so it wont do anything and the if statement to check the mouse button should be outside the if statement for checking for key-downs.

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

How do I make a game object move continuously as long as the key is pressed?

I am trying to make a game object (here, the welcome text) move as long as the key is pressed on the keyboard. But in this code of mine,
import pygame , sys
from pygame.locals import *
pygame.init()
WHITE = (255 , 255 , 255)
RED = (255 , 0 , 0)
DISPLAYSURF = pygame.display.set_mode((800 , 400))
pygame.display.set_caption('Welcome Tanks')
#render(text, antialias, color, background=None)
fontObj = pygame.font.SysFont('serif' , 40)
text = fontObj.render('Welcome Folks' , True , RED )
x = 150
y = 29
while True:
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(text ,(x , y))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN or event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit(0)
elif event.key == K_DOWN:
y += 15
elif event.key == K_UP:
y -= 15
elif event.key == K_RIGHT:
x += 14
elif event.key == K_LEFT:
x -= 15
else:
x = 150
y = 29
pygame.display.update()
The object moves only once, even though the key is continuously pressed for a long time. In other words, the object changes its position only once when the keyboard button is pressed. I want it to move continuously while I hold the key.
Which event should I look for instead of event.KEYDOWN?
I suggest you to use the key.get_pressed(), e.g.
while True:
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(text ,(x , y))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN or event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit(0)
if pygame.key.get_pressed()[K_LEFT]:
x -= 15
if pygame.key.get_pressed()[K_RIGHT]:
x += 14
if pygame.key.get_pressed()[K_UP]:
y -= 15
if pygame.key.get_pressed()[K_DOWN]:
y += 15
pygame.display.update()
pygame.key.get_pressed()
Returns a sequence of boolean values representing the state of every
key on the keyboard. Use the key constant values to index the array. A
True value means the that button is pressed.
Getting the list of pushed buttons with this function is not the
proper way to handle text entry from the user. You have no way to know
the order of keys pressed, and rapidly pushed keys can be completely
unnoticed between two calls to pygame.key.get_pressed(). There is also
no way to translate these pushed keys into a fully translated
character value. See the pygame.KEYDOWN events on the event queue for
this functionality.
Also you can take a look at doc
keyState = pygame.key.get_pressed()
Declare that, and then you use keystate:
if KeyState()[K_DOWN]:
y += 15
You could try:
if key.get_pressed()
down = True
while down = True:
y+= 15
Then get FPS clock so it does not just flow over the screen!

Character only maintains movement while mouse is moving on screen?

My while loops only maintains the movement for the sprite while the cursor is moving inside of the screen. I've tried reorganizing some of the screen.blits and display.update() and display.flip(). I can't seem to figure out why the character stops after a change in one pixel instead of continuing like it I intended.
background_image = 'Terrain_Grass_First.png'
import pygame, sys
from pygame.locals import *
pygame.init()
pygame.display.set_caption('Hans')
screen_width = 600
screen_height = 400
screen = pygame.display.set_mode((screen_width, screen_height),0,32)
pygame.mouse.set_visible(False)
sprite = pygame.image.load('Hans_front_still.png').convert_alpha()
x,y = (0,0)
movex, movey = (0,0)
while True:
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_w:
y = -1
elif event.key == K_a:
x = -1
elif event.key == K_s:
y = +1
elif event.key == K_d:
x = +1
elif event.type == KEYUP:
if event.key == K_w:
y = 0
elif event.key == K_a:
x = 0
elif event.key == K_s:
y = 0
elif event.key == K_d:
x = 0
movex += x
movey += y
screen.fill((0,0,0))
screen.blit(sprite,(movex,movey))
pygame.display.flip()
Your loop blocks on pygame.event.get.
You don't schedule any events of your own.
So, you do nothing until the OS has an event (mouse move, redraw, etc.) to give you.
And, even if you fixed that, you're calling movex += x once per event. So, when the OS is throwing a lot of events at you, your sprite will go zipping madly across the screen, but when the events are coming more slowly, it will crawl along. That's almost never what you want.
An easy fix for both problems is to just schedule your own events. For example, with pygame.time.set_timer(), you can make sure you get an event every, say, 250 milliseconds, and you can only move the sprite on those events. For example:
timer_event = pygame.USEREVENT + 1
pygame.time.set_timer(timer_event, 250)
while True:
for event in pygame.event.get():
# ...
elif event.type == timer_event:
movex += x
movey += y
Another alternative is to design your game with a fixed framerate.
A trivial example looks like this:
FRAME_TIME = 50 # 50ms = 20fps
next_frame_time = pygame.time.get_ticks() + FRAMES
while True:
while True:
event = pygame.event.poll()
if event.type == pygame.NOEVENT:
break
elif # ...
pygame.display.flip()
now = pygame.time.get_ticks()
if now < next_frame_time:
pygame.time.wait(next_frame_time - now)
next_frame_time += FRAMES
Now you can just move every 5th frame.
A realistic example has to deal with missed frames, too many events in the queue, choose between wait and delay appropriately, etc.
But I'd go with the event-driven version with a timer instead of a fixed-framerate version in most cases, especially if you're already going down that line.
The only problem is your Indentation!
The fifth and sixth lines from the bottom have wrong indentation, and you need to delete them.
These two lines:
movex += x
movey += y
should be:
movex += x
movey += y
And it works

Categories