enter image description hereI would like to display transparent text on a surface that is sized based on the length of the text.
The problem is that the text has a black background even though "None" is specified as the background in the "render" command.
I tried to apply the solutions given for questions similar to mine but they didn't work.
I attach the code and thank you for any suggestions.
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800, 600))
screen.fill ((0,0,255))
# red square
surf1 = pygame.Surface((200, 200))
surf1.fill((255, 0, 0))
rect1 = surf1.get_rect()
rect1.topleft = (50, 50)
screen.blit(surf1, rect1)
# Play button
fnt = pygame.font.SysFont("Times New Roman", 27, bold=True)
btn_play = fnt.render("Play", True, (51, 26, 0), None)
btn_play_size = btn_play.get_size()
btn_play_surface = pygame.Surface(btn_play_size)
btn_play_surface.blit(btn_play, (0, 0))
rect_btn_play = pygame.Rect(380, 50, btn_play_size[0], btn_play_size[1])
screen.blit(btn_play_surface, (380, 50))
pygame.display.flip()
def events():
done = False
while not done:
for ev in pygame.event.get():
if ev.type == QUIT:
return "quit"
elif ev.type == MOUSEBUTTONDOWN:
click = ev.pos
if rect1.collidepoint(click):
return "Red squre"
elif rect_btn_play.collidepoint(click):
return "Play"
else:
print ("You clicked outside of the surfaces")
while True:
event = events()
print (event)
if event == "quit":
break
pygame.quit()
The problem is the surface you are placing the text on. If you want to keep the transparency in the formation of the text, you need to create a pygame.Surface object with an per pixel alpha format. Use the pygame.SRCALPHA flag:
btn_play_surface = pygame.Surface(btn_play_size)
btn_play_surface = pygame.Surface(btn_play_size, pygame.SRCALPHA)
Alternatively you can set the color key for the transparent color with set_colorkey:
btn_play_surface = pygame.Surface(btn_play_size)
btn_play_surface.set_colorkey((0, 0, 0))
Related
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?
So I'm working on an interactive version of the MNIST handwritten digit image classification project with pygame, where the user draws on the gui and based off of that, the model which I have already made will look at the screen and output a prediction as to what number the image contains. My problem is that I'm not sure what approach to take to make whatever is displayed on the gui into an input of sorts for my model to predict (an image is required as an input)
Here's the code that I made for the gui:
import pygame as pg
#I'm importing a function that I made with my model
#Takes an image input and spits out a prediction as to what the number displayed in the image should be
from MNIST_Classification_GUI import makePrediction
pg.init()
screen = pg.display.set_mode([800, 600])
pg.display.set_caption("Draw a Number")
radius = 10
black = (0, 0, 0)
isGoing = True
screen.fill((255, 255, 255))
last_pos = (0, 0)
def roundline(srf, color, start, end, radius=1):
dx = end[0]-start[0]
dy = end[1]-start[1]
distance = max(abs(dx), abs(dy))
for i in range(distance):
x = int( start[0]+float(i)/distance*dx)
y = int( start[1]+float(i)/distance*dy)
pg.draw.circle(srf, color, (x, y), radius)
#To be used for the popup text containing the prediction
pg.font.init()
myFont = pg.font.SysFont("Sans Serif", 10)
draw_on = False
while isGoing:
for event in pg.event.get():
if event.type == pg.QUIT:
isGoing = False
if event.type == pg.MOUSEBUTTONDOWN:
spot = event.pos
pg.draw.circle(screen, black, spot, radius)
draw_on = True
#This is the part where I want to somehow obtain an image from the gui
#So when the user stops drawing, the popup text appears with the prediction
if event.type == pg.MOUSEBUTTONUP:
draw_on = False
#The makePrediction takes an image input and returns the predicted value
prediction = makePrediction(screen)
textSurface = myFont.render(f"The number should be {prediction}", False, black)
screen.blit(textSurface, (0, 0))
if event.type == pg.MOUSEMOTION:
if draw_on:
pg.draw.circle(screen, black, event.pos, radius)
roundline(screen, black, event.pos, last_pos, radius)
last_pos = event.pos
pg.display.flip()
pg.quit()
The PyGame display (window) is associated to a pygame.Surface object. Use pygame.image.save() to store the content of a Surface to a bitmap. The file type is automatically determined by the file extension:
pygame.image.save(screen, "screenshot.png")
I want to blit text that is input by the user to the screen. Each time the user presses Return, the typed text should be blitted to the screen. For text input I use this [text_input module] (https://github.com/Nearoo/pygame-text-input).
Here is the code I came up with so far:
import pygame_textinput
import pygame
pygame.init()
# Set some parameters
duration = 5.0
time = pygame.time.get_ticks()/1000
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
yoffset = 5
# Function that positions user input rects on screen
def renderInput(text, xoffset, yoffset):
font = pygame.font.SysFont("arial", 20)
renderText = font.render(text, False, (0, 0, 0))
rectText = renderText.get_rect()
rectText = rectText.move((0 + xoffset), (screen.get_height()/2 + yoffset))
return renderText, rectText
# Fills the screen once at the beginning
screen.fill((225, 225, 225))
while (pygame.time.get_ticks()/1000) < time + duration:
# creat new text input object on every trial
textinput = pygame_textinput.TextInput()
while True:
# Fills the surface after each keypress
screen.fill((225, 225, 225))
# Check events
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
exit()
# Feed with events every frame
# This evaluates to True once Return is pressed
if textinput.update(events):
userInput = textinput.get_text()
yoffset += 20
break
# Blit surface onto the screen
screen.blit(textinput.get_surface(), (10, 10))
# Update screen
pygame.display.update()
clock.tick(30)
# Blits user input to screen each time "Return" is pressed
# First get input text and the rectangle of the text
text, textrect = renderInput(userInput, 5, yoffset)
# Then blit it to the screen
screen.blit(text, textrect)
pygame.display.update()
My problem is, that the blitting only works if I do not fill the screen after each keypress within the while-loop that handles the input. If I do that, then the text input, however, is not cleared after each time the user presses Return.
So is there a way to have both, redraw after each keypress and have the text displayed below after each time Return is pressed by the user.
Thanks.
If I understand you correctly, the text in the input field should be cleared and it should be blit in the main area of the screen. I'd assign the text to the user_input variable if the user presses enter and then create a new pygame_textinput.TextInput() instance to clear the input field.
I've tried to simplify your code, because the two while loops are a bit confusing and I'm not sure what their purpose is. There should usually be only one while loop in a game.
import pygame
import pygame_textinput
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 20)
textinput = pygame_textinput.TextInput()
user_input = ''
done = False
while not done:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
done = True
if textinput.update(events):
user_input = textinput.get_text()
textinput = pygame_textinput.TextInput()
# Draw everything.
screen.fill((225, 225, 225))
screen.blit(textinput.get_surface(), (10, 10))
user_input_surface = font.render(user_input, True, (30, 80, 100))
screen.blit(user_input_surface, (10, 50))
pygame.display.update()
clock.tick(30)
pygame.quit()
Edit: In this version I append the rendered text surfaces to a list and blit them with an offset.
import pygame
import pygame_textinput
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 20)
textinput = pygame_textinput.TextInput()
user_inputs = []
done = False
while not done:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
done = True
if textinput.update(events):
user_inputs.append(
font.render(textinput.get_text(), True, (30, 80, 100)))
textinput = pygame_textinput.TextInput()
screen.fill((225, 225, 225))
screen.blit(textinput.get_surface(), (10, 10))
for y, text_surf in enumerate(user_inputs):
screen.blit(text_surf, (10, 50+30*y))
pygame.display.update()
clock.tick(30)
pygame.quit()
Edit2: To get a table, you can use modulo for the row offset and floor division for the column offset. The problem with this example is that the text surfaces can overlap if they are too wide.
for n, text_surf in enumerate(user_inputs):
# 5 rows. Offset = 30 pixels.
y_pos = 50 + (n%5) * 30
# After 5 rows add a new column. Offset = 100 pixels.
x_pos = 10 + n // 5 * 100
screen.blit(text_surf, (x_pos, y_pos))
I have edited my code containing your suggestions. Thanks a lot, this really seems to solve my problem. Here is the current version including a timer:
import pygame_textinput
import pygame
pygame.init()
# Set some parameters
duration = 5.0
time = pygame.time.get_ticks()/1000
xoffset = 5
yoffset = 5
screen = pygame.display.set_mode((400, 400))
font = pygame.font.SysFont("arial", 20)
clock = pygame.time.Clock()
# Creates textinput instance and an empty list to store inputs
textinput = pygame_textinput.TextInput()
userInputs = []
# Fills the screen once at the beginning
screen.fill((225, 225, 225))
while (pygame.time.get_ticks()/1000) < time + duration:
# Check events
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
exit()
# Feed with events every frame
# This evaluates to True once Return is pressed
if textinput.update(events):
userInputs.append(font.render(textinput.get_text(), True, (30, 80, 100)))
textinput = pygame_textinput.TextInput()
# Fill screen
screen.fill((225, 225, 225))
# Blit its surface onto the screen
screen.blit(textinput.get_surface(), (screen.get_rect().centerx, screen.get_rect().height/5))
for y, text_surf in enumerate(userInputs):
screen.blit(text_surf, (10, (screen.get_rect().height/4)+30*y))
# Update screen
pygame.display.update()
clock.tick(30)
I do not want to bother you to much, but now I have one more issue left that I am having trouble solving. Is it possible to render the text inputs in a second column once it exits the bottom border of the screen? So for example, if the user types a lot of words, that do not fit under each other, is it possible to move the next text input to the right and make it start next to the first input (create a second column so to speak). Thanks for your help so far, I really apreciatie it.
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))
So I am trying to do some very basic things in pygame. It is my first few days using it so I am a beginner. I am trying to change the color of something whenever I press it with the mouse. I know how to change the colors by timing which is what I have my code down below. I am trying to change the color of the cloud in my code below, if you run it you will see the cloud is the top left and I have it changing between white and black every three seconds but I want it to change based on mousebuttondown. Thanks
import pygame, time, sys
from pygame.locals import *
def drawItem(windowSurface, x, y):
pygame.draw.polygon(windowSurface, RED, ((0+x, 100+y),(100+x, 100+y), (50+x, 50+y)))
pygame.draw.polygon(windowSurface, GREEN, ((0+x,100+y),(100+x,100+y),(100+x,200+y),(0+x,200+y)))
pygame.init()
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption("Lab 9")
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
GRASS = (26, 82, 26)
SKY = (179,237,255)
color = SKY
flag = False
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
windowSurface.fill(SKY)
drawItem(windowSurface,200,120)
pygame.draw.rect(windowSurface, GRASS, (0,300,500,500),0)
house = ((0+50, 100+50),(100+50, 100+50), (50+50, 50+50), (50+100, 50+100))
for i in range(3):
pygame.draw.circle(windowSurface,color, house[i], 80)
if flag == False:
color = WHITE
flag = True
elif flag == True:
color = BLACK
flag = False
pygame.display.update()
time.sleep(3)
You've already discovered how to test for the type of an event (checking whether event.type == QUIT).
You can extend this to check whether it's a mouse button click. Stick this in your for event in pygame.event.get() loop:
if event.type == MOUSEBUTTONDOWN:
flag = not flag # This will swap the value of the flag
Then get rid of the flag = True and flag = False lines below, since you don't need them anymore. Also get rid of the time.sleep() call; or at least change it to a reasonable frame rate (like time.sleep(0.2) = 50 frames per second).