imarevic
imarevic

Reputation: 59

Blit user text input to screen

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.

Upvotes: 1

Views: 461

Answers (2)

skrx
skrx

Reputation: 20438

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))

Upvotes: 1

imarevic
imarevic

Reputation: 59

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.

Upvotes: 1

Related Questions