Making button function run once - python-3.x

I can't work out to make the button function run once when clicked. Whenever the mouse button is down it runs through the function loop until released. I only want to run through the loop once per click. Here is the code:
def Button(msg, x, y, w, h, ic, ac, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(window, ac, (x, y, w, h))
#if event.type == pygame.MOUSEBUTTONDOWN:
if click[0] == 1 and action != None:
pygame.draw.rect(window, lightgrey, (x, y, w, h))
if action == "undo":
print("hey")
if action == "reset":
for row in range(6):
for column in range(7):
board[row][column] = 0
elif action == "quit":
pygame.quit()
quit()
else:
pygame.draw.rect(window, ic, (x, y, w, h))
print("hey") prints roughly 3 times if you click as fast as you can as it is running through the loop repetitively.

pygame.mouse.get_pressed() will return True for as long as the mouse is held down. Assuming this checks every frame, it's going to return 1 or True for multiple frames, thus causing your print statement to occur multiple times.
if event.type == pygame.MOUSEBUTTONDOWN:
This line you commented out is the one you should be using. It only activates when the mouse is actually clicked, but not while it is being held down. Note that for this to work, you must have a loop like for event in pygame.event.get() before this line.
So what you basically need to do is remove click = pygame.mouse.get_pressed() and replace if click[0] == 1 with the line you commented out. Hopefully this works :)

Related

While true loops giving me recursion errors

I'm creating a game with pygame for my CompSci course. The game is working well, but now I'm trying to make a restart function and I'm getting alot of errors.
The basic outline of what I'm trying to do is:
first = True
def check(count):
if count == 1000:
return True
else:
return False
def start():
# MAIN MENU
print ("start")
count = 0
while True:
count += 1
if check(count) == True:
Game()
def Game():
count = 0
print("Game")
while True:
# GAMELOOP
count += 1
if check(count) == True:
restart()
def restart():
count = 0
print ("Restart")
while True:
# RESTART
count += 1
if check(count) == True:
start()
if first == True:
first = False
start()
However, whenever I run this, python ultimately crashes with:
RecursionError: maximum recursion depth exceeded while calling a Python object
I'm not sure about how to get around this. I was hoping that someone could suggest another method to do the same thing or something that I've done wrong.
The actual restart and menu functions:
def rest(): #Main Menu
while True:
pygame.display.update()
menu.draw()
if pygame.mouse.get_pressed()[0]:
x, y = pygame.mouse.get_pos()
if x >= 255 and x <= 355 and y >= 400 and y <= 450:
game()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def game(): #Gameloop
while True:
if bottom.time == 0 or bottom.time < 0 :
game_screen.fill((0, 0, 0))
restart()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def restart():
while True:
pygame.display.update()
menu.restart()
if pygame.mouse.get_pressed()[0]:
x, y = pygame.mouse.get_pos()
if x >= 255 and x <= 355 and y >= 400 and y <= 450:
game_screen.fill((0, 0, 0))
rest()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
Do you have a sequence of functions calling each other creating a indirect recursion.
start() counts to 1000 and then calls Game(), which counts to 1000 again and then calls restart(), whichs also counts to 1000 an then calls start(), closing the recursion loop. Once you do not have a stop condition, your recursion goes until reach the depth limit of python's recursion stack, then you get the exception.
As solution, you need to create a stop condition inside of one those functions.
Edit
I don't know pygame exactly but, as far as I get from the additional code, to the for in rest() function (which checks the events) be executed, it needs that the game() function returns, which are never happening. You can test this hyphotesis printing something just in the line before the for line. Same applies to the restart function.

Pygame - keyboard input bug - multiple times print on screen

I'm trying to display the keyboard input on the screen using python3 but here's some issues with the code.
If I type rapidly, the letters get repeated. For example: If I type in zo quickly, the screen output is zozz but if I type slowly, it outputs zo
The shift key isn't working as expected. For example: If I press Left (or right) shift and z, it outputs Z but after I release Left shift and type z, it still outputs Z and the next time when I press z it outputs z. I don't know why this happening because I reset the shift flag at the end to 0
The capslock key isn't working as expected either. Once I press capslock, the output is all uppercase but when I press it again, the output does go into lowercase, it remains uppercase. I know here that the capsLock flag isn't working but I'm not able to debug it.
Any ideas on how to get past these issues?
import pygame
from pygame.locals import *
pygame.init()
def getch_mod():
inputStr = ''
shift = 0
capsLock = 0
a = 30
screen = pygame.display.set_mode((1000, 1000))
pygame.display.set_caption("Key Press Test")
f1=pygame.font.SysFont("comicsansms",24)
while True:
events = list(pygame.event.get())
for ev in events:
screen.fill((255,255,255))
text = f1.render('Enter password:', True, (0,0,0))
screen.blit(text,(10,10))
text = f1.render(inputStr, True, (0,0,0))
screen.blit(text, (100,a))
if (ev.type == pygame.QUIT):
return
if pygame.key.get_focused():
press = pygame.key.get_pressed()
for i in range(0,len(press)):
if (press[i] == 1):
name = pygame.key.name(i)
if (name == 'left shift' or name == 'right shift'):
shift = 1
break
if (name == 'caps lock'):
if (not capsLock):
capsLock = 1
else:
capsLock = 0
break
if (name != 'return' and name != 'left shift' and name != 'right shift' and name != 'caps lock'):
if (shift or capsLock):
name = name.upper()
inputStr += str(name)
shift = 0
pygame.display.update()
c = getch_mod()
Your code is very complex for what you're trying to do.
All you have to do is iterate over the event list and add the characters unicode representation to the string. Avoid calling pygame.key.get_pressed() since it doesn't take the order of the key press into account.
Also, don't have your logic in the event loop unless it's acting on a single event. Currently you're blitting the text for every event that happen, instead of for every frame.
Having pygame.key.get_focused in the event loop is unnecessary, since if a key event has been put in the event loop then the window have to have user focus.
Here's a short example on how to blit key input:
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 720))
input_str = ''
comic_sans_font = pygame.font.SysFont("comicsansms", 24)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
input_str += event.unicode
text = comic_sans_font.render(input_str, True, (0, 0, 0))
screen.fill((255, 255, 255))
screen.blit(text, (100, 200))
pygame.display.update()

How do i stop a timer when i set a game to paused

I have created this game which gives you a time limit to kill as many as the targets as you can. Below is the part of the code which pauses and unpauses the game. The problem I am having is that when I pause the game the timer which sets the time limit is still counting. How do I get this to stop?
paused = False
def button(msg, msg_two, x, y, w, h, i, a, fontsize, action=None):
global paused
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if (x < mouse[0] < (x+450)) and (y < mouse[1]<(y+100)):
pygame.draw.rect(gameDisplay, a, [x, y, w, h])
largeText = pygame.font.Font('freesansbold.ttf',fontsize)
TextSurf, TextRect = text_objects(msg, largeText, green)
TextRect.center = ((x+(w/2)),(y+(h/2)))
gameDisplay.blit(TextSurf, TextRect)
if click[0] == 1 and action != None:
if action == "play":
gameloop()
elif action == "quit":
game_quit()
elif action == "pause":
paused = True
pause()
elif action == "unpause":
unpause()
else:
pygame.draw.rect(gameDisplay, i, [x, y, w, h])
largeText = pygame.font.Font('freesansbold.ttf',fontsize)
TextSurf, TextRect = text_objects(msg_two, largeText, green)
TextRect.center = ((x+(w/2)),(y+(h/2)))
gameDisplay.blit(TextSurf, TextRect)
def game_quit():
pygame.quit()
quit()
def unpause():
global paused
paused = False
def pause():
pygame.mouse.set_visible(1)
button_x = (display_width/4)-150
button_y = display_height/1.5
button_width = 450
button_height = 100
while paused:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.Font('freesansbold.ttf',72)
TextSurf, TextRect = text_objects('paused', largeText, red)
TextRect.center = ((display_width/2),(display_height/3))
gameDisplay.blit(TextSurf, TextRect)
mouse = pygame.mouse.get_pos()
button("let's go", "carry on", button_x, button_y, button_width, button_height, blue, light_blue, 70, "unpause")
button("Bye then :(", "Leaving?", button_x+600, button_y, button_width, button_height, red, light_red, 70, "quit")
pygame.display.update()
clock.tick(15)
def gameloop():
gameExit = False
start = time.time()
elapsed = 0
while not gameExit and elapsed < 30:
button("Stop", "Stop", 1550, 0, 50, 50, red, light_red, 15, "pause")
elapsed = time.time() - start - (enemy_kills/2)
gameloop()
def game_intro():
pygame.mouse.set_visible(1)
button_x = (display_width/4)-150
button_y = display_height/1.5
button_width = 450
button_height = 100
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.Font('freesansbold.ttf',72)
TextSurf, TextRect = text_objects('Shoot Hitler and not trump', largeText, red)
TextRect.center = ((display_width/2),(display_height/3))
gameDisplay.blit(TextSurf, TextRect)
mouse = pygame.mouse.get_pos()
button("let's go", "wanna play?", button_x, button_y, button_width, button_height, blue, light_blue, 70, "play")
button("Bye then :(", "wanna quit?", button_x+600, button_y, button_width, button_height, red, light_red, 70, "quit")
pygame.display.update()
clock.tick(15)
game_intro()
I apologize if I have missed out important parts of the code. Inform me if I have
There are other strange things in this program that makes it hard to understand it enough to give you a proper answer.
For example, the fact that you call back gameloop() at the end of the gameloop function - it creates a recursive call that should no t be recursive at all.
Anyway, what happens is that you should move most of these functions inside a class - so that some of the state variables like paused can be an attribute of that class. Instances of the class are meant to be game plays.
In that case, your "start" variable would be made an attribute as well, so that it could be modified from within the unpause method.
You then could count the amount of paused time, and add that amount to the "start" tick count .
Or you can just make start a global variable and go on with your current pattern. Since I am not re-organizng all of your code just to answer the question, keeping the things more or less as they are would require you to do:
def gameloop():
global start
...
def pause():
global pause_start
pause_start = time.time()
...
def unpause():
global start, pause_start
pause_duration = time.time() - pause_start
start += pause_duration
...

Exiting If statement loop python

For some reason I cannot exit this loop when I change to intro = false, can anyone help as to how I'd exit this if statement. It is my menu screen, and once I click 'new game' I want it to exit the game_intro function.
This is where I define game_intro:
def game_intro():
intro = True
if intro == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
window.fill(superlightgrey)
fontvertical = pygame.font.SysFont("comicsansms", 100)
text = fontvertical.render("Connect 4", True, skyblue)
word = (10, 1)
window.blit(text,word)
ButtonIntro("New Game", 340, 140, 200, 50, superlightgrey, superlightgrey, "Play")
This is where I created the button function:
def ButtonIntro(msg, x, y, w, h, ic, ac, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(window, ac, (x, y, w, h))
if click[0] == 1 and action != None:
pygame.draw.rect(window, lightgrey, (x, y, w, h))
if action == "Play":
intro = False
##WHAT DO I NEED HERE TO EXIT LOOP
and this is where I call on the function:
while intro == True:
game_intro()
print("loopexited")
The intro variable is inside a function, when you create a variable inside a function it's not connected to anything outside the function.
intro = True
def myfunction():
intro = False
myfunction()
print(intro)
This code prints:
True
The intro variable inside the myfunction is created as an entirely separate variable from the one outside.
It looks like you might have another separate intro variable inside the game_intro function too.
You can get around this using the global keyword, but you might be better off trying to find a different way to structure your code (global is considered a bad practice.)

How can I add a delay to a mouse click in pygame?

I'm attempting to make a store in my game, but am having issues with pygame.mouse.get_pressed(). When the user clicks on a button, the program thinks that they clicked on it more than once, therefore using more credits than the user intended. I would like to add a delay so that the game doesn't do this. I would like 1 click to represent them buying one object. I have tried reducing the framerate for that window, but the result is the same. Here is the code I currently have.
This is where all the mouse action occurs.
def button(x, y, w, h, ic, ac, action = None):
global paused
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + w > mouse[0] > x and y + h > mouse[1] > y:
pygame.draw.rect(gameDisplay, ac, (x, y, w, h))
if click[0] == 1 and action == Game:
Game()
if click[0] == 1 and action == quitgame:
sys.exit()
if click[0] == 1 and action == None:
paused = False
if click[0] == 1 and action == StartScreen:
save()
StartScreen()
if click[0] == 1 and action == LootScreen:
LootScreen()
if click[0] == 1 and action == open_common_drop:
open_common_drop()
if click[0] == 1 and action == open_rare_drop:
open_rare_drop()
else:
pygame.draw.rect(gameDisplay, ic, (x, y, w, h))
This is where the loot store is currently at.
def LootScreen():
global current_drops
loot = True
while loot:
for event in pygame.event.get():
if event.type == pygame.QUIT:
save()
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_t:
open_common_drop()
elif event.key == pygame.K_y:
open_rare_drop()
if event.key == pygame.K_ESCAPE:
StartScreen()
gameDisplay.fill(gray)
title('Loot Chests!')
button(400, 150, 260, 50, blue, bright_blue, open_common_drop)
button(695, 150, 260, 50, red, bright_red, open_rare_drop)
button(display_width * 0.42, display_height / 1.15, 255, 50, red, bright_red, StartScreen)
game_display_text('Open Common Chest (T)', 407, 165)
game_display_text('Open Rare Chest (Y)', 725, 165)
game_display_text('You Got: %s' % current_drops, 50, display_height / 2)
game_display_text('Credits: %.2f' % player_loot_data['credit_count'], 15, 15)
game_display_text('Main Menu', display_width * 0.47, display_height / 1.13)
game_display_text('Janus\': %s' % player_loot_data['loot_data_one'] , 950, 500)
game_display_text('Peace of Minds: %s' % player_loot_data['loot_data_two'], 950, 535)
pygame.display.update()
clock.tick(30)
You will need to use a bool switch in your button function. Here I re-worked the function in a way that should work.
def button(x, y, w, h, ic, ac, action = None, held):
global paused
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + w > mouse[0] > x and y + h > mouse[1] > y:
pygame.draw.rect(gameDisplay, ac, (x, y, w, h))
if click[0] == 1:
if held == False:
if action == Game:
Game()
elif action == quitgame:
sys.exit()
elif action == None:
paused = False
elif action == StartScreen:
save()
StartScreen()
elif action == LootScreen:
LootScreen()
elif action == open_common_drop:
open_common_drop()
elif action == open_rare_drop:
open_rare_drop()
held = True
else:
held = False
else:
pygame.draw.rect(gameDisplay, ic, (x, y, w, h))
return held
As you see, there is a new variable held added to the function to process whether the button is held or not. Held is taken and returned here, so it won't reset every time the function is called.
With that down, let me show you why I wrote it that way, and just how this logic works at its core.
import pygame
#
display = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
#
held = False # Variable to handle if mouse button is held
#
RUNNING = True
while RUNNING:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
#
button = pygame.mouse.get_pressed() # Get mouse state
if button[0]: # Check if left mouse button is pressed
if held == False: # Check if button is held down
print(True) # If button is not held down, print true
held = True # Set held eqaual to true for next iteration
else: # If left mouse button is not pressed
held = False # held is set to false, alowing event to happen again
#
display.fill((0,0,0))
#
pygame.display.flip()
#
pygame.quit()
The above is a very simple stand alone program that also implements the held variable to monitor if the mouse button is held. In this program, the variable held is first declared as False as the mouse is not being pressed. Then, within the main loop, pygame.mouse.get_pressed() is called to get the mouse input, and is immediately followed by a check for the left mouse button. If the left mouse button is pressed, a check for held will happen. If it is false, the program will print True. held is then set to True for the next iteration. The else: statement will fire if the left mouse button is not held down and reset held to its default False state.
I would suggest a timed bool switch. Here's an example that should help out.
import time
boolswitch = False
timer = 0.0
clock = time.time()
x = time
while True:
if timer > 0.25:
boolswitch = True
timer = 0.0
else:
x = time.time()
timer += (x-clock)
clock = x
if boolswitch:
click = pygame.mouse.get_pressed()
if click[0] == 1:
boolswitch = False
clock = time.time()

Resources