I have a question regarding the glyph a font and the pygame width of a font yesterday i some code regarding this 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"{glyph_rect[0]}")
print(f'letter {letter} bitmap height: {bh} glyph height: {gh} width = {glyph_rect[0].width} width_2 = {bw}')
win.fill((0, 0, 0))
win.blit(text_1, (0, 0))
pygame.display.update()
i += 1
run = i < len(text)
pygame.quit()
# win.blit(text_1, (0, 0))
I know why the glyph height is diffrent because i asked that in a question and i got a answer for that but why is the glyph width always a bit less than the actual width? Can someone explain me in detail
The width is different because there is a space between characters when they are connected to words.
glyph_rect[0].width is the width of the glyph. bw is the width of the bitmap. The glyph does not fill the entire bitmap and has some space on both sides.
You can investigate this with the following program (each letter is displayed for 1 second):
import pygame
pygame.init()
win = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
font = pygame.font.Font("freesansbold.ttf", 200)
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, (0, 0, 0))
text_draw = font.render(letter, True, (0, 0, 0), (255, 255, 255))
bw, bh = font.size(letter)
glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects()
if glyph_rect:
gh = glyph_rect[0].height
print(f"{glyph_rect[0]}")
print(f'letter {letter} bitmap height: {bh} glyph height: {gh} width = {glyph_rect[0].width} width_2 = {bw}')
win.fill((128, 128, 128))
win.blit(text_draw, (100, 100))
for r in glyph_rect:
draw_rect = r.move(100, 100)
pygame.draw.rect(win, (255, 0, 0), draw_rect, 3)
pygame.display.update()
i += 1
run = i < len(text)
clock.tick(1)
pygame.quit()
Related
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 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
I am rewriting my question
Pygame - rect disappears before I reach it because I think I have not described it clearly.
Full code:
#! /usr/bin/python
import pygame, time, sys, random, os
from pygame.locals import *
from time import gmtime, strftime
pygame.init()
w = 640
h = 400
screen = pygame.display.set_mode((w, h),RESIZABLE)
clock = pygame.time.Clock()
x = y = 100
def starting():
basicfont = pygame.font.SysFont(None, 48)
text = basicfont.render('Starting...', True, (255, 255, 255), (0, 0, 255))
textrect = text.get_rect()
textrect.centerx = screen.get_rect().centerx
textrect.centery = screen.get_rect().centery
screen.fill((0, 0, 255))
screen.blit(text, textrect)
pygame.display.update()
def taskbar():
basicfont = pygame.font.SysFont(None, 24)
pygame.draw.rect(screen, ((0, 255, 0)), (0, h-40, w, 40), 0)
text = basicfont.render(strftime("%Y-%m-%d", gmtime()), True, (0, 0, 0))
text2 = basicfont.render(strftime("%H:%M:%S", gmtime()), True, (0, 0, 0))
screen.blit(text, (w - 100, h - 37))
screen.blit(text2, (w - 100, h - 17))
logoimage = pygame.image.load("C:\Users\chef\Desktop\logo.png").convert()
logoimage = pygame.transform.scale(logoimage, (40, 40))
screen.blit(logoimage, (0, h-40),)
def start():
button1, button2, button3 = pygame.mouse.get_pressed()
mousex, mousey = pygame.mouse.get_pos()
if 0 < mousex < 40 and h-40 < mousey < h:
if button1:
pygame.draw.rect(screen, ((255, 0, 0)), (0, int(h/2), int(w/6), int(h/2)-40), 0)
pygame.display.update()
starting()
#time.sleep(2)
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type==VIDEORESIZE:
w = event.dict['size'][0]
h = event.dict['size'][1]
screen=pygame.display.set_mode(event.dict['size'],RESIZABLE)
screen.fill((255, 255, 255))
taskbar()
start()
pygame.display.update()
clock.tick(60)
Just like my original question, when I press logoimage.png and the red rectangle pops up, it shows for a second then immediately disappears. How can I make it so that the rect will stay until I press outside of it?
You should move the screen.fill((255, 255, 255)) out of the while loop. This is causing problems, because each time the program loops, it colors the display white, writing over whatever you had there before. One way to work around this is to move the screen.fill((255, 255, 255)) out of the loop and place it before the pygame.draw.rect function. This will color the screen before you place the rect, and will not continue to fill it. For instance:
if button1:
screen.fill((255, 255, 255))
pygame.draw.rect(screen, ((255, 0, 0)), (0, int(h/2), int(w/6), int(h/2)-40), 0)
You could also move it elsewhere or come up with a way to draw rect every loop after you fill the window.
Hope this helped.
You need to flag a boolean that it's been clicked. Something like:
btnClicked = false
def start():
button1, button2, button3 = pygame.mouse.get_pressed()
mousex, mousey = pygame.mouse.get_pos()
if button1:
if 0 < mousex < 40 and h-40 < mousey < h:
btnClicked = true
else:
btnClicked = false
if btnClicked:
pygame.draw.rect(screen, ((255, 0, 0)), (0, int(h/2), int(w/6), int(h/2)-40), 0)
pygame.display.update()
I have just added in some code I had tried out into my game as a scoreboard and I have encountered an error. Only my background will display. No text or anything will appear. Here is the code I am using:
class MenuScores(MenuClass):
def __init__(self, surface, engine):
MenuClass.__init__(self, surface)
self.MoonSurvival = engine
self.text = "Name"
def name():
size = 36
screen = pygame.display.set_mode((1280, 720))
name = ""
font = pygame.font.SysFont('data/fonts/score.ttf', size)
background = pygame.image.load('data/images/bg.jpg')
while True:
# readlines returns a list; having this in
# loop allows pygame to draw recently added
# string; no need to close and open a window
namefile = open('score.txt', 'r')
names = namefile.readlines()
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.unicode.isalpha():
if len(name) < 4:
if event.unicode == 3:
name += 0
else:
name += event.unicode
elif event.key == K_BACKSPACE:
name = name[:-1]
elif event.key == K_RETURN:
f = open("data/scores/score.txt", "a")
f.write(str(name) + " " + str(self.MoonSurvival.total_score) + "\n")
f.close()
name = ""
self.text = ""
elif event.type == QUIT:
return
# create a Rectangle container, where yours
# text variable will be drawn
# Rect(left, top, width, height)
textrect = Rect(0, 0, 100, size)
screen.fill((0, 0, 0))
for i in names:
# iterate through lines from text file (it is stored
# in names variable and it is a list)
# create text variable and draw it to textrect
text = font.render(i[:-1], True, (255,0,0), (0,0,0))
screen.blit(text, textrect)
# change y coordinate of textrect; in next iteration
# next line will appear below the previous line
textrect.centery += size
block = font.render(name, True, (255, 255, 255))
rect = block.get_rect()
rect.center = screen.get_rect().center
screen.blit(background, (0, 0))
screen.blit(block, rect)
pygame.display.update()
pygame.display.flip()
def draw(self):
self.renderText()
self.drawHint()
self.surface.blit(self.background, (0, 0))
# update surface
pygame.display.update()
def _handleEvents(self, event):
def pressed(key):
keys = pygame.key.get_pressed()
if keys[key]:
return True
else:
return False
if pressed(pygame.K_SPACE):
self.MoonSurvival.game_state = STATE_MENU
self.MoonSurvival.level = 1
def renderText(self):
# split texts at \n (newline)
texts = self.text.split('\n')
for i in range(len(texts)):
textSurface = self.menufont.render(texts[i], 0, (255, 0, 0))
textRect = textSurface.get_rect()
textRect.centerx = SCREEN_WIDTH / 2
textRect.centery = SCREEN_HEIGHT / 2 + i * self.menufont.size(texts[i])[1]
self.surface.blit(textSurface, textRect)
def drawHint(self):
textSurface = self.menufont.render('(Press SPACE to return to menu)', 0, (255, 0, 0))
textRect = textSurface.get_rect()
textRect.centerx = SCREEN_WIDTH / 2
textRect.centery = SCREEN_HEIGHT - 50
self.surface.blit(textSurface, textRect)
I really don't know how to fix this. The text appeared fine when it was on its own (the def name()) So why does it not work here?
self.renderText()
self.drawHint()
self.surface.blit(self.background, (0, 0))
Here, you are drawing the text on self.surface, then drawing the hint on self.surface, and then painting the background image on self.surface, over top everything else.
Instead, you should draw the background first, and everything else after it.
self.surface.blit(self.background, (0, 0))
self.renderText()
self.drawHint()