I am using pygame.font.Font.render() to render some text. I'd like the text to be translucent, ie have an alpha value other than 255, so I tried passing a color argument with an alpha value (eg (255, 0, 0, 150)) as the color argument for pygame.font.Font.render() but it didn't have any effect. I also tried using pygame.Surface.convert_alpha() on the resulting Surface object, but that didn't do anything either. Any ideas?
I'm not sure why, but after some experimentation I have discovered that the surface created with font.render cannot have it's alpha value changed. Just blit that surface to another surface, and change the alpha value of the new surface.
textsurface=font.render('Test', True, (0, 0, 0))
surface=pygame.Surface((100, 30))
surface.fill((255, 255, 255))
surface.blit(textsurface, pygame.Rect(0, 0, 10, 10))
surface.set_alpha(50)
window.blit(surface, pygame.Rect(0, 30, 10, 10))
When using the pygame.font module, the alpha channel of the text color is not taken into account when rendering a text, but see pygame.font.Font.render:
Antialiased images are rendered to 24-bit RGB images. If the background is transparent a pixel alpha will be included.
and pygame.Surface.set_alpha
Changed in pygame 2.0: per-surface alpha can be combined with per-pixel alpha.
Hence it is completely sufficient to set the transparency after rendering the text with set_alpha. This even works for anti-aliased text:
font = pygame.font.SysFont(None, 150)
text_surf = font.render('test text', True, (255, 0, 0))
text_surf.set_alpha(127)
window.blit(text_surf, (x, y))
Minimal example: repl.it/#Rabbid76/PyGame-TransparentText
import pygame
pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 150)
text_surf = font.render('test text', True, (255, 0, 0))
text_surf.set_alpha(127)
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (128, 128, 128), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:
pygame.draw.rect(background, color, rect)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.blit(background, (0, 0))
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
By using the pygame.freetype module, you can use a transparent color directly when creating a text surface:
ft_font = pygame.freetype.SysFont('Times New Roman', 150)
text_surf2, text_rect2 = ft_font.render('test text', (255, 0, 0, 128))
window.blit(text_surf2, (x, y))
Or if you are rendering the text directly onto a surface:
ft_font = pygame.freetype.SysFont('Times New Roman', 150)
ft_font.render_to(window, (x, y), 'test text', (255, 0, 0, 128))
Minimal example: repl.it/#Rabbid76/PyGame-TransparentFreeTypeText
import pygame
import pygame.freetype
pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()
ft_font = pygame.freetype.SysFont('Times New Roman', 150)
text_surf2, text_rect2 = ft_font.render('test text', (255, 0, 0, 128))
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (128, 128, 128), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:
pygame.draw.rect(background, color, rect)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.blit(background, (0, 0))
text_rect = ft_font.get_rect('test text')
text_rect.center = (window.get_width() // 2, window.get_height() // 2 - 70)
ft_font.render_to(window, text_rect.topleft, 'test text', (255, 0, 0, 128))
text_rect2.center = (window.get_width() // 2, window.get_height() // 2 + 70)
window.blit(text_surf2, text_rect2)
pygame.display.flip()
pygame.quit()
exit()
See also Text and font
You can change the transparency of the entire surface using a individual alpha value.
Use surf=pygame.Surface((size,pygame.SRCALPHA). Now if you change the alpha value by setting surf.set_alpha(alpha) of the screen, the surface around the text wont be black anymore.
Related
I am trying to make a logic in which i can subtract the geometrical shapes. Lets say i have to subtract a circle from a rectangle.
The idea is to draw each point of the rectangle as a single individual point using win.set_at(). And then when we need to subtract the circle from the rectangle we an simply calculate that the point on which we are drawing and completing our square lies or not in the circle. If it lies then don't set pixels else draw the pixel. This is my code:
import pygame
WIDTH, HEIGHT = 700, 500
win = pygame.display.set_mode((WIDTH, HEIGHT))
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
def drawer(window, x, y, radius, s_l, s_w, s_x, s_y):
for i in range(0, HEIGHT):
for j in range(0, WIDTH):
distance = (x - j)**2 + (y - i)**2
if not distance <= radius**2 and i >= s_y and j >= s_x and i <= s_w + s_x and j <= s_l + s_y:
window.set_at((j, i), BLACK)
c_x, c_y = 130, 150
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill(WHITE)
drawer(win, c_x, c_y, 30, 100, 100, 100, 100)
pygame.display.flip()
pygame.quit()
But is it is quite slow. Can you suggest a better way (more optimized algorithm) of doing it.
For a good performance I suggest to use pygame.mask. Draw the shapes on pygame.Surface objects and create masks from the surfaces:
mask1 = pygame.mask.from_surface(shape1)
mask2 = pygame.mask.from_surface(shape2)
Invert the mask of the second shape and create the overlapping mask. Finally, create a new _Surface: from the overlapping mask:
mask2.invert()
subtract_maks = mask1.overlap_mask(mask2, (0, 0))
subtract_shape = subtract_maks.to_surface(setcolor = "red", unsetcolor=(0, 0, 0, 0))
Minimal example
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
shape1 = pygame.Surface((200, 200), pygame.SRCALPHA)
pygame.draw.circle(shape1, "white", (100, 100), 100)
shape2 = pygame.Surface((200, 200), pygame.SRCALPHA)
pygame.draw.rect(shape2, "white", (25, 50, 150, 100))
mask1 = pygame.mask.from_surface(shape1)
mask2 = pygame.mask.from_surface(shape2)
mask2.invert()
subtract_maks = mask1.overlap_mask(mask2, (0, 0))
subtract_shape = subtract_maks.to_surface(setcolor = "red", unsetcolor=(0, 0, 0, 0))
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *background.get_size(), (128, 128, 128), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
[pygame.draw.rect(background, color, rect) for rect, color in tiles]
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
print(pygame.key.name(event.key))
window.blit(background, (0, 0))
window.blit(subtract_shape, (50, 50))
pygame.display.flip()
pygame.quit()
exit()
How can i make this kind of countdown in Pygame? (i'm looking for how could i make the circle's perimeter decrease, that's the issue, because displaying the time isn't hard )
Keep in mind that how long the perimeter of the circle is and the displayed time should be in proportion with each other.
Just use pygame.draw.arc and specify the stop_angle argument depending on the counter:
percentage = counter/100
end_angle = 2 * math.pi * percentage
pygame.draw.arc(window, (255, 0, 0), arc_rect, 0, end_angle, 10)
Minimal example:
import pygame
import math
pygame.init()
window = pygame.display.set_mode((200, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
counter = 100
text = font.render(str(counter), True, (0, 128, 0))
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, 1000)
def drawArc(surf, color, center, radius, width, end_angle):
arc_rect = pygame.Rect(0, 0, radius*2, radius*2)
arc_rect.center = center
pygame.draw.arc(surf, color, arc_rect, 0, end_angle, width)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
counter -= 1
text = font.render(str(counter), True, (0, 128, 0))
if counter == 0:
pygame.time.set_timer(timer_event, 0)
window.fill((255, 255, 255))
text_rect = text.get_rect(center = window.get_rect().center)
window.blit(text, text_rect)
drawArc(window, (255, 0, 0), (100, 100), 90, 10, 2*math.pi*counter/100)
pygame.display.flip()
pygame.quit()
exit()
Sadly the quality of pygame.draw.arc with a width > 1 is poor. However this can be improved, using cv2 and cv2.ellipse:
import pygame
import cv2
import numpy
pygame.init()
window = pygame.display.set_mode((200, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
counter = 100
text = font.render(str(counter), True, (0, 128, 0))
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, 1000)
def drawArcCv2(surf, color, center, radius, width, end_angle):
circle_image = numpy.zeros((radius*2+4, radius*2+4, 4), dtype = numpy.uint8)
circle_image = cv2.ellipse(circle_image, (radius+2, radius+2),
(radius-width//2, radius-width//2), 0, 0, end_angle, (*color, 255), width, lineType=cv2.LINE_AA)
circle_surface = pygame.image.frombuffer(circle_image.flatten(), (radius*2+4, radius*2+4), 'RGBA')
surf.blit(circle_surface, circle_surface.get_rect(center = center))
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
counter -= 1
text = font.render(str(counter), True, (0, 128, 0))
if counter == 0:
pygame.time.set_timer(timer_event, 0)
window.fill((255, 255, 255))
text_rect = text.get_rect(center = window.get_rect().center)
window.blit(text, text_rect)
drawArcCv2(window, (255, 0, 0), (100, 100), 90, 10, 360*counter/100)
pygame.display.flip()
pygame.quit()
exit()
So I read the documentation of pygame but I could not understand it clearly. I recently asked a question about bitmap fonts and I got some code as my answer; here is the code:
import pygame
pygame.init()
win = pygame.display.set_mode((800, 600))
font = pygame.font.Font("freesansbold.ttf", 32)
i = 0
text = "hello how are you?"
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
letter = text[i]
text_1 = font.render(letter, True, (255, 255, 255))
bw, bh = font.size(letter)
glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects()
# print(glyph_rect)
if glyph_rect:
gh = glyph_rect[0].height
print(f'letter {letter} bitmap height: {bh} glyph height: {gh}')
win.fill((0, 0, 0))
win.blit(text_1, (0, 0))
pygame.display.update()
i += 1
run = i < len(text)
pygame.quit()
So, my questions are on the line glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects().
What does the pygame.mask.from_surface() function do?
What does the line glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects() do?
What arguments does the variable glyph_rect return, and what is the meaning of those arguments?
pygame.mask.from_surface creates a pygame.mask.Mask object form a pygame.Surface.
A Surface is bitmap. A Mask is an 2 dimensional array with Boolean values. The Mask created is the size of the _Surface. A field is True if the corresponding pixel in the surface is not transparent, and False if it is transparent.
pygame.mask.Mask.get_bounding_rects creates a list of pygame.Rect objects. Each rectangle describes a bounding area of connected pixles.
If the Surface contains exactly 1 connected image, you will get exactly 1 rectangle surrounding the image.
See the example. The black rectangle is the Surface rectangle and the red rectangle is the bound rectangle of the connected component:
repl.it/#Rabbid76/ImageHitbox
import pygame
def getMaskRect(surf, top = 0, left = 0):
surf_mask = pygame.mask.from_surface(surf)
rect_list = surf_mask.get_bounding_rects()
surf_mask_rect = rect_list[0].unionall(rect_list)
surf_mask_rect.move_ip(top, left)
return surf_mask_rect
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
try:
my_image = pygame.image.load('Bomb-256.png')
except:
my_image = pygame.Surface((200, 200), pygame.SRCALPHA)
pygame.draw.circle(my_image, (0, 128, 0), (60, 60), 40)
pygame.draw.circle(my_image, (0, 0, 128), (100, 150), 40)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pos = window.get_rect().center
my_image_rect = my_image.get_rect(center = pos)
my_image_mask_rect = getMaskRect(my_image, *my_image_rect.topleft)
window.fill((255, 255, 255))
window.blit(my_image, my_image_rect)
pygame.draw.rect(window, (0, 0, 0), my_image_rect, 3)
pygame.draw.rect(window, (255, 0, 0), my_image_mask_rect, 3)
pygame.display.flip()
pygame.quit()
exit()
I have been assigned a homework problem in pygame to make two rectangles, one red , one green and when clicked, text appears which gives the name of the colour of the button, I have created the shapes successfully, however I don't know how to make the text appear when the button is clicked. Any help is appreciated
I have successfully drawn two rectangles using the pygame.draw.rect() function and I have called pos = pygame.mouse.get_pos() in order to get an x and y value for the user's mouse.
pos = pygame.mouse.get_pos()
x = pos[0]
y = pos[1]
screen.fill(WHITE)
pygame.draw.rect(screen, GREEN, [100, 300, 200, 200])
pygame.draw.rect(screen, RED, [400, 300, 200, 200])
pygame.display.flip()
clock.tick(60)
The result is two rectangles, one green, one red in the centre of the screen.
To verify if amouse button is pressed oyu can use pygame.mouse.get_pressed():
leftClicked = pygame.mouse.get_pressed()[0]
For text rendering you can use pygame.font or pygame.pygame.freetype. e. g:
import pygame.freetype
font = pygame.freetype.SysFont('Times New Roman', 30)
Define a pygame.Rect for each button:
rect_green = pygame.Rect(100, 300, 200, 200)
rect_red = pygame.Rect(400, 300, 200, 200)
Verify if the mouse button is pressed an the mouse coursor is on the button by .collidepoint()). e.g.:
pygame.draw.rect(screen, GREEN, rectGreen)
if leftClicked and rectGreen.collidepoint(pos):
text_surf, text_rect = font.render("GREEN", WHITE, size=30)
text_rect.center = rectGreen.center
screen.blit(text_surf, text_rect)
See the short example:
import pygame
import pygame.freetype
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
pygame.init()
size = (800,600)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
font = pygame.freetype.SysFont('Times New Roman', 30)
rect_green = pygame.Rect(100, 300, 200, 200)
rect_red = pygame.Rect(400, 300, 200, 200)
def DrawButton(surf, cursor_pos, pressed, rect, color, text_color, text):
pygame.draw.rect(surf, color, rect)
if pressed and rect.collidepoint(cursor_pos):
text_surf, text_rect = font.render(text, text_color, size=30)
text_rect.center = rect.center
screen.blit(text_surf, text_rect)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pos = pygame.mouse.get_pos()
x = pos[0]
y = pos[1]
leftClicked = pygame.mouse.get_pressed()[0]
screen.fill(WHITE)
DrawButton(screen, pos, leftClicked, rect_green, GREEN, BLACK, "GREEN")
DrawButton(screen, pos, leftClicked, rect_red, RED, BLACK, "RED")
pygame.display.flip()
clock.tick(60)
pygame.quit()
I tried to run this program, however this error comes up:
Traceback (most recent call last):
File "C:/Python/ComputingCW/ComputingCW.py", line 12, in <module>
Surface = pygame.image.load('Pygame_Background.jpg')((W, H), 0, 32)
TypeError: 'pygame.Surface' object is not callable
It used to be:
Surface = pygame.display.set_mode((W, H), 0, 32) and then using "Surface.fill(BLACK)" to make a black background.
I don't understand why won't it allow me to use a image as my background. Can someone please help me, and explain why is this happening.
import pygame, sys, time
from pygame.locals import *
pygame.init()
mainClock = pygame.time.Clock()
# all_fonts = pygame.font.get_fonts()
W = 1280
H = 900
Surface = pygame.image.load('Pygame_Background.jpg')((W, H), 0, 32)
pygame.display.set_caption('Physics in Motion')
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
game = True
mouse_pos = (0, 0)
mouse_click = (0, 0)
text1B = False
text2B = False
text3B = False
output = ''
while game == True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == MOUSEMOTION:
mouse_pos = event.pos
if event.type == MOUSEBUTTONUP:
mouse_click = event.pos
color = WHITE
Font = pygame.font.SysFont('arial', 72)
if text1B:
color = RED
text = Font.render('Start Simulation', True, color)
text_rect = text.get_rect()
text_rect.center = (W / 2, H / 5)
if text_rect.collidepoint(mouse_click):
output = 'Simulation()'
if text_rect.collidepoint(mouse_pos):
text1B = True
else:
text1B = False
Surface.blit(text, text_rect)
color = WHITE
if text2B:
color = RED
Font = pygame.font.SysFont('arial', 72)
text = Font.render('Graph Generator', True, color)
text_rect = text.get_rect()
text_rect.center = (W / 2, H * 2 / 5)
if text_rect.collidepoint(mouse_click):
output = 'GraphG()'
if text_rect.collidepoint(mouse_pos):
text2B = True
else:
text2B = False
Surface.blit(text, text_rect)
color = WHITE
if text3B:
color = RED
Font = pygame.font.SysFont('arial', 72)
text = Font.render('Exit', True, color)
text_rect = text.get_rect()
text_rect.center = (W / 2, H * 3 / 5)
if text_rect.collidepoint(mouse_click):
pygame.quit()
sys.exit()
if text_rect.collidepoint(mouse_pos):
text3B = True
else:
text3B = False
Surface.blit(text, text_rect)
Font = pygame.font.SysFont('arial', 72)
text = Font.render(output, True, BLUE)
text_rect = text.get_rect()
text_rect.center = (W / 2, H * 4 / 5)
Surface.blit(text, text_rect)
pygame.display.flip()
mainClock.tick(100000)
This line pygame.image.load('Pygame_Background.jpg') loads an image from your hard disk and turns it into a pygame.Surface object. Now you have this surface and try to call it like a function with parentheses behind it ((W, H), 0, 32) and that causes the TypeError, because a surface object isn't a function (it's not callable).
What you actually want to do is:
Create the display window (I call it screen here).
screen = pygame.display.set_mode((W, H), 0, 32)
Load the background image (better call it background because Surface is the name of the pygame.Surface class).
background = pygame.image.load('Pygame_Background.jpg').convert()
Also, call the pygame.Surface.convert or convert_alpha method to improve the performance.
Blit the background and everything else onto the screen in the main while loop.
screen.blit(background, (0, 0))
screen.blit(text, text_rect)
This looks like a name conflict. pygame.Surface is a module (or a sub module), but this line seems to overwrite this Surface object:
Surface = pygame.image.load('Pygame_Background.jpg')((W, H), 0, 32)
I would try to use different variable in the left hand side, for example surf_bg.
Try using the absolute path of the file of 'Pygame_Background.jpg'
Trace your directory starting from your Drive's letter