How do I make graphics of this button better in pygame? - python-3.x

import pygame, sys, time
from pygame.locals import*
###################################
screen = "START"
pygame.init()
window_width = 1000
window_height = 650
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("LOGO QUIZ")
#############################################
def button(normal, activated, x_pos, y_pos, length, height, func=None):
button = pygame.image.load(normal)
mousex, mousey = pygame.mouse.get_pos()
if (mousex>x_pos)and(mousex<(length+x_pos))and(mousey>y_pos)and(mousey<(y_pos+height)):
button = pygame.image.load(activated)
for event in pygame.event.get():
if (event.type == pygame.MOUSEBUTTONDOWN )and (event.button == 1):
if (mousex>x_pos)and(mousex<(mousex+x_pos))and(mousey>y_pos)and(mousey<(y_pos+height)):
func()
window.blit(button,(x_pos, y_pos))
## button = pygame.image.load(activated)
window.blit(button,(x_pos, y_pos))
def START():
global screen
while screen != "QUIT":
for event in pygame.event.get():
if event.type == QUIT:
screen = "QUIT"
bg = pygame.image.load("START.png")
window.blit(bg,(0,0))
button("play_u.png", "play_a.png", 350, 200, 325, 75, game)
pygame.display.update()
def game():
global screen
while screen != "QUIT":
for event in pygame.event.get():
if event.type == QUIT:
screen = "QUIT"
window.fill((20, 20, 20))
pygame.display.update()
pygame.quit()
sys.exit()
START()
pygame.display.update()
The above code is for the start menu of my game, I made a START function to show the start screen and then the game function to run the main game later, in the button function I want to make it as when the mouse is hovered over the picture, it should change the picture(activated pic) but when I do this it takes lots of time it change , how do i shorten the time?

Your code does not work because you call pygame.event.get() multiple times per frame; once in START, and once in button.
Think about what happends when there's a MOUSEBUTTONDOWN event in the queue, then pygame.event.get() is called in START: The event gets removed from the queue, and when pygame.event.get() is called in button, the MOUSEBUTTONDOWN event is already gone and if (event.type == pygame.MOUSEBUTTONDOWN ) will never be True.
So instead of calling for event in pygame.event.get(): in START, you could store the result of pygame.event.get() in a variable and iterate over that in START and button. This way, you won't lose events.
Here's an example:
...
def button(events, normal, activated, x_pos, y_pos, length, height, func=None):
button = pygame.image.load(normal)
mousex, mousey = pygame.mouse.get_pos()
if (mousex>x_pos)and(mousex<(length+x_pos))and(mousey>y_pos)and(mousey<(y_pos+height)):
button = pygame.image.load(activated)
# iterate over the list if events instead of calling pygame.event.get
for event in events:
if (event.type == pygame.MOUSEBUTTONDOWN )and (event.button == 1):
if (mousex>x_pos)and(mousex<(mousex+x_pos))and(mousey>y_pos)and(mousey<(y_pos+height)):
func()
window.blit(button,(x_pos, y_pos))
...
def START():
global screen
while screen != "QUIT":
# store the list of events in a variable
events = pygame.event.get()
# iterate over the new list
for event in events:
if event.type == QUIT:
screen = "QUIT"
bg = pygame.image.load("START.png")
window.blit(bg,(0,0))
# pass the list of events to the button function
button(events, "play_u.png", "play_a.png", 350, 200, 325, 75, game)
pygame.display.update()

Related

Why won't my Pygame window background load?

Whenever I run my code, the window opens just fine but the background image will not load. Although the image won't load immediately, if I minimize the window and open it again, the image loads just fine.
This is my code:
import pygame
import os
pygame.init()
WIDTH, HEIGHT = 500, 365
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
BG = pygame.transform.scale(pygame.image.load(os.path.join("GameMap3.png")).convert_alpha(), (WIDTH, HEIGHT))
PLAYER1 = pygame.image.load(os.path.join("PlayerType1.gif"))
fps = 60
main_font = pygame.font.SysFont("comicsans", 50)
player_vel = 5
clock = pygame.time.Clock()
def Main():
global BG
while True:
WIN.blit(BG, (0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
Main()
You missed to update the display with either pygame.display.update() or pygame.display.flip()
def Main():
global BG
while True:
WIN.blit(BG, (0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
pygame.display.update() # <--- this is missing
Main()
The typical PyGame application loop has to:
handle the events with either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display with either pygame.display.update() or pygame.display.flip()
limit the frames per second to limit CPU usage with pygame.time.Clock.tick

Hi, I'm looking for a fix to having multiple buttons in Pygame

For context, I've just gotten into Pygame, and I'm trying to learn how to use buttons. Of course, there's making the classes or functions and likewise calling them to work. However, when I open a new screen, I'd like there to be a new button, which then only works there.
My issue comes in after the second scene, where I'm unsure how to call a second button, or a third etc. If I put a return in a function where my button is, and check whether that function has collided with a point, the whole screen becomes a button which isn't ideal. So I'm looking for a fix.
Here's the chunk causing issues, and I'd love any advice on optimising it. My guess is that HeroCreation()[1] is causing issues, but I'm not entirely sure how else to make multiple buttons work simultaenously.
def TextCreation(font, text, coords, screen):
label = font.render(text, True, BLACK)
labelRect = label.get_rect()
labelRect.topright = coords
window.blit(label, labelRect)
return(label, labelRect)
StartButton = TextCreation(largeFont, 'Start Game', (rect_centerx+140, rect_centery - 250), window)
QuitButton = TextCreation(largeFont, 'Quit Game', (rect_centerx+140, rect_centery + 150), window)
def HeroCreation():
window.fill(WHITE)
Greeting = TextCreation(largeFont, 'Welcome, Hero!', (rect_centerx+210, rect_centery-200), window)
Intro1 = TextCreation(paraFont, "I presume you're caught up, but just in case:", (rect_centerx+250, rect_centery + 50), window)
Intro2 = TextCreation(paraFont, "The Rat King's wrecking our kingdom, and you're the only one who can find the Orb of Power.", (rect_centerx+500, rect_centery + 100), window)
Intro3 = TextCreation(paraFont, "Deal with him once and for all! Do us proud, hero.",(rect_centerx+260, rect_centery + 150), window)
pygame.draw.rect(window, LIGHT_GREEN, (rect_centerx - 170, rect_centery-80, 400, 70), 0)
ProceedtoMapButton = TextCreation(paraFont, "Are you ready?", (rect_centerx+110, rect_centery-70), window)
return (ProceedtoMapButton)
pygame.display.flip()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
mouse = pygame.mouse.get_pos()
if QuitButton[1].collidepoint(mouse):
pygame.quit()
sys.exit()
if StartButton[1].collidepoint(mouse):
HeroCreation()
if HeroCreation()[1].collidepoint(mouse):
drawGrid(width, height, window)
if event.type == QUIT:
pygame.quit()
sys.exit()
For each screen, create new buttons, then remove them when the screen exits. In your loop, check if the button is on the screen before the collision check.
Try this code:
HeroButton = None
while run:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
mouse = pygame.mouse.get_pos()
if QuitButton[1].collidepoint(mouse): # quit button on every screen ?
pygame.quit()
sys.exit()
if StartButton != None and StartButton[1].collidepoint(mouse):
StartButton = None # done with this button
HeroButton = HeroCreation()
elif HeroButton != None and HeroButton[1].collidepoint(mouse):
HeroButton = None # done with this button
drawGrid(width, height, window) # start game?
if event.type == QUIT:
pygame.quit()
sys.exit()

Utilising the pygame.mixer.music.get_endevent()

I want a tkinter label to show nothing when a sound effect has finished.
I've been researching the www on how to create/initialise/catch the end of music event with no luck.
def play_btn():
if mixer.music.get_busy():
mixer.music.fadeout(1000)
snd_fyl.set(snd_list.get(ACTIVE))
mixer.music.load(snd_dir+"/"+snd_list.get(ACTIVE)+"mp3")
mixer.music.play()
def stop_btn():
mixer.music.stop()
clear_label()
def clear_label():
snd_fyl.set("")
snd_lbl1 = LabelFrame(MainWindow, text="Sound effect playing", labelanchor=N)
snd_playing_lbl = Label(snd_lbl1, width=40, textvariable=snd_fyl)
Obviously play_btn function plays a sound effect from a list.
The stop_btn function prematurely halts the sound effect and clears the label.
The clear_label function has been created in readiness for the end_of_song event
You have to use set_endevent() to set value which it will send to event queue when music has finished.
MUSIC_END = pygame.USEREVENT+1
pygame.mixer.music.set_endevent(MUSIC_END)
And then you can test it in event loop
if event.type == MUSIC_END:
print('music end event')
It will print text when music has finished - but not when you stop it or pause it.
BTW: on Linux I see this text few milliseconds before it ends playing music.
Full working example - but without tkinter
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
MUSIC_END = pygame.USEREVENT+1
pygame.mixer.music.set_endevent(MUSIC_END)
pygame.mixer.music.load('sound.wav')
pygame.mixer.music.play()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == MUSIC_END:
print('music end event')
if event.type == pygame.MOUSEBUTTONDOWN:
# play again
pygame.mixer.music.play()
pygame.quit()
EDIT: Example with tkinter
import pygame
import tkinter as tk
def check_event():
for event in pygame.event.get():
if event.type == MUSIC_END:
print('music end event')
label['text'] = ''
root.after(100, check_event)
def play():
label['text'] = 'playing'
pygame.mixer.music.play()
# --- main ---
pygame.init()
MUSIC_END = pygame.USEREVENT+1
pygame.mixer.music.set_endevent(MUSIC_END)
pygame.mixer.music.load('sound.wav')
root = tk.Tk()
label = tk.Label(root)
label.pack()
button = tk.Button(root, text='Play', command=play)
button.pack()
check_event()
root.mainloop()
pygame.quit()

PyGame, waiting for computer to decide its move

Okay
So I am building a tic-tac-toe on pygame in which A player can play against player, computer vs player or computer vs computer.
I have already developed the code for a MiniMaxAgent() which takes its input as a 2D matrix and returns the (row, col) where it would play a move. The thing is that this code can take several seconds to execute on an nxn board. Because of this the pyGame Code hangs.
Sample event loop:
while running:
mouseClicked = False
DISPLAYSURF.fill(BGCOLOR)
renderBoard()
#event handlers
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEMOTION:
mouseX, mouseY = event.pos
elif event.type == MOUSEBUTTONUP:
mouseX, mouseY = event.pos
mouseClicked = True
row, col = players[currentPlayer].turn(currentPlayer*board.state)
board.state[row][col] = currentPlayer
currentPlayer = currentPlayer * -1
pygame.display.update()
As you can see, when I call the function players[currentPlayer].turn(), It should return me the optimal move in some seconds. But PyGame freezes.
How should I implement this?
A simple solution is to run the blocking function in question in a Thread. This will allow your game loop to keep running.
Here's a simple example. Note how the loop keeps running while the turn function wastes some time.
import pygame
import pygame.freetype
import random
import time
import threading
pygame.init()
screen = pygame.display.set_mode((800, 300))
PLAYER_TURN, AI_TURN = 0, 1
font = pygame.freetype.Font(None, 30)
state = PLAYER_TURN
running = True
result = None
def turn(boardstate):
print('thinking about ' + boardstate + '....')
time.sleep(1)
print('thinking very hard....')
time.sleep(1)
global result
global state
result = random.randint(10, 100)
state = PLAYER_TURN
r = pygame.rect.Rect((0, 250, 100, 50))
while running:
for e in pygame.event.get():
if e.type == pygame.QUIT:
running = False
elif e.type == pygame.MOUSEBUTTONUP:
if state == PLAYER_TURN:
state = AI_TURN
threading.Thread(target=turn, args=['an argument']).start()
screen.fill(pygame.color.Color('grey'))
if state == PLAYER_TURN:
font.render_to(screen, (50, 50), 'It is your turn. Click anything')
if result:
font.render_to(screen, (50, 180), 'AI said: ' + str(result))
elif state == AI_TURN:
font.render_to(screen, (50, 50), 'Waiting for AI')
pygame.draw.rect(screen, pygame.color.Color('red'), r)
r.move_ip(1, 0)
if not screen.get_rect().contains(r):
r.x = 0
pygame.display.update()

how to store multiple mouse click coordinates python

so Ive been trying to find a way to store multiple click x and y's with no luck, Ive worked with pygame and opencv but I cannot find a way to store multiple x and y's without deleting the previous set.
import pygame
pygame.init()
while True:
for e in pygame.event.get():
if e.type == pygame.MOUSEBUTTONDOWN:
print (pygame.mouse.get_pos())
Just append the event.pos or pygame.mouse.get_pos() to a list or collections.deque if the size should be limited.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
clicks = []
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
clicks.append(event.pos)
print(clicks)
screen.fill((30, 30, 30))
pg.display.flip()
clock.tick(30)
pg.quit()

Resources