Pygame creating surfaces - python

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)
def taskbar():
basicfont = pygame.font.SysFont(None, 24)
taskbarrect = pygame.Rect((0, int(h-40)), (int(w), int(h)))
text = basicfont.render(strftime("%Y-%m-%d", gmtime()), True, (0, 0, 0))
text2 = basicfont.render(strftime("%H:%M:%S", gmtime()), True, (0, 0, 0))
taskbarrect.blit(text, (w - 100, h - 37))
taskbarrect.blit(text2, (w - 100, h - 17))
pygame.display.update()
starting()
screen.fill((255,255,255))
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)
taskbar()
Because I want to make my code run faster, I want to create surfaces and blit rects on top of them, so that I can do pygame.display.update(taskbarrect) and speed up my code. However, I don't know how to create multiple surfaces. I have tried taskbarrect=(xcoordinate, ycoordinate, width, length) then blitting an image or text or whatever, but trying it says tuple object has no attribute blit. Trying the method in the code (suggested by #elegent) gives 'pygame.Rect' object has no attribute 'blit'.
What am I doing wrong?

Pygame uses surfaces to represent any form of image. This could be either
your main screen Surface, which is returned by the pygame.display.set_mode() function
myDisplay = pygame.display.set_mode((500, 300))
or created object of the pygame.Surface class:
#create a new Surface
myNewSurface = Surface((500, 300))
#change its background color
myNewSurface.fill((55,155,255))
#blit myNewSurface onto the main screen at the position (0, 0)
myDisplay.blit(myNewSurface, (0, 0))
#update the screen to display the changes
display.update() #or display.flip()
Pygame's display.update() has a method that allows one to update only some portions of the screen by passing one object or a list of pygame.Rect objects to it. Therefore, we could also call:
myUpdateRect= pygame.Rect((500, 300), (0, 0))
display.update(myUpdateRect)
Anyway, I recommend to using the pygame.draw module to draw simple shapes like rectangles, circles and polygons onto a surface, because all of these functions return a rectangle representing the bounding area of changed pixels.
This enables you to pass this information to the update() function, so Pygame only updates the pixels of the just drawn shapes:
myDisplay = pygame.display.set_mode((500, 300))
myRect= pygame.Rect((100, 200), (50, 100))
pygame.display.update(pygame.draw.rect(myDisplay, (55, 155, 255), myRect))
Update:
def taskbar():
basicfont = pygame.font.SysFont(None, 24)
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.fill((55, 155, 255))
screen.blit(text, (w - 100, h - 37))
screen.blit(text2, (w - 100, h - 17))
pygame.display.update()
Hope this helps :)

Related

pygame.surfarray.pixels3d doesn’t give correct pixel values with a text surface

So, I was trying to get all the pixels occupied by some text, but I realised pygame.surfarray.pixels3d doesn’t give the expected output.
Here is what I tried:
import pygame
# import numpy as np
pygame.init()
width, height = 800, 800
win = pygame.display.set_mode((width, height))
font = pygame.font.SysFont("Monospace", 100, True)
text = font.render("test", True, (255, 255, 255))
# saves the correct image with text
pygame.image.save(text,"image.png")
# gives all (255,255,255)?!
pixels = pygame.surfarray.pixels3d(text).copy()
# remake surface from these pixels - doesn’t give the text.
text = pygame.surfarray.make_surface(pixels)
text_rect = text.get_rect(center=(width // 2, height // 2))
def main():
run = True
win.fill((0, 0, 0))
win.blit(text, text_rect)
pygame.display.update()
while run:
for e in pygame.event.get():
if e.type == pygame.QUIT:
run = False
break
if __name__ == "__main__":
main()
pygame.surfarray.pixels3d works perfectly for other surfaces, so I don’t understand if this is a bug in pygame or am I doing something wrong?
Thanks in advance.
EDIT:
I have also tried using surfarray.array3d, but no luck.
You only read the color channels of the Surface also has an alpha channel. Get the color channels (array3d or pixels3d) and the alpha channel (array_alpha or pixels_alpha) and stick them together. Recreate the Surface with pygame.image.frombuffer:
pixels_rgb = pygame.surfarray.array3d(text)
pixels_alpha = pygame.surfarray.array_alpha(text).reshape((*pixels_rgb.shape[0:2], 1))
pixels_rgba = np.concatenate((pixels_rgb, pixels_alpha), 2)
text = pygame.image.frombuffer(pixels_rgba.transpose((1, 0, 2)).copy(order='C'), text.get_size(), 'RGBA')
Alternatively you can create a text with an opaque black background. Set a black color key (set_colorkey) and convert it with convert_alpha:
text = font.render("test", True, (255, 255, 255), (0, 0, 0))
text.set_colorkey(0)
text = text.convert_alpha()
Complete code
import pygame
import numpy as np
pygame.init()
width, height = 800, 800
win = pygame.display.set_mode((width, height))
font = pygame.font.SysFont("Monospace", 100, True)
text1 = font.render("test 1", True, (255, 255, 255))
text2 = font.render("test 2", True, (255, 255, 255), (0, 0, 0))
text2.set_colorkey(0)
text2 = text2.convert_alpha()
pixels_rgb = pygame.surfarray.array3d(text1)
pixels_alpha = pygame.surfarray.array_alpha(text1).reshape((*pixels_rgb.shape[0:2], 1))
pixels_rgba = np.concatenate((pixels_rgb, pixels_alpha), 2)
text1 = pygame.image.frombuffer(pixels_rgba.transpose((1, 0, 2)).copy(order='C'), text1.get_size(), 'RGBA')
pixels = pygame.surfarray.pixels3d(text2).copy()
text2 = pygame.surfarray.make_surface(pixels)
text1_rect = text1.get_rect(center=(width // 2, height // 2 - 50))
text2_rect = text1.get_rect(center=(width // 2, height // 2 + 50))
def main():
run = True
win.fill((0, 0, 0))
win.blit(text1, text1_rect)
win.blit(text2, text2_rect)
pygame.display.update()
while run:
for e in pygame.event.get():
if e.type == pygame.QUIT:
run = False
if __name__ == "__main__":
main()

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.

Trouble adding text to screen in pygame

When I run this code, I get the error "ValueError: size needs to be (int width, int height)".
display_width = 800
display_height = 600
welcomeScreen = pygame.display.set_mode((display_width, display_height))
red = (255, 0, 0)
white = (255, 255, 255)
welcomeScreen.fill(white, rect = None, special_flags = 0)
welcomeMessageBackground = pygame.draw.rect(welcomeScreen, red, [200, 100, 400, 75])
welcomeFont = pygame.font.Font(None, 50)
welcomeMessage = welcomeFont.render('Welcome! Click a button to get started.', True, white)
pygame.Surface.blit(pygame.Surface(display_width, display_height), welcomeMessageBackground, area = None, special_flags = 0)
pygame.display.update()
The line causing problems is
pygame.Surface.blit(pygame.Surface(display_width, display_height), welcomeMessageBackground, area = None, special_flags = 0)
I've tried substituting the variables for the values 800 and 600, same error. What I want is for the text in welcomeMessage to appear over the rect welcomeMessageBackground. Any help is appreciated!
This line doesn't make sense.
pygame.Surface.blit(pygame.Surface(display_width, display_height), welcomeMessageBackground, area = None, special_flags = 0)
You shouldn't call pygame.Surface.blit() but the blit method of the surface onto which you want to blit the other surface, e.g.:
welcomeScreen.blit(welcomeMessage, (70, 100))
welcomeFont.render returns a surface which you can blit onto the screen or another surface.
To create a pygame.Surface instance, you have to pass a tuple for the width and height, pygame.Surface((100, 200)), but in your case you don't have to create another surface at all.
Also, if you want to add a background color behind the text, you can just pass the color to the render method of your font:
welcomeMessage = welcomeFont.render(
'Welcome! Click a button to get started.', True, white, red)
You need to use a tuple for the value:
pygame.Surface.blit(pygame.Surface((display_width, display_height)), welcomeMessageBackground, area = None, special_flags = 0)
So wrapping your display_width and display_height inside () should work. Check it out the documentation.

How to display text in pygame? [duplicate]

This question already has answers here:
pygame - How to display text with font & color?
(7 answers)
Closed 2 years ago.
I can't figure out to display text in pygame.
I know I can't use print like in regular Python IDLE but I don't know how.
import pygame, sys
from pygame.locals import *
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = ( 255, 0, 0)
pygame.init()
size = (700, 500)
screen = pygame.display.set_mode(size)
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('P.Earth')
while 1: # main game loop
for event in pygame.event.get():
if event.type == QUIT:
pygame.display.update()
import time
direction = ''
print('Welcome to Earth')
pygame.draw.rect(screen, RED, [55,500,10,5], 0)
time.sleep(1)
This is only the beginning part of the whole program.
If there is a format that will allow me to show the text I type in the pygame window that'd be great. So instead of using print I would use something else. But I don't know what that something else is.
When I run my program in pygame it doesn't show anything.
I want the program to run in the pygame window instead of it just running in idle.
You can create a surface with text on it. For this take a look at this short example:
pygame.font.init() # you have to call this at the start,
# if you want to use this module.
my_font = pygame.font.SysFont('Comic Sans MS', 30)
This creates a new object on which you can call the render method.
text_surface = my_font.render('Some Text', False, (0, 0, 0))
This creates a new surface with text already drawn onto it.
At the end you can just blit the text surface onto your main screen.
screen.blit(text_surface, (0,0))
Bear in mind, that every time the text changes, you have to recreate the surface again, to see the new text.
There's also the pygame.freetype module which is more modern, works with more fonts and offers additional functionality.
Create a font object with pygame.freetype.SysFont() or pygame.freetype.Font if the font is inside of your game directory.
You can render the text either with the render method similarly to the old pygame.font.Font.render or directly onto the target surface with render_to.
import pygame
import pygame.freetype # Import the freetype module.
pygame.init()
screen = pygame.display.set_mode((800, 600))
GAME_FONT = pygame.freetype.Font("your_font.ttf", 24)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((255,255,255))
# You can use `render` and then blit the text surface ...
text_surface, rect = GAME_FONT.render("Hello World!", (0, 0, 0))
screen.blit(text_surface, (40, 250))
# or just `render_to` the target surface.
GAME_FONT.render_to(screen, (40, 350), "Hello World!", (0, 0, 0))
pygame.display.flip()
pygame.quit()
When displaying I sometimes make a new file called Funk. This will have the font, size etc. This is the code for the class:
import pygame
def text_to_screen(screen, text, x, y, size = 50,
color = (200, 000, 000), font_type = 'data/fonts/orecrusherexpand.ttf'):
try:
text = str(text)
font = pygame.font.Font(font_type, size)
text = font.render(text, True, color)
screen.blit(text, (x, y))
except Exception, e:
print 'Font Error, saw it coming'
raise e
Then when that has been imported when I want to display text taht updates E.G score I do:
Funk.text_to_screen(screen, 'Text {0}'.format(score), xpos, ypos)
If it is just normal text that isn't being updated:
Funk.text_to_screen(screen, 'Text', xpos, ypos)
You may notice {0} on the first example. That is because when .format(whatever) is used that is what will be updated. If you have something like Score then target score you'd do {0} for score then {1} for target score then .format(score, targetscore)
This is slighly more OS independent way:
# do this init somewhere
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
font = pygame.font.Font(pygame.font.get_default_font(), 36)
# now print the text
text_surface = font.render('Hello world', antialias=True, color=(0, 0, 0))
screen.blit(text_surface, dest=(0,0))

I wrote a pygame program but when I try to use py2exe I don't get an exe in the dist

I wrote a program called Hello.py that looks like this:
import pygame, sys
from pygame.locals import *
# set up pygame
pygame.init()
# set up the window
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Hello world!')
# set up the colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# set up fonts
basicFont = pygame.font.SysFont(None, 48)
# set up the text
text = basicFont.render('Hello world!', True, WHITE, BLUE)
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
# draw the white background onto the surface
windowSurface.fill(WHITE)
# draw a green polygon onto the surface
pygame.draw.polygon(windowSurface, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)))
# draw some blue lines onto the surface
pygame.draw.line(windowSurface, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(windowSurface, BLUE, (120, 60), (60, 120))
pygame.draw.line(windowSurface, BLUE, (60, 120), (120, 120), 4)
# draw a blue circle onto the surface
pygame.draw.circle(windowSurface, BLUE, (300, 50), 20, 0)
# draw a red ellipse onto the surface
pygame.draw.ellipse(windowSurface, RED, (300, 250, 40, 80), 1)
# draw the text's background rectangle onto the surface
pygame.draw.rect(windowSurface, RED, (textRect.left - 20, textRect.top - 20, textRect.width + 40, textRect.height + 40))
# get a pixel array of the surface
pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = BLACK
del pixArray
# draw the text onto the surface
windowSurface.blit(text, textRect)
# draw the window onto the screen
pygame.display.update()
# run the game loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
And I have a program called setup.py that looks like this:
from distutils.core import setup
import py2exe
import pygame
setup(window=['Hello.py'])
when I run py2exe, it makes a dist folder but inside the folder there is no exe file, just the other files it creates. I hate to keep asking questions like this but I'm a little short for time and don't have time to play 20 error messages with my computer. I am currently running windows vista.
Pygame already has a default program for using pygame and py2exe, Pygame2exe.
Here is a link to it.
Just fill in the arguments in BuildExe.__init__ and run it. You don't even have to run it in console because it adds "py2exe" to the arguments.
Hope this answer helps!

Categories