Python menu with "add image" option [duplicate] - python

I am a newbie to Pygame and I have created already the codes for my button but I still have a problem because I don't know how will I put an image instead of the solid color red in a rectangle. Here is my codes, hope you can help!
def button(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(screen, ac, (x, y, w, h))
if click[0] == 1 and action!= None:
if action == "continue":
quiz()
else:
pygame.draw.rect(screen, ic, (x, y, w, h))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.blit(randomList, [0, 0])
button(399, 390, 300, 50, red, brightRed, "continue")
pygame.display.update()

All you have to do is to load an image:
my_image = pygame.image.load('my_image.png').convert_alpha()
And blit it an top of the rectangle:
def button(x, y, w, h, ic, ac, img, imgon, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
rect = pygame.Rect(x, y, w, h)
on_button = rect.collidepoint(mouse)
if on_button:
pygame.draw.rect(screen, ac, rect)
screen.blit(imgon, imgon.get_rect(center = rect.center))
else:
pygame.draw.rect(screen, ic, rect)
screen.blit(img, img.get_rect(center = rect.center))
if on_button:
if click[0] == 1 and action!= None:
if action == "continue":
quiz()
image = pygame.image.load('my_image.png').convert_alpha()
imageOn = pygame.image.load('my_image_on.png').convert_alpha()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.blit(randomList, [0, 0])
button(399, 390, 300, 50, red, brightRed, image, imageOn, "continue")
pygame.display.update()
In pygame a button is nothing more than a pygame.Surface object. It is completely irrelevant whether a text or an image is on the button. I recommend to represent the buttons by pygame.sprite.Sprite objects.
See also Pygame mouse clicking detection respectively Mouse and Sprite.
Minimal example:
import pygame
class SpriteObject(pygame.sprite.Sprite):
def __init__(self, x, y, filename):
super().__init__()
img = pygame.image.load(filename).convert_alpha()
self.original_image = pygame.Surface((70, 70))
self.original_image.blit(img, img.get_rect(center = self.original_image.fill((127, 127, 127)).center))
self.hover_image = pygame.Surface((70, 70))
self.hover_image.blit(img, img.get_rect(center = self.hover_image.fill((228, 228, 228)).center))
self.image = self.original_image
self.rect = self.image.get_rect(center = (x, y))
self.hover = False
def update(self):
self.hover = self.rect.collidepoint(pygame.mouse.get_pos())
self.image = self.hover_image if self.hover else self.original_image
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
group = pygame.sprite.Group([
SpriteObject(window.get_width() // 3, window.get_height() // 3, 'Apple64.png'),
SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, 'Banana64.png'),
SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, 'Pear64.png'),
SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, 'Plums64.png'),
])
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update()
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()

Related

Using electron with python and pygame to create a menu [duplicate]

I am making a game in pygame that requires me to have a drop down box and radio buttons for selection of an option. Any hints as to how to go about this would be appreciated.
Regards,
I recommend implementing a class for graphical UI elements. The class has a constructor (__init__) that defines all required attributes and all required states. A Draw method that draws the entire UI element depending on its current state. The class should have an update method that receives the events, changes the state of the UI:
class OptionBox():
def __init__(self, x, y, w, h, color, ...):
...
def draw(self, surf):
...
def update(self, event_list):
...
See also UI elements and see some examples:
Dropdown menu
Radio Button
Option Box:
import pygame
class OptionBox():
def __init__(self, x, y, w, h, color, highlight_color, font, option_list, selected = 0):
self.color = color
self.highlight_color = highlight_color
self.rect = pygame.Rect(x, y, w, h)
self.font = font
self.option_list = option_list
self.selected = selected
self.draw_menu = False
self.menu_active = False
self.active_option = -1
def draw(self, surf):
pygame.draw.rect(surf, self.highlight_color if self.menu_active else self.color, self.rect)
pygame.draw.rect(surf, (0, 0, 0), self.rect, 2)
msg = self.font.render(self.option_list[self.selected], 1, (0, 0, 0))
surf.blit(msg, msg.get_rect(center = self.rect.center))
if self.draw_menu:
for i, text in enumerate(self.option_list):
rect = self.rect.copy()
rect.y += (i+1) * self.rect.height
pygame.draw.rect(surf, self.highlight_color if i == self.active_option else self.color, rect)
msg = self.font.render(text, 1, (0, 0, 0))
surf.blit(msg, msg.get_rect(center = rect.center))
outer_rect = (self.rect.x, self.rect.y + self.rect.height, self.rect.width, self.rect.height * len(self.option_list))
pygame.draw.rect(surf, (0, 0, 0), outer_rect, 2)
def update(self, event_list):
mpos = pygame.mouse.get_pos()
self.menu_active = self.rect.collidepoint(mpos)
self.active_option = -1
for i in range(len(self.option_list)):
rect = self.rect.copy()
rect.y += (i+1) * self.rect.height
if rect.collidepoint(mpos):
self.active_option = i
break
if not self.menu_active and self.active_option == -1:
self.draw_menu = False
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if self.menu_active:
self.draw_menu = not self.draw_menu
elif self.draw_menu and self.active_option >= 0:
self.selected = self.active_option
self.draw_menu = False
return self.active_option
return -1
pygame.init()
clock = pygame.time.Clock()
window = pygame.display.set_mode((640, 480))
list1 = OptionBox(
40, 40, 160, 40, (150, 150, 150), (100, 200, 255), pygame.font.SysFont(None, 30),
["option 1", "2nd option", "another option"])
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
selected_option = list1.update(event_list)
if selected_option >= 0:
print(selected_option)
window.fill((255, 255, 255))
list1.draw(window)
pygame.display.flip()
pygame.quit()
exit()

wondering about pixel perfect mousepointer collision in the case of a button

I've got a pretty simple code up right now that just moves between two menu screens once the button for each is pressed. I know that you can mask images in pygame to get pixel perfect collision but not sure how I'd go about doing it for the buttons in this code (it's just pretty annoying that you can click slightly off and have it still transfer you to the other menu). A follow-up question I had was on how I could do fade transitions between the screens - I've seen some tutorials but they've all seemed overcomplicated.
import pygame, os, time, random, sys
width, height = 1600, 900
pygame.init()
mainMenu = True
resMenu = False
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
fps = 120
bg = pygame.image.load("assets/mainMenu.jpg").convert()
bgRes = pygame.image.load("assets/resMenu.jpg").convert()
res_button_image = pygame.transform.scale2x(
pygame.image.load("assets/changeRes.png")
).convert_alpha()
back_button_image = pygame.transform.scale2x(
pygame.image.load("assets/backToMenu.png")
).convert_alpha()
class resolutionButton:
def __init__(self, x, y, image, scale):
width = image.get_width()
height = image.get_height()
self.image = pygame.transform.scale(
image, (int(width * scale), int(height * scale))
)
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
self.clicked = False
def draw(self, surface):
action = False
# get mouse position
pos = pygame.mouse.get_pos()
# check mouseover and clicked conditions
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
# draw button on screen
surface.blit(self.image, (self.rect.x, self.rect.y))
return action
class backtoMenuButton:
def __init__(self, x, y, image, scale):
width = image.get_width()
height = image.get_height()
self.image = pygame.transform.scale(
image, (int(width * scale), int(height * scale))
)
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
self.clicked = False
def draw(self, surface):
action = False
# get mouse position
pos = pygame.mouse.get_pos()
# check mouseover and clicked conditions
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
# draw button on screen
surface.blit(self.image, (self.rect.x, self.rect.y))
return action
while True:
while mainMenu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
start_button = resolutionButton(100, 400, res_button_image, 1)
screen.blit(bg, (0, 0))
if start_button.draw(screen):
resMenu = True
mainMenu = False
pygame.display.update()
clock.tick(fps)
while resMenu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
back_button = backtoMenuButton(100, 700, back_button_image, 1)
screen.blit(bgRes, (0, 0))
if back_button.draw(screen):
resMenu = False
mainMenu = True
pygame.display.update()
clock.tick(fps)
To test whether the mouse is on an icon, you need to create a mask from the image (pygame.Surface) with pygame.mask.from_surface:
image_mask = pygame.mask.from_surface(image)
Define the bounding rectangle of the icon. e.g.:
image_rect = image.get_rect(center = (x, y)
Test whether the mouse is on the image, calculate the coordinates of the pixel on which the mouse is located (mask_x, mask_y) and use pygame.mask.Mask.get_at to test whether the mask is set at this point:
mouse_pos = pygame.mouse.get_pos()
if image_rect.collidepoint(mouse_pos):
mask_x = mouse_pos[0] - image_rect.left
mask_y = mouse_pos[1] - image_rect.top
if image_mask.get_at((mask_x, mask_y)):
print("hit")
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
image = pygame.image.load('Banana.png')
image_rect = image.get_rect(center = window.get_rect().center)
image_mask = pygame.mask.from_surface(image)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
color = (0, 0, 0)
mouse_pos = pygame.mouse.get_pos()
if image_rect.collidepoint(mouse_pos):
mask_x = mouse_pos[0] - image_rect.left
mask_y = mouse_pos[1] - image_rect.top
if image_mask.get_at((mask_x, mask_y)):
color = (255, 0, 0)
window.fill(color)
window.blit(image, image_rect)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()

How can I make a button that goes to website in pygame?

I have a "Credits" menu in my pygame and I'd like to make some buttons that go to certain websites.
What I mean is, when the button is clicked, it should open up, for example, GitHub (or whatever the link is).
Is there a way I can achieve this?
Implement a Button class ans use the webbrowser module to open an URL
import webbrowser
if event.type == pygame.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos) and event.button == 1:
webbrowser.open(self.url)
See also Pygame mouse clicking detection and How can I open a website in my web browser using Python?
Minimal example:
import pygame
import webbrowser
class Button(pygame.sprite.Sprite):
def __init__(self, x, y, w, h, font, text, action):
super().__init__()
text_surf = font.render(text, True, (0, 0, 0))
self.button_image = pygame.Surface((w, h))
self.button_image.fill((96, 96, 96))
self.button_image.blit(text_surf, text_surf.get_rect(center = (w // 2, h // 2)))
self.hover_image = pygame.Surface((w, h))
self.hover_image.fill((96, 96, 96))
self.hover_image.blit(text_surf, text_surf.get_rect(center = (w // 2, h // 2)))
pygame.draw.rect(self.hover_image, (96, 196, 96), self.hover_image.get_rect(), 3)
self.image = self.button_image
self.rect = pygame.Rect(x, y, w, h)
self.action = action
def update(self, event_list):
hover = self.rect.collidepoint(pygame.mouse.get_pos())
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN:
if hover and event.button == 1:
self.action()
self.image = self.hover_image if hover else self.button_image
pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()
font50 = pygame.font.SysFont(None, 50)
button1 = Button(50, 40, 200, 60, font50, "Pygame",
lambda : webbrowser.open('https://www.pygame.org/news'))
group = pygame.sprite.Group(button1)
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()

How to Fill a rectangle with color every time the user clicks it? pygame

I know that there are several similar questions online, but none of them really helped me. I simply want to draw a grid and give the user the option to click into those grid cells. Every time the user clicks, the color/fill of the cell should change from black to white.
What I'm doing at the moment is the following:
BLACK = (0, 0, 0)
WHITE = (200, 200, 200)
def drawGrid(h, w, blocksize):
for x in range(w):
for y in range(h):
rect = pygame.Rect(x*blocksize, y*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, WHITE, rect, 1)
def handle_events():
col = WHITE
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
col = WHITE
# determine mouse position
mpos_x, mpos_y = event.pos
# determine cell number
coord = mpos_x // blocksize, mpos_y // blocksize
rect = pygame.Rect(coord[0]*blocksize, coord[1]*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, col, rect, 1)
pygame.display.update()
def main():
global SCREEN, CLOCK, blocksize
w = int( sys.argv[1] )
h = int( sys.argv[2] )
blocksize = int( sys.argv[3] )
pygame.init()
SCREEN = pygame.display.set_mode((h, w))
CLOCK = pygame.time.Clock()
SCREEN.fill(BLACK)
drawGrid(h,w,blocksize)
handle_events()
if __name__ == "__main__":
main()
The program is printing the grid. However, when I click somewhere nothing happens. I know this is not the best code, so I would appreciate for any suggestion.
I changed the code a little and it worked properly, pygame.draw.rect(SCREEN, col, rect, 1) you draw same thing and you can't see the change. You should use pygame.draw.rect(SCREEN, col, rect):
import pygame
import sys
BLACK = (0, 0, 0)
WHITE = (200, 200, 200)
# WINDOW_HEIGHT = 400
# WINDOW_WIDTH = 400
def drawGrid(h, w, blocksize):
#blockSize = 20 #Set the size of the grid block
for x in range(w):
for y in range(h):
rect = pygame.Rect(x*blocksize, y*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, WHITE, rect, 1)
def handle_events():
#coords_list = []
col = WHITE
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
col = WHITE
# determine mouse position
mpos_x, mpos_y = event.pos
# determine cell number
coord = mpos_x // 32, mpos_y // 32
rect = pygame.Rect(coord[0]*32, coord[1]*32,
32, 32)
pygame.draw.rect(SCREEN, col, rect)
#coords_list.append(coord)
pygame.display.update()
#return coords_list
def main():
global SCREEN, CLOCK, blocksize
pygame.init()
SCREEN = pygame.display.set_mode((480, 640))
CLOCK = pygame.time.Clock()
SCREEN.fill(BLACK)
drawGrid(480,640,32)
handle_events()
if __name__ == "__main__":
main()
As a suggestion, I think you should use sprite for every cell. For example like this:
class Cell(pygame.sprite.Sprite):
def __init__(self, sprite_group, x, y, cell_dimension, color=BLACK):
self.groups = sprite_group
pygame.sprite.Sprite.__init__(self, self.groups)
self.image = pygame.Surface((cell_dimension, cell_dimension))
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = x * cell_dimension
self.rect.y = y * cell_dimension
self.clicked = False
def update(self):
if self.clicked:
self.image.fill(WHITE)

Pygame mask collision

I'm trying to get proper collision detection with rotating surfaces in pygame. I decided to try using masks. It somewhat works, but it is not as precise as I'd liked/thought. I tried updating the mask at the end of the cycle to get a "fresh" hitbox for the next frame, but it didn't work as expected. What is my mistake?
import pygame
import random
WHITE = [255, 255, 255]
RED = [255, 0, 0]
pygame.init()
FPS = pygame.time.Clock()
fps = 6
winW = 1000
winH = 500
BGCOLOR = WHITE
win = pygame.display.set_mode((winW, winH))
win.fill(WHITE)
pygame.display.set_caption('')
pygame.display.set_icon(win)
class Box(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([w, h], pygame.SRCALPHA)
self.image.fill(random_color())
self.mask = pygame.mask.from_surface(self.image)
self.rect = pygame.Rect(x, y, w, h)
self.angle = 0
def move(self):
self.rect.center = pygame.mouse.get_pos()
def draw(self):
blits = self.rotate()
win.blit(blits[0], blits[1])
self.mask = pygame.mask.from_surface(blits[0])
def rotate(self):
self.angle += 3
new_img = pygame.transform.rotate(self.image, self.angle)
new_rect = new_img.get_rect(center=self.rect.center)
return new_img, new_rect
def update_display():
win.fill(BGCOLOR)
player.draw()
for p in platforms:
p.draw()
pygame.display.update()
def collision():
return pygame.sprite.spritecollide(player, plat_collide, False, pygame.sprite.collide_mask)
def random_color():
return [random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]
player = Box(100, 400, 50, 50)
platforms = [Box(300, 400, 100, 50)]
plat_collide = pygame.sprite.Group(platforms)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
hits = collision()
if hits:
BGCOLOR = RED
else:
BGCOLOR = WHITE
player.move()
update_display()
FPS.tick(fps)
pygame.quit()
Your application works fine. But note, pygame.sprite.collide_mask() use the .rect and .mask attribute of the sprite object for the collision detection.
You have to update self.rect after rotating the image:
class Box(pygame.sprite.Sprite):
# [...]
def rotate(self):
self.angle += 3
new_img = pygame.transform.rotate(self.image, self.angle)
new_rect = new_img.get_rect(center=self.rect.center)
# update .rect attribute
self.rect = new_rect # <------
return new_img, new_rect
See also Sprite mask
Minimal example: repl.it/#Rabbid76/PyGame-SpriteMask
import pygame
class SpriteObject(pygame.sprite.Sprite):
def __init__(self, x, y, w, h, color):
pygame.sprite.Sprite.__init__(self)
self.angle = 0
self.original_image = pygame.Surface([w, h], pygame.SRCALPHA)
self.original_image.fill(color)
self.image = self.original_image
self.rect = self.image.get_rect(center = (x, y))
self.mask = pygame.mask.from_surface(self.image )
def update(self):
self.rotate()
def rotate(self):
self.angle += 0.3
self.image = pygame.transform.rotate(self.original_image, self.angle)
self.rect = self.image.get_rect(center = self.rect.center)
self.mask = pygame.mask.from_surface(self.image )
pygame.init()
clock = pygame.time.Clock()
window = pygame.display.set_mode((400, 400))
size = window.get_size()
moving_object = SpriteObject(0, 0, 50, 50, (128, 0, 255))
static_objects = [
SpriteObject(size[0] // 2, size[1] // 3, 100, 50, (128, 128, 128)),
SpriteObject(size[0] // 4, size[1] * 2 // 3, 100, 50, (128, 128, 128)),
SpriteObject(size[0] * 3 // 4, size[1] * 2 // 3, 100, 50, (128, 128, 128))
]
all_sprites = pygame.sprite.Group([moving_object] + static_objects)
static_sprites = pygame.sprite.Group(static_objects)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
moving_object.rect.center = pygame.mouse.get_pos()
all_sprites.update()
collide = pygame.sprite.spritecollide(moving_object, static_sprites, False, pygame.sprite.collide_mask)
window.fill((255, 0, 0) if collide else (255, 255, 255))
all_sprites.draw(window)
pygame.display.update()
pygame.quit()
exit()

Categories