Reputation: 220
I want to blit text onto a rectangle which can move and readjust its size. I was thinking on making the rectangle a surface and just blitting the text on the surface, but I do not know how to do so :(
The rectangle that can move around and be able to be resized is:
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
rect1 = pg.Rect(100, 100, 161, 100)
rect2 = pg.Rect(300, 200, 161, 100)
selected_rect = None
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
for rect in (rect1, rect2):
if rect.collidepoint(event.pos):
selected_rect = rect # Select the colliding rect.
elif event.type == pg.MOUSEBUTTONUP:
selected_rect = None # De-select the rect.
elif event.type == pg.MOUSEMOTION:
if selected_rect is not None: # If a rect is selected.
if event.buttons[0]: # Left mouse button is down.
# Move the rect.
selected_rect.x += event.rel[0]
selected_rect.y += event.rel[1]
else: # Right or middle mouse button.
# Scale the rect.
selected_rect.w += event.rel[0]
selected_rect.h += event.rel[1]
selected_rect.w = max(selected_rect.w, 10)
selected_rect.h = max(selected_rect.h, 10)
screen.fill((30, 30, 30))
pg.draw.rect(screen, (0, 100, 250), rect1)
pg.draw.rect(screen, (0, 200, 120), rect2)
pg.display.flip()
clock.tick(30)
Also if possible can anyone help me with the rectangles a bit, They seem to be able to move off the screen, how is it possible to make the screen size the border and make the rectangles bounce off of it?
Upvotes: 2
Views: 1436
Reputation: 20478
Here's a basic solution. I start by splitting the text into separate words. Then, to create the lines, I add one word after the other to an intermediary list (line
) and use the pygame.font.Font.size
method to get the size of the word which I add to the line_width
variable. When the line_width
exceeds the rectangle width, I use the words in the line list to render a text surface and append it to the self.images
list.
To blit the text surfaces, I enumerate the self.images
and then multiply the index by the height of the font to shift the surfaces.
import pygame as pg
class TextBox:
def __init__(self, text, pos, font, bg_color, text_color=(255, 255, 255)):
self.font = font
self.font_height = font.get_linesize()
self.text = text.split() # Single words.
self.rect = pg.Rect(pos, (200, 200))
self.bg_color = bg_color
self.text_color = text_color
self.render_text_surfaces()
def render_text_surfaces(self):
"""Create a new text images list when the rect gets scaled."""
self.images = [] # The text surfaces.
line_width = 0
line = []
space_width = self.font.size(' ')[0]
# Put the words one after the other into a list if they still
# fit on the same line, otherwise render the line and append
# the resulting surface to the self.images list.
for word in self.text:
line_width += self.font.size(word)[0] + space_width
# Render a line if the line width is greater than the rect width.
if line_width > self.rect.w:
surf = self.font.render(' '.join(line), True, self.text_color)
self.images.append(surf)
line = []
line_width = self.font.size(word)[0] + space_width
line.append(word)
# Need to render the last line as well.
surf = self.font.render(' '.join(line), True, self.text_color)
self.images.append(surf)
def draw(self, screen):
"""Draw the rect and the separate text images."""
pg.draw.rect(screen, self.bg_color, self.rect)
for y, surf in enumerate(self.images):
# Don't blit below the rect area.
if y * self.font_height + self.font_height > self.rect.h:
break
screen.blit(surf, (self.rect.x, self.rect.y+y*self.font_height))
def scale(self, rel):
self.rect.w += rel[0]
self.rect.h += rel[1]
self.rect.w = max(self.rect.w, 30) # 30 px is the minimum width.
self.rect.h = max(self.rect.h, 30)
self.render_text_surfaces()
def move(self, rel):
self.rect.move_ip(rel)
self.rect.clamp_ip(screen.get_rect())
text = """Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum."""
pg.init()
screen = pg.display.set_mode((800, 600))
clock = pg.time.Clock()
FONT = pg.font.Font(None, 34)
selected_box = None
textbox = TextBox(text, (50, 50), FONT, (20, 50, 120))
textbox2 = TextBox(text, (350, 100), pg.font.Font(None, 22), (20, 80, 60))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
for box in (textbox, textbox2):
if box.rect.collidepoint(event.pos):
selected_box = box # Select the colliding box.
elif event.type == pg.MOUSEBUTTONUP:
selected_box = None # De-select the box.
elif event.type == pg.MOUSEMOTION:
if selected_box is not None: # If a box is selected.
if event.buttons[0]: # Left mouse button is down.
selected_box.move(event.rel)
else:
selected_box.scale(event.rel)
screen.fill((30, 30, 30))
textbox.draw(screen)
textbox2.draw(screen)
pg.display.flip()
clock.tick(60)
There are still some things that need to be improved, but I leave that to you. For example:
render_text_surfaces
method to update the surfaces.Upvotes: 2
Reputation: 23024
I can recommend pygame-text which simplifies drawing text onto a surface.
Given a surface:
surface = pg.Surface((200, 100))
You can add text easily enough:
import ptext # Grab the module from Github
ptext.draw("hello world", (20, 100), surf=surface)
screen.get_surface().blit(surface, (10, 10))
Plenty more info on the above link.
Upvotes: 0