how to center text in a rectangle in pygame [duplicate] - python

This question already has answers here:
How to Center Text in Pygame
(6 answers)
Closed 1 year ago.
im making a game in pygame and I can't figure out how I can center some text in a rectangle. I draw the rectangle by using pygame.draw_rect(win, color, (x, y, w, h)) and text with
text = font.render("text", 1, color).
i know I can use text_rect = text.get_Rect(), to get a rect object of the text. but I don't know how to use those to center the text. I was searching for a solution online and couldn't find it

You can get a rect from the surface generated by rendering your font. Then set the centre that rect to the centre of your rectangle. Then blit your font surface to the main surface.
Here's a small example that uses the mousewheel to enlarge and shrink the border rectangle to show that the font remains centred. Pressing a key randomises the text.
import random
import pygame
WIDTH, HEIGHT = 320, 240
pygame.init()
pygame.font.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
# use the first available font
font = pygame.font.SysFont(pygame.font.get_fonts()[0], 60)
pygame.display.set_caption("Centering Font Rect")
text = "Initial Text"
widget = font.render(text, True, pygame.Color("seagreen"))
border = pygame.Rect(10, 10, WIDTH - 40, HEIGHT - 40)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
# change the text
text = random.choice(["Short", "Long Looooong", ".", "+++", "ABC"])
widget = font.render(text, True, pygame.Color("purple"))
elif event.type == pygame.MOUSEWHEEL:
# enlarge or shrink the border rect
border.inflate_ip(event.y, event.y)
screen.fill(pygame.Color("turquoise"))
# draw border rect
pygame.draw.rect(screen, pygame.Color("red"), border, width=1)
# get the current font rect
font_rect = widget.get_rect()
# move rect to be centered on border rect
font_rect.center = border.center
screen.blit(widget, font_rect)
# update display
pygame.display.update()
pygame.quit()
This shows:
Then after some resizing:

Related

How to get mouse coordinates on a grid [duplicate]

This question already has answers here:
Is it possible to update a pygame drawing attribute when a event occurs?
(1 answer)
Python: How to make drawn elements snap to grid in pygame
(1 answer)
Closed 5 months ago.
I have a function to get a position on a grid (the size of the grid is flexible) based on the mouse position
def get_coords_from_mouse_pos(self, mx, my):
return mx // self.item_width, my // self.item_height
self.item_width and self.item_height are the width and height of one tile on the grid. mx and my are the mouse x and y positions. This probably exists somewhere else but I can't find it.
I probably just have the math wrong, but I would appreciate it if someone could help.
Here's a minimal example that shows your logic working, the left and top grid borders are considered as part of the cell below.
import pygame
# Configuration
width, height = 320, 240
cell_width, cell_height = 32, 32
pygame.init()
sys_font = pygame.font.SysFont(None, 60)
text = sys_font.render(" ", True, "turquoise")
clock = pygame.time.Clock()
screen = pygame.display.set_mode((width, height))
# create transparent background grid
grid = pygame.Surface((width, height), flags=pygame.SRCALPHA)
for y in range(0, height, cell_height):
pygame.draw.aaline(grid, "green", (0, y), (width, y)) # horizontal lines
for x in range(0, width, cell_width):
pygame.draw.aaline(grid, "green", (x, 0), (x, height)) # vertical lines
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
mx, my = pygame.mouse.get_pos()
pygame.display.set_caption(f"Mouse: {mx}, {my}")
if mx > 0 and my > 0:
cellx = mx // cell_width
celly = my // cell_height
text = sys_font.render(f"Cell: {cellx}, {celly}", True, "turquoise")
# Graphics
screen.fill("grey25")
screen.blit(grid, (0, 0))
# Draw Text in the center
screen.blit(text, text.get_rect(center=screen.get_rect().center))
# Update Screen
pygame.display.update()
clock.tick(30)
pygame.quit()
This will look something like this:

Padding for text using pygame SysFont

I'm trying to use pygame's SysFont to display some text on the screen but I want the background rectangle to be slightly wider than the actual text.
When I use the following code the background seems to start exactly where the first letter starts and finishes exactly where the last letter ends.
font = pygame.font.SysFont("Helvetica", 30)
img = font.render("ABC", True, "white", "blue1")
img_width = img.get_width()
img_height = img.get_height()
screen.blit(img, (200, 200, img_width, img_height))
Is there a way to have some padding to the left and right of the text in the background colour? I thought that perhaps adding something to img_width might help but it didn't.
You need to render the text on Surface larger than the text Surface:
Render the text with a transparent background
Create a Surface larger the the text Surface (see pygame.Rect.inflate)
Fill the Surface with the background color
blit the text in the center of the Surface
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
def create_text_box(font, text, text_color, box_color, margin_x, margin_y):
text_surf = font.render("ABC", True, text_color, "blue1")
box_surf = pygame.Surface(text_surf.get_rect().inflate(margin_x, margin_y).size)
box_surf.fill(box_color)
box_surf.blit(text_surf, text_surf.get_rect(center = box_surf.get_rect().center))
return box_surf
font = pygame.font.SysFont(None, 100)
text_surf = create_text_box(font, "ABC", "white", "blue1", 10, 0)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
See also How to separately change the opacity of a text on a button pygame?

Have an outline of text in Pygame

So I have this function in which it puts text on the screen:
def text_speech(font : str ,size : int,text : str,color,x,y, bold : bool):
SCREEN = width, height = 900, 600
font = pygame.font.Font(font,size)
font.set_bold(bold)
text = font.render(text, True, color)
textRect = text.get_rect()
textRect.center = (x,y)
screen.blit(text,textRect)
If I do this:
screen.fill((0,0,0))
text_speed('arialnarrow.ttf', 40, 'Hello', (255,255,255), (width/2), (height/2), False)
It generates the world 'Hello' on a black screen with white text. Is it possible that if the user hovers their mouse over this, it creates a red (255,0,0) outline?
To accomplish an outline you have to blit the multiple times. Render the text in the outline color (red):
outlineSurf = font.render(text, True, (255, 0, 0))
outlineSize = outlineSurf.get_size()
Create a surface which is grater than the text surface. The width and the height have to be increased by the doubled outline thickness:
textSurf = pygame.Surface((outlineSize[0] + outline*2, outlineSize[1] + 2*outline))
textRect = textSurf.get_rect()
Blit the outline surface 8 times on the text surface, shifted by the outline thickness (horizontal, vertical and diagonal:
offsets = [(ox, oy)
for ox in range(-outline, 2*outline, outline)
for oy in range(-outline, 2*outline, outline)
if ox != 0 or ox != 0]
for ox, oy in offsets:
px, py = textRect.center
textSurf.blit(outlineSurf, outlineSurf.get_rect(center = (px+ox, py+oy)))
Render the text with the text color and convert the surface to a per pixel alpha format (convert_alpha):
innerText = font.render(text, True, color).convert_alpha()
Blit the text in the middle of textSurf:
textSurf.blit(innerText, innerText.get_rect(center = textRect.center))
Blit textSurf onto the window:
textRect.center = (x,y)
screen.blit(textSurf, textRect)
See the example:
import pygame
import pygame.font
pygame.init()
width, height = 400, 300
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
textRect = pygame.Rect(0, 0, 0, 0)
def text_speech(font : str, size : int, text : str, color, x, y, bold : bool, outline: int):
global textRect
# font = pygame.font.Font(font,size)
font = pygame.font.SysFont(None, size)
font.set_bold(True)
if outline > 0:
outlineSurf = font.render(text, True, (255, 0, 0))
outlineSize = outlineSurf.get_size()
textSurf = pygame.Surface((outlineSize[0] + outline*2, outlineSize[1] + 2*outline))
textRect = textSurf.get_rect()
offsets = [(ox, oy)
for ox in range(-outline, 2*outline, outline)
for oy in range(-outline, 2*outline, outline)
if ox != 0 or ox != 0]
for ox, oy in offsets:
px, py = textRect.center
textSurf.blit(outlineSurf, outlineSurf.get_rect(center = (px+ox, py+oy)))
innerText = font.render(text, True, color).convert_alpha()
textSurf.blit(innerText, innerText.get_rect(center = textRect.center))
else:
textSurf = font.render(text, True, color)
textRect = textSurf.get_rect()
textRect.center = (x,y)
screen.blit(textSurf, textRect)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
hover = textRect.collidepoint(pygame.mouse.get_pos())
outlineSize = 3 if hover else 0
screen.fill((0,0,0))
text_speech('arialnarrow.ttf', 40, 'Hello', (255,255,255), (width/2), (height/2), False, outlineSize)
pygame.display.flip()
Assuming that by "outline" you mean a stroke around it, I've got and easy solution. Simply render the same text, centered around the same position as the text you've already written, a bit bigger and in red. Then, just check when the mouse is hovering over the rect of your initial text, and if so, blit the outline.
In order to do this, we need to extract the rect of your first text. I changed your function so that it outputs the rendered surface, and rect.
I also made a few other adjustments :
You don't need to generate the font and render the text each time, this wastes CPU cycles. I recommend setting each of your fonts as global constants, for each size/typeface
You define a screen within your function, but never use it. I changed the function so that it no longer does the job of rendering.
When you call text_speech (I assume your second usage is a typo), width and height don't refer to anything. I also defined them as global constants, which I set to be your display size.
You haven't included any display code, so I wrote the bare minimum for a running concept.
import pygame
pygame.init()
# Font constants
ARIALNARROW_40 = font = pygame.font.Font('arialnarrow.ttf', 40)
ARIALNARROW_42 = font = pygame.font.Font('arialnarrow.ttf', 42)
# Screen size
WIDTH = 900
HEIGHT = 600
def text_speech(font, text, color, x, y, bold):
font.set_bold(bold)
rendered_text = font.render(text, True, color)
# Directly center the rect upon its creation
text_rect = rendered_text.get_rect(center=(x,y))
return text_rect, rendered_text
screen = pygame.display.set_mode((WIDTH, HEIGHT))
inner_rect, inner_text = text_speech(
ARIALNARROW_40, 'Hello', (255, 255, 255),
(WIDTH / 2), (HEIGHT / 2), False
)
# For your outline
outline_rect, outline_text = text_speech(
ARIALNARROW_42, 'Hello', (255, 0, 0),
(WIDTH / 2), (HEIGHT / 2), False
)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
# Paint our screen
screen.fill((0,0,0))
if inner_rect.collidepoint(pygame.mouse.get_pos()):
# Touching our text! Render outline
screen.blit(outline_text, outline_rect)
screen.blit(inner_text, inner_rect)
# Enact our display changes
pygame.display.update()
Note This does add the potentially unwanted affected of having a "side zoom". However getting around this would mean that'd you either have to mess around with font kerning (with the pygame.freetype module) but that could get very messy very fast, or you could prerender a stroke image ahead of time (and blit it using the same logic I used) but that would require you to rerender every time you changed the text, for all your text surfaces.

How to fade in a text or an image with PyGame

Can someone help me on this ? :
I try to make a title that appear smoothly on the user screen :
def image(name,x,y,u):
screen.blit(name,(x,y))
if u = 1
pygame.display.update()
Window == 'main'
While windows = 'main':
image(background,0,0)
image(title, 640, 120)
pygame.display.update()
But texte appaers suddently and not as I would like...
You want to use transparency, and pygame uses three different types:
There are three types of transparency supported in pygame: colorkeys, surface alphas, and pixel alphas. Surface alphas can be mixed with colorkeys, but an image with per pixel alphas cannot use the other modes. Colorkey transparency makes a single color value transparent. Any pixels matching the colorkey will not be drawn. The surface alpha value is a single value that changes the transparency for the entire image. A surface alpha of 255 is opaque, and a value of 0 is completely transparent.
Per pixel alphas are different because they store a transparency value for every pixel. This allows for the most precise transparency effects, but it also the slowest. Per pixel alphas cannot be mixed with surface alpha and colorkeys.
So let's use colorkey to create a transparent Surface and blit some text to it, and then use surface alpha to create the fade in effect by calling set_alpha and the Surface:
import pygame
def main():
screen = pygame.display.set_mode((300, 100))
FONT = pygame.font.SysFont(None, 64)
text = FONT.render('Hello World', False, pygame.Color('darkorange'))
surf = pygame.Surface(text.get_rect().size)
surf.set_colorkey((1,1,1))
surf.fill((1,1,1))
surf.blit(text, (0, 0))
clock = pygame.time.Clock()
alpha = 0
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
alpha = (alpha + 1) % 256
surf.set_alpha(alpha)
screen.fill(pygame.Color('dodgerblue'))
screen.blit(surf, (20, 20))
clock.tick(120)
print(alpha)
pygame.display.update()
if __name__ == '__main__':
pygame.init()
main()
pygame.Surface.blit() already does what you want. It bends a surface to an other surface, depended on its alpha channel. All you have to do is to set the alpha channel by pygame.Surface.set_alpha():
image.set_alpha(min(1.0,alpha)*255)
Write a function, which "blends" a surface to the screen. In the following function BlendSurface, the parameter alpha is the opacity value of the surface in range[0, 1]. 0.0 would generate a completely transparent (invisible) surface. 1.0 would generate a completely opaque surface:
def BlendSurface(image, pos, alpha):
image.set_alpha(min(1.0,alpha)*255)
screen.blit(image, pos)
See the simple demo program:
import pygame
import pygame.font
pygame.init()
BLUE = ( 0, 0, 255)
YELLOW = (255, 255, 0)
size = (800,600)
screen = pygame.display.set_mode(size)
def BlendSurface(image, pos, alpha):
image.set_alpha(min(1.0,alpha)*255)
screen.blit(blendImage, pos)
clock = pygame.time.Clock()
font = pygame.font.SysFont('Times New Roman', 100)
text = font.render('blend text', False, YELLOW)
i = 0
done = False
while not done:
clock.tick(60)
i += 1
if i > 200:
i = 0
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLUE)
BlendSurface(text, (100, 100), i/200)
pygame.display.flip()

Line is not drawn (It seems that pygame.draw.polygon is not running)

I made Button frame. But Square is not drawn.
class button:
def __init__(self,screen,size): # size -->(width,height)
self.width = size[0]
self.height = size[1]
self.screen = screen
def Buttongenerate(self,TEXT,POS):
font = pygame.font.Font(None,self.height//3)
text = font.render(TEXT,True,(250,250,250))
textsize = font.size(TEXT)
TEXTPOS = [self.width+(self.width-textsize[0])/2,\
self.height+(self.height-textsize[1])/2]
self.screen.blit(text,TEXTPOS)
pygame.draw.polygon(self.screen, (250,250,250), [[POS[0],POS[1]],\
[POS[0]+self.width,POS[1]],[POS[0]+self.width,POS[1]+self.height],\
[POS[0],POS[1]+self.height]], 2)
while True:
screen.fill((0,0,0))
buttontest2 = button(screen,(200,100))
buttontest2.Buttongenerate("TEST",(200,200))
pygame.display.flip()
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
exit(0)
When I run this code , but It seems that pygame.draw.polygon is not running
pygame.draw.polygon works just fine.
Your problem is that you calculate TEXTPOS wrong (I just guess the text should be rendered inside the rectangle). You should use
TEXTPOS = [POS[0]+(self.width-textsize[0])/2, POS[1]+(self.height-textsize[1])/2]
which actually uses POS to calculate the final position of the text.
Otherwise, the text is rendered above the rectangle. Here's the full, running code:
import pygame
pygame.init()
screen = pygame.display.set_mode((600,600))
class button:
def __init__(self,screen,size): # size -->(width,height)
self.width = size[0]
self.height = size[1]
self.screen = screen
def Buttongenerate(self,TEXT,POS):
font = pygame.font.Font(None,self.height//3)
text = font.render(TEXT,True,(250,250,250))
textsize = font.size(TEXT)
TEXTPOS = [POS[0]+(self.width-textsize[0])/2, POS[1]+(self.height-textsize[1])/2]
self.screen.blit(text,TEXTPOS)
pygame.draw.polygon(self.screen, (250,250,250), [[POS[0],POS[1]],[POS[0]+self.width,POS[1]],[POS[0]+self.width,POS[1]+self.height],[POS[0],POS[1]+self.height]], 2)
while True:
screen.fill((0,0,0))
buttontest2 = button(screen,(200,100))
buttontest2.Buttongenerate("TEST",(200,200))
pygame.display.flip()
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
Note that it would be better to create a new surface for the text and rectangle once instead of loading the font, rendering the text, and drawing on the screen surface every iteration of the main loop.

Categories