Multi line text input thumbnail

Getting Multi-Line Text Input In Pygame

How To Get Text Input From The Player

If you have tried making a game that requires the player to type their responses with the keyboard, you will have noticed that there are a few approaches to this.

  • You could use the KEYDOWN event and check for specific keys being pressed, but this detects every key, including the function and command keys, so you would need to filter those out.
  • key.get_pressed() is another option, but pygame’s own documentation warns against using this for input.
  • TEXTINPUT is an event that is similar to KEYDOWN but it filters out all the function keys and is the preferred approach.

Starter Code and Initial Setup

Using the event handler, which I covered in a previous tutorial, I add the TEXTINPUT event on lines 26 – 28 to create the starter code:

				
					import pygame

pygame.init()

#define screen size
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

#create game window
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Text Input")

#define colours
BG = (60, 114, 228)
TEXT_COL = (246, 247, 246)

#game loop
run = True
while run:
  
  #update background
  screen.fill(BG)

  #event handler
  for event in pygame.event.get():
    #handle text input
    if event.type == pygame.TEXTINPUT:
      print(event)

    #quit program
    if event.type == pygame.QUIT:
      run = False

  #update display
  pygame.display.flip()

pygame.quit()
				
			

Pressing the “d” key on the keyboard brings up this output in the terminal.

TEXTINPUT event

If you try pressing other keys that aren’t text or numerical input, you will notice that they don’t get recorded as an event. The TEXTINPUT event filters those out automatically.

The attribute that we need from that event is the ‘text‘ attribute. This can then be stored in a variable that we will use for tracking the text input.

Define a text variable before the game loop:

				
					#create empty string
text = ""
				
			

And then update the event handler code as below to add the text input onto that string, then print the contents of the string out.

				
					  #event handler
  for event in pygame.event.get():
    #handle text input
    if event.type == pygame.TEXTINPUT:
      text += event.text
      print(text)
				
			

Running this code should now give you a functional text input. However the text is being displayed in the terminal, and not the game screen.

Display Text Input On The Screen

We will use this helper function for displaying the text on the screen. If you need a refresher on how this works, check out my previous tutorial.

Then we add a font on lines 17 – 19, call this function in the main game loop, and remove the print statement from the event handler, since we’re displaying the text on the screen now.

The updated code below shows the changes highlighted.

				
					import pygame

pygame.init()

#define screen size
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

#create game window
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Text Input")

#define colours
BG = (60, 114, 228)
TEXT_COL = (246, 247, 246)

#define font
font_size = 60
font = pygame.font.SysFont("Futura", font_size)

#create empty string
text = ""

#function for outputting text onto the screen
def draw_text(text, font, text_col, x, y):
  img = font.render(text, True, text_col)
  width = img.get_width()
  screen.blit(img, (x - (width / 2), y))

#game loop
run = True
while run:
  
  #update background
  screen.fill(BG)

  #display typed input
  draw_text(text, font, TEXT_COL, SCREEN_WIDTH / 2, 200)

  #event handler
  for event in pygame.event.get():
    #handle text input
    if event.type == pygame.TEXTINPUT:
      text += event.text

    #quit program
    if event.type == pygame.QUIT:
      run = False

  #update display
  pygame.display.flip()

pygame.quit()
				
			

This code gets us 90% of the way there. You can now type and it will come up on the screen. It will automatically detect capital letters when you hold shift and even the space bar works.

Now let’s add some improvements to make it more functional.

Deleting Typed Text

Notice that backspace doesn’t work and it isn’t possible to delete the text once you have typed it.

Well, the backspace key isn’t picked up by TEXTINPUT, so we have to handle that separately using good old KEYDOWN.

This can be accomplished with the additional code below. Update your event handler with the highlighted code below and you should now be able to delete text.

				
					  #event handler
  for event in pygame.event.get():
    #handle text input
    if event.type == pygame.TEXTINPUT:
      text += event.text

    #handle special keys
    if event.type == pygame.KEYDOWN:
      if event.key == pygame.K_BACKSPACE:
        text = text[:-1]
				
			

At this point, the text input functionality is good enough to be used as-is.

But we can improve this further to add multi line text.

Multiline Text Input

Adding multiline text input only required a few small tweaks. Since each line will be a separate string, we will need a way of storing these strings together and also being able to track them as we delete text using the backspace key.

This will be achieved using a list, which will contain the strings. The final code is below, with changes highlighted.

				
					import pygame

pygame.init()

#define screen size
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

#create game window
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Text Input")

#define colours
BG = (60, 114, 228)
TEXT_COL = (246, 247, 246)

#define font
font_size = 60
font = pygame.font.SysFont("Futura", font_size)

#create empty string
text = [""]

#function for outputting text onto the screen
def draw_text(text, font, text_col, x, y):
  img = font.render(text, True, text_col)
  width = img.get_width()
  screen.blit(img, (x - (width / 2), y))

#game loop
run = True
while run:
  
  #update background
  screen.fill(BG)

  #display typed input
  for row, line in enumerate(text):
    draw_text(line, font, TEXT_COL, SCREEN_WIDTH / 2, 200 + (row * font_size))

  #event handler
  for event in pygame.event.get():
    #handle text input
    if event.type == pygame.TEXTINPUT:
      text[-1] += event.text

    #handle special keys
    if event.type == pygame.KEYDOWN:
      if event.key == pygame.K_BACKSPACE:
        text[-1] = text[-1][:-1]
        if len(text[-1]) == 0:
          if len(text) > 1:
            text = text[:-1]
      elif event.key == pygame.K_RETURN:
        text.append("")
    
    #quit program
    if event.type == pygame.QUIT:
      run = False

  #update display
  pygame.display.flip()

pygame.quit()
				
			

A few things changed there, which I’ll explain:

  • Line 22 is now a list containing an empty string.
  • Lines 37-39 show the updated code to display the typed input. We iterate through the list, pulling each string out as variable line. I also use enumerate to get variable row. This tracks the line numbers, which I can then use for the y coordinate to make sure each line is displayed below the others.
  • Line 45 is similar to before, but now we are working with a list rather than a single string. I pass [-1] because this gives me the last value in the list, i.e. the last string that is being typed.
  • Line 50 does the same thing as before but now using lists instead of a single string.
  • Lines 51 – 53 check if the string has been fully deleted, in which case it moves up to the previous string.
  • Lines 54 & 55 check for the RETURN key and add a new empty line to the list.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *