I have a program that needs to often write text on the screen on a textbox. Since I'm already using pygame for something else here and have a separate file for my functions, I thought about going about it this way. As it stands right now my code looks like this:
In the main file:
import mainengine as me, pygame
pygame.init()
pygame.font.init()
screenSize = width, height = 1280, 720
screen = pygame.display.set_mode(screenSize)
pygame.display.set_caption('test')
bg = pygame.image.load("test.jpg")
screen.blit(bg, [0, 0])
me.text('test', 'how do code')
pygame.display.flip()
Then in the secondary file:
def text(speaker, text):
import pygame
white = (255, 255, 255)
gray = (200, 200, 200)
Sfont = pygame.font.Font('freesansbold.ttf', 24)
Lfont = pygame.font.Font('freesansbold.ttf', 18)
tb = pygame.image.load("textbox.png")
tbRect = tb.get_rect()
tbRect = tbRect.move(20, 550)
screen.blit(tb, tbRect)
screen.blit(Sfont.render(speaker, True, gray), [tbRect.x + 10, tbRect.y + 10])
screen.blit(Lfont.render(text, True, white), [tbRect.x + 10, tbRect.y + 35])
This is the error I get when I run the main file:
Traceback (most recent call last):
File "C:\Users\Denis\Desktop\Stuff\Misc Misc\Python\Scripts\Game\Main\test.py", line 18, in <module>
me.text('test', 'how do code')
File "C:\Users\Denis\Desktop\Stuff\Misc Misc\Python\Scripts\Game\Main\mainengine.py", line 66, in text
screen.blit(tb, tbRect)
NameError: name 'screen' is not defined
Putting this in a function would make the code a lot cleaner, so It'd be nice if there was a way to pull it off, chances are its not exactly what I have here. I'm not exactly the best at software development.
Thanks in advance!
I recommend to add an argument for the target surface to the function:
def text(surf, speaker, text):
# [...]
Furthermore, do not load the image and do not recreate the pygame.font.Font, every time when a text is rendered. Create the objects once at initialization. Loading a pygame.Surface and creating a pygame.font.Font object are very time consuming operations:
import pygame
pygame.init()
Sfont = pygame.font.Font('freesansbold.ttf', 24)
Lfont = pygame.font.Font('freesansbold.ttf', 18)
tb = pygame.image.load("textbox.png")
def text(surf, speaker, text):
white = (255, 255, 255)
gray = (200, 200, 200)
tbRect = tb.get_rect(topleft = (20, 550))
surf.blit(tb, tbRect)
surf.blit(Sfont.render(speaker, True, gray), [tbRect.x + 10, tbRect.y + 10])
surf.blit(Lfont.render(text, True, white), [tbRect.x + 10, tbRect.y + 35])
Pass the target surface to the function. For instance:
me.text(screen, 'test', 'how do code')
Related
I'm making a game and I started working on the save and loading part it's supposed to change some text based on a value in the JSON file and when I try to import the JSON file by giu that I built I get this error
Traceback (most recent call last):
File "save_test.py", line 112, in <module>
object.process()
File "save_test.py", line 65, in process
self.onclickFunction()
File "save_test.py", line 83, in open_file
import_text()
File "save_test.py", line 91, in import_text
importedText = data_load(['Text']['text'])
TypeError: list indices must be integers or slices, not str
you will need a JSON file so that you can import it then you run the code
Code:
# Imports
import sys
import pygame
import tkinter as tk
from tkinter import filedialog
import json
root = tk.Tk()
root.withdraw()
# Configuration
pygame.init()
fps = 60
fpsClock = pygame.time.Clock()
width, height = 640, 480
screen = pygame.display.set_mode((width, height))
white = (255, 255, 255)
green = (0, 255, 0)
blue = (0, 0, 128)
font = pygame.font.SysFont('Arial', 40)
objects = []
importedText = "example text"
class Button():
def __init__(self, x, y, width, height, buttonText='Button', onclickFunction=None, onePress=False):
self.x = x
self.y = y
self.width = width
self.height = height
self.onclickFunction = onclickFunction
self.onePress = onePress
self.fillColors = {
'normal': '#ffffff',
'hover': '#666666',
'pressed': '#333333',
}
self.buttonSurface = pygame.Surface((self.width, self.height))
self.buttonRect = pygame.Rect(self.x, self.y, self.width, self.height)
self.buttonSurf = font.render(buttonText, True, (20, 20, 20))
self.alreadyPressed = False
objects.append(self)
def process(self):
mousePos = pygame.mouse.get_pos()
self.buttonSurface.fill(self.fillColors['normal'])
if self.buttonRect.collidepoint(mousePos):
self.buttonSurface.fill(self.fillColors['hover'])
if pygame.mouse.get_pressed(num_buttons=3)[0]:
self.buttonSurface.fill(self.fillColors['pressed'])
if self.onePress:
self.onclickFunction()
elif not self.alreadyPressed:
self.onclickFunction()
self.alreadyPressed = True
else:
self.alreadyPressed = False
self.buttonSurface.blit(self.buttonSurf, [
self.buttonRect.width/2 - self.buttonSurf.get_rect().width/2,
self.buttonRect.height/2 - self.buttonSurf.get_rect().height/2
])
screen.blit(self.buttonSurface, self.buttonRect)
def open_file():
global importedText
importedText = filedialog.askopenfilename(filetypes=(("Lil brute save file", "*.json"), ("All Files", "*.*")))
import_text()
def import_text():
global importedText
f = open(importedText)
data_load = json.load(f)
importedText = data_load(['Text']['text'])
# set the center of the rectangular object.
customButton = Button(30, 30, 400, 100, 'import',open_file)
# Game loop.
while True:
screen.fill((20, 20, 20))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
text = font.render(importedText, True, green, blue)
# create a rectangular object for the
# text surface object
textRect = text.get_rect()
textRect.center = (width // 2, height // 2)
for object in objects:
object.process()
screen.blit(text, textRect)
pygame.display.flip()
pygame.display.update()
fpsClock.tick(fps)
JSON file: {"Text": {"text": "import successful"}}
Your code is calling data_load as a function, but it is a dictionary. Change your import_text function to:
def import_text():
global importedText
f = open(importedText)
data_load = json.load(f)
importedText = data_load['Text']['text']
I'm aware of the fact that pygame's screen.blit is not meant to support multiple lines, however I can't figure out a work around. All of the other threads that ask this question just don't work with my code. How do I make this work?
I've tried to split response into two by using splitline() on DisplayRoom.prompt and then having the game just load two lines separately, but DisplayRoom.prompt.splitline() does not turn `DisplayRoom.prompt from a tuple to a list and only returns the value for it.
screen.fill(background_colour)
txt_surface = userfont.render(text, True, color)
screen.blit(txt_surface, (100, 800))
response = promptfont.render(DisplayRoom.prompt, True, color)
screen.blit(response, (80, 300))
pygame.display.flip()
clock.tick_busy_loop(60) # limit FPS
When I defined DisplayRoom.prompt, I expected \n to linebreak it but it doesn't work which is why I'm here.
It is not Surface.blit which doesn't support multiple lines. blit simply draw a Surface on another Surface, it doesn't care what the Surface contains.
It's pygame.Font.render which doesn't support multilines. The docs clearly say:
The text can only be a single line: newline characters are not rendered.
So I don't know what DisplayRoom.prompt is in your code, but if is not a string, is bound to fail: render raises a TypeError: text must be a unicode or bytes.
And if is a string with newlines, newlines are just not rendered.
You have to split the text and render each line separately.
In the following example I create a simple function blitlines which illustrates how you could do.
import sys
import pygame
def blitlines(surf, text, renderer, color, x, y):
h = renderer.get_height()
lines = text.split('\n')
for i, ll in enumerate(lines):
txt_surface = renderer.render(ll, True, color)
surf.blit(txt_surface, (x, y+(i*h)))
background_colour = (0, 0, 0)
textcolor = (255, 255, 255)
multitext = "Hello World!\nGoodbye World!\nI'm back World!"
pygame.init()
screen = pygame.display.set_mode((500, 500))
userfont = pygame.font.Font(None, 40)
screen.fill(background_colour)
blitlines(screen, multitext, userfont, textcolor, 100, 100)
pygame.display.flip()
#main loop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
This is the result on the screen.
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.
I currently took a snippet off the web that allows for 4 pictures to be taken with a camera. This works well.
I've then tried to take another snippet off the web that would count down before it takes a picture, this gave me great headache, and I'm wondering if someone much smarter can figer this out for me...
surface = pygame.display.set_mode((0,0))
fontObj = pygame.font.Font("freesansbold.ttf", 100)
textSurfaceObj = fontObj.render("3", True, (255, 0, 0))
textRectObj = textSurfaceObj.get_rect()
textRectObj.center = (surface.get_width() / 2, surface.get_height() / 2)
def show_image(image_path):
screen = init_pygame()
img=pygame.image.load(image_path)
img = pygame.transform.scale(img,(transform_x,transfrom_y))
screen.blit(img,(offset_x,offset_y))
pygame.display.flip()
def init_pygame():
pygame.init()
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
pygame.display.set_caption('Pictures')
pygame.mouse.set_visible(False) #hide the mouse cursor
return pygame.display.set_mode(size, pygame.FULLSCREEN)
print "Taking pics"
now = time.strftime("%Y-%m-%d-%H:%M:%S")
try:
for i, filename in enumerate(camera.capture_continuous(config.file_path + now + '-' + '{counter:02d}.jpg')):
print(filename)
for y in range(3,0,-1):
surface.fill((0,0,0,0))
textSurfaceObj = fontObj.render(str(y), True, (255, 0, 0))
surface.blit(textSurfaceObj, textRectObj)
pygame.display.update()
pygame.time.wait(1000)
sleep(capture_delay)
if i == total_pics-1:
break
finally:
camera.stop_preview()
camera.close()
It returns me this:
Traceback (most recent call last):
File "pics.py", line 58, in <module>
fontObj = pygame.font.Font("freesansbold.ttf", 100)
pygame.error: font not initialized
I was under the impression that if pygame.init() was done, fonts should be initialised?
That's because you invoke:
pygame.font.Font("freesansbold.ttf", 100)
before you call:
pygame.init()
probably in line 58 of your file pics.py.You've provided the answer to your question yourself. You might be smarter than you think.
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))