Related
I want to get some text input from the user in Python and display what they are typing in a text box, and when they press enter, it gets stored in a string.
I've looked everywhere, but I just can't find anything. I'm using Pygame.
You can define a rect as the area of the input box. If a pygame.MOUSEBUTTONDOWN event occurs, use the colliderect method of the input_box rect to check if it collides with the event.pos and then activate it by setting a active variable to True.
If the box is active you can type something and Pygame will generate pygame.KEYDOWN events which have a unicode attribute that you can simply add to a string, e.g. text += event.unicode. If the user presses enter, you can do something with the text string (in the example I just print it) and reset it to ''.
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
font = pg.font.Font(None, 32)
clock = pg.time.Clock()
input_box = pg.Rect(100, 100, 140, 32)
color_inactive = pg.Color('lightskyblue3')
color_active = pg.Color('dodgerblue2')
color = color_inactive
active = False
text = ''
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if input_box.collidepoint(event.pos):
# Toggle the active variable.
active = not active
else:
active = False
# Change the current color of the input box.
color = color_active if active else color_inactive
if event.type == pg.KEYDOWN:
if active:
if event.key == pg.K_RETURN:
print(text)
text = ''
elif event.key == pg.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
screen.fill((30, 30, 30))
# Render the current text.
txt_surface = font.render(text, True, color)
# Resize the box if the text is too long.
width = max(200, txt_surface.get_width()+10)
input_box.w = width
# Blit the text.
screen.blit(txt_surface, (input_box.x+5, input_box.y+5))
# Blit the input_box rect.
pg.draw.rect(screen, color, input_box, 2)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Here's an object-oriented variant that allows you to easily create multiple input boxes:
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
COLOR_INACTIVE = pg.Color('lightskyblue3')
COLOR_ACTIVE = pg.Color('dodgerblue2')
FONT = pg.font.Font(None, 32)
class InputBox:
def __init__(self, x, y, w, h, text=''):
self.rect = pg.Rect(x, y, w, h)
self.color = COLOR_INACTIVE
self.text = text
self.txt_surface = FONT.render(text, True, self.color)
self.active = False
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if self.rect.collidepoint(event.pos):
# Toggle the active variable.
self.active = not self.active
else:
self.active = False
# Change the current color of the input box.
self.color = COLOR_ACTIVE if self.active else COLOR_INACTIVE
if event.type == pg.KEYDOWN:
if self.active:
if event.key == pg.K_RETURN:
print(self.text)
self.text = ''
elif event.key == pg.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
# Re-render the text.
self.txt_surface = FONT.render(self.text, True, self.color)
def update(self):
# Resize the box if the text is too long.
width = max(200, self.txt_surface.get_width()+10)
self.rect.w = width
def draw(self, screen):
# Blit the text.
screen.blit(self.txt_surface, (self.rect.x+5, self.rect.y+5))
# Blit the rect.
pg.draw.rect(screen, self.color, self.rect, 2)
def main():
clock = pg.time.Clock()
input_box1 = InputBox(100, 100, 140, 32)
input_box2 = InputBox(100, 300, 140, 32)
input_boxes = [input_box1, input_box2]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for box in input_boxes:
box.handle_event(event)
for box in input_boxes:
box.update()
screen.fill((30, 30, 30))
for box in input_boxes:
box.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
There are also third party modules available like pygame_textinput.
Use the KEYDOWN event to get the input from the keyboard (see pygame.event). The key that was pressed can be obtained from the key attribute of the pygame.event.Event object. unicode contains a single character string that is the fully translated character. Add the character to the text when a key is pressed.
Two special keys need to be dealt with. If RETURN is pressed, the input is finished. If BACKSPACE is pressed, the last character of the input text must be removed:
repl.it/#Rabbid76/PyGame-TextInput
import pygame
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text = ""
input_active = True
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
input_active = True
text = ""
elif event.type == pygame.KEYDOWN and input_active:
if event.key == pygame.K_RETURN:
input_active = False
elif event.key == pygame.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
window.fill(0)
text_surf = font.render(text, True, (255, 0, 0))
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Use the algorithm in a pygame.sprite.Sprite class. Handle the event in the update method.Determine whether the mouse clicks in the text entry field with collidepoint (see How to detect when a rectangular object, image or sprite is clicked) and activate the text input box:
class TextInputBox(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
Pass the list of events to the update method of the Group that contains the Sprite:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
Minimal example: repl.it/#Rabbid76/PyGame-SpriteTextInput
import pygame
class TextInputBox(pygame.sprite.Sprite):
def __init__(self, x, y, w, font):
super().__init__()
self.color = (255, 255, 255)
self.backcolor = None
self.pos = (x, y)
self.width = w
self.font = font
self.active = False
self.text = ""
self.render_text()
def render_text(self):
t_surf = self.font.render(self.text, True, self.color, self.backcolor)
self.image = pygame.Surface((max(self.width, t_surf.get_width()+10), t_surf.get_height()+10), pygame.SRCALPHA)
if self.backcolor:
self.image.fill(self.backcolor)
self.image.blit(t_surf, (5, 5))
pygame.draw.rect(self.image, self.color, self.image.get_rect().inflate(-2, -2), 2)
self.rect = self.image.get_rect(topleft = self.pos)
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text_input_box = TextInputBox(50, 50, 400, font)
group = pygame.sprite.Group(text_input_box)
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
You can find a great module for Pygame text input here.
I have been using it for a while and I really like it. A tutorial how to use it is included in the description.
However, I have added the possibility to draw a (coloured) rectangle around the text, by adding a rect and a rect_color parameter to the *_init_() function and adding
if self.rect != None:
pygame.draw.rect(screen, self.rect_color, self.rect) #screen is my pygame display surface
to the update(self, events) function.
The pygame_gui module allows you to create a text_input box from the user by creating a UITextEntryLine instance. You'll need to set up an instance as in the quick start guide.
Create the text_input:
from pygame.rect import Rect
from pygame_gui.elements.ui_text_entry_line import UITextEntryLine
text_input = UITextEntryLine(relative_rect=Rect(0, 0, 100, 100), manager=manager)
Get the text if enter is clicked:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
if event.user_type == pygame_gui.UI_TEXT_ENTRY_FINISHED:
if event.ui_element == text_input:
entered_text = event.text
I have written a class that can handle text input
minimal example:
import pygame as pg
from pgtextbox import pgtextbox
pg.init()
screen=pg.display.set_mode((1000,500))
textbox=pgtextbox(200,20)
textbox.insertAtCurser('Hallo')
while True:
e = pg.event.wait(30000)
if e.type == pg.QUIT:
raise StopIteration
textbox.addPgEvent(e)#uses keydown events
print(textbox.text)
screen.fill((0,0,0))
screen.blit(textbox.render(),(10,0))
pg.display.flip()
pg.display.quit()
pgtextbox class:
import pygame as pg
class pgtextbox:#By K1521
def __init__(self,width=100,height=10,fontname=None):
self.surface=pg.Surface((width,height))
self.text=""
self.width=width
self.height=height
self.font=pg.font.Font(fontname,pgtextbox.getMaxFontSize(fontname,lineheight=height))
self.curserindex=0
self.cursersurface=pg.Surface((self.font.size("|")[0]//2,self.font.size("|")[1]))
self.cursersurface.fill((255,255,255))
#self.cursersurface=self.font.render("|",False,(255,255,255),(0,0,0))
self.offsety=int((height-self.font.get_linesize())/2)
self.offsetx=0
def curserpos(self):
return self.font.size(self.text[:self.curserindex])[0]
def addPgEvent(self,event):
if event.type==pg.KEYDOWN:
if event.key==pg.K_BACKSPACE:
self.deleteAtCurser()
elif event.key==pg.K_RIGHT:
self.offsetCurser(1)
elif event.key==pg.K_LEFT:
self.offsetCurser(-1)
else:
self.insertAtCurser(event.unicode)
def render(self):
self.surface.fill((0,0,0))
width=self.width-self.cursersurface.get_width()
text=self.font.render(self.text,False,(255,255,255),(0,0,0))
if self.curserindex>=0:
curserpos=self.curserpos()+self.offsetx
curserposnew=max(0,min(curserpos,width))
self.offsetx+=curserposnew-curserpos
curserpos=curserposnew
#if curserpos<0:
#self.offsetx-=curserpos
#curserpos=0
#if curserpos>width:
#curserpos=curserpos-width
#self.offsetx-=curserpos
else:
#self.offsetx=min(width-text.get_width(),0)
self.offsetx=0
self.surface.blit(text,(self.offsetx,self.offsety))
if self.curserindex>=0:
self.surface.blit(self.cursersurface,(curserpos,self.offsety))
#print((curserpos,self.offsety))
return self.surface
def insertAtCurser(self,t):
if self.curserindex<0:
self.curserindex=len(self.text)
self.text=self.text[:self.curserindex]+t+self.text[self.curserindex:]
self.curserindex+=len(t)
def deleteAtCurser(self,length=1):
if self.curserindex<0:
self.curserindex=len(self.text)
newcurserindex=max(0,self.curserindex-length)
self.text=self.text[:newcurserindex]+self.text[self.curserindex:]
self.curserindex=newcurserindex
def offsetCurser(self,i):
self.curserindex=max(min(self.curserindex+i,len(self.text)),0)
#staticmethod
def longestline(self,fontname,lines):
size=pg.font.Font(fontname,1000)
return max(lines,key=lambda t:size(t)[0])
#staticmethod
def getMaxFontSize(fontname,width=None,lineheight=None,line=None):
def font(size):
return pg.font.Font(fontname,size)
fontsize=float("inf")# inf
if width:
aproxsize=width*1000//font(1000).size(line)[0]
while font(aproxsize).size(line)[0]<width:
aproxsize+=1
while font(aproxsize).size(line)[0]>width:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
if lineheight:
aproxsize=lineheight*4//3
while font(aproxsize).get_linesize()<lineheight:
aproxsize+=1
while font(aproxsize).get_linesize()>lineheight:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
return fontsize
#staticmethod
def rendermultilinetext(text,width=None,height=10,fontname=None,antialias=False,color=(255,255,255),background=None):
if(len(text)-text.count("\n")==0):
return pg.Surface((0,0))
def font(size):
return pg.font.Font(fontname,size)
text=text.split("\n")
fontsize=1000000000# inf
longestline=None
if height:
longestline=pgtextbox.longestline(fontname,lines)
fontsize=pgtextbox.getMaxFontSize(fontname,width,lineheight,longestline)
font=font(fontsize)
width=font.size(longestline)[0]
lineheight=font.get_linesize()
heigth=len(text)*lineheight
textsurface=pg.Surface((width,heigth))
if background:
textsurface.fill(background)
for i,line in enumerate(text):
textsurface.blit(font.render(line,antialias,color,background),(0,i*lineheight))
return textsurface
So first I made a button class:
class Button:
def __init__(self, text, color_idle, y, function=''):
# General
self.pressed = False
self.function_finished = False
self.function = function
#Rectangle
self.button1 = pygame.Rect(0, y, 280, 24)
self.color = color_idle
# Text
font1 = pygame.font.Font(None, 30)
self.text_surface = font1.render(text, True, WHITE)
self.text_rect = self.text_surface.get_rect(center=self.button1.center)
def draw(self):
# Drawing The Button
pygame.draw.rect(window, self.color, self.button1, border_radius=3)
# Drawing The Text
window.blit(self.text_surface, self.text_rect)
self.click()
self.scrool()
def click(self):
mouse_pos = pygame.mouse.get_pos()
if self.button1.collidepoint(mouse_pos):
if pygame.mouse.get_pressed()[0]:
self.pressed = True
if pygame.mouse.get_pressed()[2]:
self.pressed = False
if self.pressed == True:
self.color = LIGHT_BLUE_PRESSED
# Button Function
if self.function != '':
if self.function == 'unlock_every_character' and self.function_finished == False:
unlock_every_character()
self.function_finished = True
if self.function == 'unlock_every_item' and self.function_finished == False:
unlock_every_item()
self.function_finished = True
if self.function == 'unlock_every_animal' and self.function_finished == False:
unlock_every_animal()
self.function_finished = True
if self.pressed == False:
self.color = LIGHT_BLUE_IDLE
if self.button1.collidepoint(mouse_pos):
self.color = LIGHT_BLUE_HOVER
def scrool(self):
for event in pygame.event.get():
if event.button == 4: # SCROLL UP
screen_level += 10
if event.button == 5: # SCROLL DOWN
screen_level -= 10
The button changes color when you hover over it with a mouse or press it.
To create a button object you have to specify the button text, color (I want to have different colored buttons), the y position and the function that it activates.
button1 = Button('Text1', LIGHT_BLUE_IDLE, 2, 'unlock_every_character')
And i want to be able to scroll, so i did something like this:
while True:
for event in pygame.event.get():
if event.button == 4: # SCROLL UP
screen_level += 10
if event.button == 5: # SCROLL DOWN
screen_level -= 10
And so i add the screen_level to the button object y position:
screen_level = 0
button1 = Button('Text1', LIGHT_BLUE_IDLE, 2 + screen_level, 'unlock_every_character')
But it won't work, because i have to specify the screen_level before the loop so i can add it to the button object witch is also created before the loop. So for the scroll to work i have to create my button inside the loop, after the event.button check and then the scrolling works, but the color changing brakes, because i set the button color (LIGHT_BLUE_IDLE) while creating the button, and so because i now create my button in the loop i create it 60 times every second and so i set it's color to LIGHT_BLUE_IDLE 60 times per second.
Please help
To answer the question, only pygame.MOUSEBUTTONDOWN events have button attribute, so you need to check for that before you check for button events.
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
Apart from that, there are a few problems. First is this:
if self.button1.collidepoint(mouse_pos):
if pygame.mouse.get_pressed()[0]:
self.pressed = True
if pygame.mouse.get_pressed()[2]: #<- this
self.pressed = False
pygame.mouse.get_pressed()[2] checks for right mouse button, so your button isn't reusable until you press it.
Second is this:
def scrool(self):
for event in pygame.event.get():
You can only call pygame.event.get() once in your application, otherwise it will cause problems. Calling it in scroll function means that you cannot call it anywhere else, thus you don't have access to events anywhere else in your code.
You are also not updating the text surface position every frame which means only the button's rectangle will move and not the text.
Also, you don't have to compare every function as a string because you can directly pass the function itself.
This is just about coding practice but a function should be responsible for one job.
def draw(self):
# Drawing The Button
pygame.draw.rect(window, self.color, self.button1, border_radius=3)
# Drawing The Text
window.blit(self.text_surface, self.text_rect)
self.click()
self.scrool()
It should not be doing that. A better way to handle this is to make a run function to call all of the button functions.
One last thing, it is considered better practice and more "pythonic" to check for true and false like this:
if self.pressed == True: #no
if self.pressed: #yes
if self.pressed == False: #no
if not self.pressed: #yes
Finally, instead of trying use if statements and pygame.mouse.get_pressed() to make the button execute the function once on click, you can use events for checking for mouse press and get rid of the if statements.
Here is the code with the above mentioned improvements.
import pygame
pygame.init()
window = pygame.display.set_mode((400,350))
pygame.display.set_caption('caption')
def unlockEveryCharacter():
print("'unlock_every_character'")
def unlockEveryItem():
print("'unlock_every_item'")
def unlockEveryAnimal():
print("'unlock_every_animal'")
class Button:
def __init__(self, text, color_idle, y, function=None):
# General
self.function = function
#Rectangle
self.button1 = pygame.Rect(0, y, 280, 24)
self.color = color_idle
# Text
font1 = pygame.font.Font(None, 30)
self.text_surface = font1.render(text, True, (255, 255, 255))
self.text_rect = self.text_surface.get_rect(center=self.button1.center)
def draw(self):
# Drawing The Button
pygame.draw.rect(window, self.color, self.button1, border_radius=3)
# Drawing The Text
self.text_rect = self.button1
window.blit(self.text_surface, self.text_rect)
def click(self, events):
pressed = False
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN:
if (event.button == 1):
if self.button1.collidepoint(event.pos):
pressed = True
if pressed:
self.color = (200, 200, 255)
# Button Function
if self.function is not None:
self.function()
if not pressed:
self.color = (100, 100, 200)
if self.button1.collidepoint(pygame.mouse.get_pos()):
self.color = (50, 50, 200)
def scrool(self, events):
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
self.button1.y -= 10
if event.button == 5: # SCROLL DOWN
self.button1.y += 10
def run(self, events):
self.draw()
self.click(events)
self.scrool(events)
unlockEveryCharacterButton = Button("Unlock every character", (255, 255, 255), 10, unlockEveryCharacter)
unlockEveryItemButton = Button("Unlock every item", (255, 255, 255), 50, unlockEveryItem)
unlockEveryAnimalButton = Button("Unlock every animal", (255, 255, 255), 90, unlockEveryAnimal)
buttons = [unlockEveryCharacterButton, unlockEveryItemButton, unlockEveryAnimalButton]
running = True
while running:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
running = False
window.fill((190, 232, 220))
for button in buttons:
button.run(events)
pygame.display.update()
pygame.quit()
I want to get some text input from the user in Python and display what they are typing in a text box, and when they press enter, it gets stored in a string.
I've looked everywhere, but I just can't find anything. I'm using Pygame.
You can define a rect as the area of the input box. If a pygame.MOUSEBUTTONDOWN event occurs, use the colliderect method of the input_box rect to check if it collides with the event.pos and then activate it by setting a active variable to True.
If the box is active you can type something and Pygame will generate pygame.KEYDOWN events which have a unicode attribute that you can simply add to a string, e.g. text += event.unicode. If the user presses enter, you can do something with the text string (in the example I just print it) and reset it to ''.
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
font = pg.font.Font(None, 32)
clock = pg.time.Clock()
input_box = pg.Rect(100, 100, 140, 32)
color_inactive = pg.Color('lightskyblue3')
color_active = pg.Color('dodgerblue2')
color = color_inactive
active = False
text = ''
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if input_box.collidepoint(event.pos):
# Toggle the active variable.
active = not active
else:
active = False
# Change the current color of the input box.
color = color_active if active else color_inactive
if event.type == pg.KEYDOWN:
if active:
if event.key == pg.K_RETURN:
print(text)
text = ''
elif event.key == pg.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
screen.fill((30, 30, 30))
# Render the current text.
txt_surface = font.render(text, True, color)
# Resize the box if the text is too long.
width = max(200, txt_surface.get_width()+10)
input_box.w = width
# Blit the text.
screen.blit(txt_surface, (input_box.x+5, input_box.y+5))
# Blit the input_box rect.
pg.draw.rect(screen, color, input_box, 2)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Here's an object-oriented variant that allows you to easily create multiple input boxes:
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
COLOR_INACTIVE = pg.Color('lightskyblue3')
COLOR_ACTIVE = pg.Color('dodgerblue2')
FONT = pg.font.Font(None, 32)
class InputBox:
def __init__(self, x, y, w, h, text=''):
self.rect = pg.Rect(x, y, w, h)
self.color = COLOR_INACTIVE
self.text = text
self.txt_surface = FONT.render(text, True, self.color)
self.active = False
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if self.rect.collidepoint(event.pos):
# Toggle the active variable.
self.active = not self.active
else:
self.active = False
# Change the current color of the input box.
self.color = COLOR_ACTIVE if self.active else COLOR_INACTIVE
if event.type == pg.KEYDOWN:
if self.active:
if event.key == pg.K_RETURN:
print(self.text)
self.text = ''
elif event.key == pg.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
# Re-render the text.
self.txt_surface = FONT.render(self.text, True, self.color)
def update(self):
# Resize the box if the text is too long.
width = max(200, self.txt_surface.get_width()+10)
self.rect.w = width
def draw(self, screen):
# Blit the text.
screen.blit(self.txt_surface, (self.rect.x+5, self.rect.y+5))
# Blit the rect.
pg.draw.rect(screen, self.color, self.rect, 2)
def main():
clock = pg.time.Clock()
input_box1 = InputBox(100, 100, 140, 32)
input_box2 = InputBox(100, 300, 140, 32)
input_boxes = [input_box1, input_box2]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for box in input_boxes:
box.handle_event(event)
for box in input_boxes:
box.update()
screen.fill((30, 30, 30))
for box in input_boxes:
box.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
There are also third party modules available like pygame_textinput.
Use the KEYDOWN event to get the input from the keyboard (see pygame.event). The key that was pressed can be obtained from the key attribute of the pygame.event.Event object. unicode contains a single character string that is the fully translated character. Add the character to the text when a key is pressed.
Two special keys need to be dealt with. If RETURN is pressed, the input is finished. If BACKSPACE is pressed, the last character of the input text must be removed:
repl.it/#Rabbid76/PyGame-TextInput
import pygame
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text = ""
input_active = True
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
input_active = True
text = ""
elif event.type == pygame.KEYDOWN and input_active:
if event.key == pygame.K_RETURN:
input_active = False
elif event.key == pygame.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
window.fill(0)
text_surf = font.render(text, True, (255, 0, 0))
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Use the algorithm in a pygame.sprite.Sprite class. Handle the event in the update method.Determine whether the mouse clicks in the text entry field with collidepoint (see How to detect when a rectangular object, image or sprite is clicked) and activate the text input box:
class TextInputBox(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
Pass the list of events to the update method of the Group that contains the Sprite:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
Minimal example: repl.it/#Rabbid76/PyGame-SpriteTextInput
import pygame
class TextInputBox(pygame.sprite.Sprite):
def __init__(self, x, y, w, font):
super().__init__()
self.color = (255, 255, 255)
self.backcolor = None
self.pos = (x, y)
self.width = w
self.font = font
self.active = False
self.text = ""
self.render_text()
def render_text(self):
t_surf = self.font.render(self.text, True, self.color, self.backcolor)
self.image = pygame.Surface((max(self.width, t_surf.get_width()+10), t_surf.get_height()+10), pygame.SRCALPHA)
if self.backcolor:
self.image.fill(self.backcolor)
self.image.blit(t_surf, (5, 5))
pygame.draw.rect(self.image, self.color, self.image.get_rect().inflate(-2, -2), 2)
self.rect = self.image.get_rect(topleft = self.pos)
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text_input_box = TextInputBox(50, 50, 400, font)
group = pygame.sprite.Group(text_input_box)
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
You can find a great module for Pygame text input here.
I have been using it for a while and I really like it. A tutorial how to use it is included in the description.
However, I have added the possibility to draw a (coloured) rectangle around the text, by adding a rect and a rect_color parameter to the *_init_() function and adding
if self.rect != None:
pygame.draw.rect(screen, self.rect_color, self.rect) #screen is my pygame display surface
to the update(self, events) function.
The pygame_gui module allows you to create a text_input box from the user by creating a UITextEntryLine instance. You'll need to set up an instance as in the quick start guide.
Create the text_input:
from pygame.rect import Rect
from pygame_gui.elements.ui_text_entry_line import UITextEntryLine
text_input = UITextEntryLine(relative_rect=Rect(0, 0, 100, 100), manager=manager)
Get the text if enter is clicked:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
if event.user_type == pygame_gui.UI_TEXT_ENTRY_FINISHED:
if event.ui_element == text_input:
entered_text = event.text
I have written a class that can handle text input
minimal example:
import pygame as pg
from pgtextbox import pgtextbox
pg.init()
screen=pg.display.set_mode((1000,500))
textbox=pgtextbox(200,20)
textbox.insertAtCurser('Hallo')
while True:
e = pg.event.wait(30000)
if e.type == pg.QUIT:
raise StopIteration
textbox.addPgEvent(e)#uses keydown events
print(textbox.text)
screen.fill((0,0,0))
screen.blit(textbox.render(),(10,0))
pg.display.flip()
pg.display.quit()
pgtextbox class:
import pygame as pg
class pgtextbox:#By K1521
def __init__(self,width=100,height=10,fontname=None):
self.surface=pg.Surface((width,height))
self.text=""
self.width=width
self.height=height
self.font=pg.font.Font(fontname,pgtextbox.getMaxFontSize(fontname,lineheight=height))
self.curserindex=0
self.cursersurface=pg.Surface((self.font.size("|")[0]//2,self.font.size("|")[1]))
self.cursersurface.fill((255,255,255))
#self.cursersurface=self.font.render("|",False,(255,255,255),(0,0,0))
self.offsety=int((height-self.font.get_linesize())/2)
self.offsetx=0
def curserpos(self):
return self.font.size(self.text[:self.curserindex])[0]
def addPgEvent(self,event):
if event.type==pg.KEYDOWN:
if event.key==pg.K_BACKSPACE:
self.deleteAtCurser()
elif event.key==pg.K_RIGHT:
self.offsetCurser(1)
elif event.key==pg.K_LEFT:
self.offsetCurser(-1)
else:
self.insertAtCurser(event.unicode)
def render(self):
self.surface.fill((0,0,0))
width=self.width-self.cursersurface.get_width()
text=self.font.render(self.text,False,(255,255,255),(0,0,0))
if self.curserindex>=0:
curserpos=self.curserpos()+self.offsetx
curserposnew=max(0,min(curserpos,width))
self.offsetx+=curserposnew-curserpos
curserpos=curserposnew
#if curserpos<0:
#self.offsetx-=curserpos
#curserpos=0
#if curserpos>width:
#curserpos=curserpos-width
#self.offsetx-=curserpos
else:
#self.offsetx=min(width-text.get_width(),0)
self.offsetx=0
self.surface.blit(text,(self.offsetx,self.offsety))
if self.curserindex>=0:
self.surface.blit(self.cursersurface,(curserpos,self.offsety))
#print((curserpos,self.offsety))
return self.surface
def insertAtCurser(self,t):
if self.curserindex<0:
self.curserindex=len(self.text)
self.text=self.text[:self.curserindex]+t+self.text[self.curserindex:]
self.curserindex+=len(t)
def deleteAtCurser(self,length=1):
if self.curserindex<0:
self.curserindex=len(self.text)
newcurserindex=max(0,self.curserindex-length)
self.text=self.text[:newcurserindex]+self.text[self.curserindex:]
self.curserindex=newcurserindex
def offsetCurser(self,i):
self.curserindex=max(min(self.curserindex+i,len(self.text)),0)
#staticmethod
def longestline(self,fontname,lines):
size=pg.font.Font(fontname,1000)
return max(lines,key=lambda t:size(t)[0])
#staticmethod
def getMaxFontSize(fontname,width=None,lineheight=None,line=None):
def font(size):
return pg.font.Font(fontname,size)
fontsize=float("inf")# inf
if width:
aproxsize=width*1000//font(1000).size(line)[0]
while font(aproxsize).size(line)[0]<width:
aproxsize+=1
while font(aproxsize).size(line)[0]>width:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
if lineheight:
aproxsize=lineheight*4//3
while font(aproxsize).get_linesize()<lineheight:
aproxsize+=1
while font(aproxsize).get_linesize()>lineheight:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
return fontsize
#staticmethod
def rendermultilinetext(text,width=None,height=10,fontname=None,antialias=False,color=(255,255,255),background=None):
if(len(text)-text.count("\n")==0):
return pg.Surface((0,0))
def font(size):
return pg.font.Font(fontname,size)
text=text.split("\n")
fontsize=1000000000# inf
longestline=None
if height:
longestline=pgtextbox.longestline(fontname,lines)
fontsize=pgtextbox.getMaxFontSize(fontname,width,lineheight,longestline)
font=font(fontsize)
width=font.size(longestline)[0]
lineheight=font.get_linesize()
heigth=len(text)*lineheight
textsurface=pg.Surface((width,heigth))
if background:
textsurface.fill(background)
for i,line in enumerate(text):
textsurface.blit(font.render(line,antialias,color,background),(0,i*lineheight))
return textsurface
I want to get some text input from the user in Python and display what they are typing in a text box, and when they press enter, it gets stored in a string.
I've looked everywhere, but I just can't find anything. I'm using Pygame.
You can define a rect as the area of the input box. If a pygame.MOUSEBUTTONDOWN event occurs, use the colliderect method of the input_box rect to check if it collides with the event.pos and then activate it by setting a active variable to True.
If the box is active you can type something and Pygame will generate pygame.KEYDOWN events which have a unicode attribute that you can simply add to a string, e.g. text += event.unicode. If the user presses enter, you can do something with the text string (in the example I just print it) and reset it to ''.
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
font = pg.font.Font(None, 32)
clock = pg.time.Clock()
input_box = pg.Rect(100, 100, 140, 32)
color_inactive = pg.Color('lightskyblue3')
color_active = pg.Color('dodgerblue2')
color = color_inactive
active = False
text = ''
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if input_box.collidepoint(event.pos):
# Toggle the active variable.
active = not active
else:
active = False
# Change the current color of the input box.
color = color_active if active else color_inactive
if event.type == pg.KEYDOWN:
if active:
if event.key == pg.K_RETURN:
print(text)
text = ''
elif event.key == pg.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
screen.fill((30, 30, 30))
# Render the current text.
txt_surface = font.render(text, True, color)
# Resize the box if the text is too long.
width = max(200, txt_surface.get_width()+10)
input_box.w = width
# Blit the text.
screen.blit(txt_surface, (input_box.x+5, input_box.y+5))
# Blit the input_box rect.
pg.draw.rect(screen, color, input_box, 2)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Here's an object-oriented variant that allows you to easily create multiple input boxes:
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
COLOR_INACTIVE = pg.Color('lightskyblue3')
COLOR_ACTIVE = pg.Color('dodgerblue2')
FONT = pg.font.Font(None, 32)
class InputBox:
def __init__(self, x, y, w, h, text=''):
self.rect = pg.Rect(x, y, w, h)
self.color = COLOR_INACTIVE
self.text = text
self.txt_surface = FONT.render(text, True, self.color)
self.active = False
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if self.rect.collidepoint(event.pos):
# Toggle the active variable.
self.active = not self.active
else:
self.active = False
# Change the current color of the input box.
self.color = COLOR_ACTIVE if self.active else COLOR_INACTIVE
if event.type == pg.KEYDOWN:
if self.active:
if event.key == pg.K_RETURN:
print(self.text)
self.text = ''
elif event.key == pg.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
# Re-render the text.
self.txt_surface = FONT.render(self.text, True, self.color)
def update(self):
# Resize the box if the text is too long.
width = max(200, self.txt_surface.get_width()+10)
self.rect.w = width
def draw(self, screen):
# Blit the text.
screen.blit(self.txt_surface, (self.rect.x+5, self.rect.y+5))
# Blit the rect.
pg.draw.rect(screen, self.color, self.rect, 2)
def main():
clock = pg.time.Clock()
input_box1 = InputBox(100, 100, 140, 32)
input_box2 = InputBox(100, 300, 140, 32)
input_boxes = [input_box1, input_box2]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for box in input_boxes:
box.handle_event(event)
for box in input_boxes:
box.update()
screen.fill((30, 30, 30))
for box in input_boxes:
box.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
There are also third party modules available like pygame_textinput.
Use the KEYDOWN event to get the input from the keyboard (see pygame.event). The key that was pressed can be obtained from the key attribute of the pygame.event.Event object. unicode contains a single character string that is the fully translated character. Add the character to the text when a key is pressed.
Two special keys need to be dealt with. If RETURN is pressed, the input is finished. If BACKSPACE is pressed, the last character of the input text must be removed:
repl.it/#Rabbid76/PyGame-TextInput
import pygame
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text = ""
input_active = True
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
input_active = True
text = ""
elif event.type == pygame.KEYDOWN and input_active:
if event.key == pygame.K_RETURN:
input_active = False
elif event.key == pygame.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
window.fill(0)
text_surf = font.render(text, True, (255, 0, 0))
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Use the algorithm in a pygame.sprite.Sprite class. Handle the event in the update method.Determine whether the mouse clicks in the text entry field with collidepoint (see How to detect when a rectangular object, image or sprite is clicked) and activate the text input box:
class TextInputBox(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
Pass the list of events to the update method of the Group that contains the Sprite:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
Minimal example: repl.it/#Rabbid76/PyGame-SpriteTextInput
import pygame
class TextInputBox(pygame.sprite.Sprite):
def __init__(self, x, y, w, font):
super().__init__()
self.color = (255, 255, 255)
self.backcolor = None
self.pos = (x, y)
self.width = w
self.font = font
self.active = False
self.text = ""
self.render_text()
def render_text(self):
t_surf = self.font.render(self.text, True, self.color, self.backcolor)
self.image = pygame.Surface((max(self.width, t_surf.get_width()+10), t_surf.get_height()+10), pygame.SRCALPHA)
if self.backcolor:
self.image.fill(self.backcolor)
self.image.blit(t_surf, (5, 5))
pygame.draw.rect(self.image, self.color, self.image.get_rect().inflate(-2, -2), 2)
self.rect = self.image.get_rect(topleft = self.pos)
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text_input_box = TextInputBox(50, 50, 400, font)
group = pygame.sprite.Group(text_input_box)
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
You can find a great module for Pygame text input here.
I have been using it for a while and I really like it. A tutorial how to use it is included in the description.
However, I have added the possibility to draw a (coloured) rectangle around the text, by adding a rect and a rect_color parameter to the *_init_() function and adding
if self.rect != None:
pygame.draw.rect(screen, self.rect_color, self.rect) #screen is my pygame display surface
to the update(self, events) function.
The pygame_gui module allows you to create a text_input box from the user by creating a UITextEntryLine instance. You'll need to set up an instance as in the quick start guide.
Create the text_input:
from pygame.rect import Rect
from pygame_gui.elements.ui_text_entry_line import UITextEntryLine
text_input = UITextEntryLine(relative_rect=Rect(0, 0, 100, 100), manager=manager)
Get the text if enter is clicked:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
if event.user_type == pygame_gui.UI_TEXT_ENTRY_FINISHED:
if event.ui_element == text_input:
entered_text = event.text
I have written a class that can handle text input
minimal example:
import pygame as pg
from pgtextbox import pgtextbox
pg.init()
screen=pg.display.set_mode((1000,500))
textbox=pgtextbox(200,20)
textbox.insertAtCurser('Hallo')
while True:
e = pg.event.wait(30000)
if e.type == pg.QUIT:
raise StopIteration
textbox.addPgEvent(e)#uses keydown events
print(textbox.text)
screen.fill((0,0,0))
screen.blit(textbox.render(),(10,0))
pg.display.flip()
pg.display.quit()
pgtextbox class:
import pygame as pg
class pgtextbox:#By K1521
def __init__(self,width=100,height=10,fontname=None):
self.surface=pg.Surface((width,height))
self.text=""
self.width=width
self.height=height
self.font=pg.font.Font(fontname,pgtextbox.getMaxFontSize(fontname,lineheight=height))
self.curserindex=0
self.cursersurface=pg.Surface((self.font.size("|")[0]//2,self.font.size("|")[1]))
self.cursersurface.fill((255,255,255))
#self.cursersurface=self.font.render("|",False,(255,255,255),(0,0,0))
self.offsety=int((height-self.font.get_linesize())/2)
self.offsetx=0
def curserpos(self):
return self.font.size(self.text[:self.curserindex])[0]
def addPgEvent(self,event):
if event.type==pg.KEYDOWN:
if event.key==pg.K_BACKSPACE:
self.deleteAtCurser()
elif event.key==pg.K_RIGHT:
self.offsetCurser(1)
elif event.key==pg.K_LEFT:
self.offsetCurser(-1)
else:
self.insertAtCurser(event.unicode)
def render(self):
self.surface.fill((0,0,0))
width=self.width-self.cursersurface.get_width()
text=self.font.render(self.text,False,(255,255,255),(0,0,0))
if self.curserindex>=0:
curserpos=self.curserpos()+self.offsetx
curserposnew=max(0,min(curserpos,width))
self.offsetx+=curserposnew-curserpos
curserpos=curserposnew
#if curserpos<0:
#self.offsetx-=curserpos
#curserpos=0
#if curserpos>width:
#curserpos=curserpos-width
#self.offsetx-=curserpos
else:
#self.offsetx=min(width-text.get_width(),0)
self.offsetx=0
self.surface.blit(text,(self.offsetx,self.offsety))
if self.curserindex>=0:
self.surface.blit(self.cursersurface,(curserpos,self.offsety))
#print((curserpos,self.offsety))
return self.surface
def insertAtCurser(self,t):
if self.curserindex<0:
self.curserindex=len(self.text)
self.text=self.text[:self.curserindex]+t+self.text[self.curserindex:]
self.curserindex+=len(t)
def deleteAtCurser(self,length=1):
if self.curserindex<0:
self.curserindex=len(self.text)
newcurserindex=max(0,self.curserindex-length)
self.text=self.text[:newcurserindex]+self.text[self.curserindex:]
self.curserindex=newcurserindex
def offsetCurser(self,i):
self.curserindex=max(min(self.curserindex+i,len(self.text)),0)
#staticmethod
def longestline(self,fontname,lines):
size=pg.font.Font(fontname,1000)
return max(lines,key=lambda t:size(t)[0])
#staticmethod
def getMaxFontSize(fontname,width=None,lineheight=None,line=None):
def font(size):
return pg.font.Font(fontname,size)
fontsize=float("inf")# inf
if width:
aproxsize=width*1000//font(1000).size(line)[0]
while font(aproxsize).size(line)[0]<width:
aproxsize+=1
while font(aproxsize).size(line)[0]>width:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
if lineheight:
aproxsize=lineheight*4//3
while font(aproxsize).get_linesize()<lineheight:
aproxsize+=1
while font(aproxsize).get_linesize()>lineheight:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
return fontsize
#staticmethod
def rendermultilinetext(text,width=None,height=10,fontname=None,antialias=False,color=(255,255,255),background=None):
if(len(text)-text.count("\n")==0):
return pg.Surface((0,0))
def font(size):
return pg.font.Font(fontname,size)
text=text.split("\n")
fontsize=1000000000# inf
longestline=None
if height:
longestline=pgtextbox.longestline(fontname,lines)
fontsize=pgtextbox.getMaxFontSize(fontname,width,lineheight,longestline)
font=font(fontsize)
width=font.size(longestline)[0]
lineheight=font.get_linesize()
heigth=len(text)*lineheight
textsurface=pg.Surface((width,heigth))
if background:
textsurface.fill(background)
for i,line in enumerate(text):
textsurface.blit(font.render(line,antialias,color,background),(0,i*lineheight))
return textsurface
I want to get some text input from the user in Python and display what they are typing in a text box, and when they press enter, it gets stored in a string.
I've looked everywhere, but I just can't find anything. I'm using Pygame.
You can define a rect as the area of the input box. If a pygame.MOUSEBUTTONDOWN event occurs, use the colliderect method of the input_box rect to check if it collides with the event.pos and then activate it by setting a active variable to True.
If the box is active you can type something and Pygame will generate pygame.KEYDOWN events which have a unicode attribute that you can simply add to a string, e.g. text += event.unicode. If the user presses enter, you can do something with the text string (in the example I just print it) and reset it to ''.
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
font = pg.font.Font(None, 32)
clock = pg.time.Clock()
input_box = pg.Rect(100, 100, 140, 32)
color_inactive = pg.Color('lightskyblue3')
color_active = pg.Color('dodgerblue2')
color = color_inactive
active = False
text = ''
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if input_box.collidepoint(event.pos):
# Toggle the active variable.
active = not active
else:
active = False
# Change the current color of the input box.
color = color_active if active else color_inactive
if event.type == pg.KEYDOWN:
if active:
if event.key == pg.K_RETURN:
print(text)
text = ''
elif event.key == pg.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
screen.fill((30, 30, 30))
# Render the current text.
txt_surface = font.render(text, True, color)
# Resize the box if the text is too long.
width = max(200, txt_surface.get_width()+10)
input_box.w = width
# Blit the text.
screen.blit(txt_surface, (input_box.x+5, input_box.y+5))
# Blit the input_box rect.
pg.draw.rect(screen, color, input_box, 2)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
Here's an object-oriented variant that allows you to easily create multiple input boxes:
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
COLOR_INACTIVE = pg.Color('lightskyblue3')
COLOR_ACTIVE = pg.Color('dodgerblue2')
FONT = pg.font.Font(None, 32)
class InputBox:
def __init__(self, x, y, w, h, text=''):
self.rect = pg.Rect(x, y, w, h)
self.color = COLOR_INACTIVE
self.text = text
self.txt_surface = FONT.render(text, True, self.color)
self.active = False
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if self.rect.collidepoint(event.pos):
# Toggle the active variable.
self.active = not self.active
else:
self.active = False
# Change the current color of the input box.
self.color = COLOR_ACTIVE if self.active else COLOR_INACTIVE
if event.type == pg.KEYDOWN:
if self.active:
if event.key == pg.K_RETURN:
print(self.text)
self.text = ''
elif event.key == pg.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
# Re-render the text.
self.txt_surface = FONT.render(self.text, True, self.color)
def update(self):
# Resize the box if the text is too long.
width = max(200, self.txt_surface.get_width()+10)
self.rect.w = width
def draw(self, screen):
# Blit the text.
screen.blit(self.txt_surface, (self.rect.x+5, self.rect.y+5))
# Blit the rect.
pg.draw.rect(screen, self.color, self.rect, 2)
def main():
clock = pg.time.Clock()
input_box1 = InputBox(100, 100, 140, 32)
input_box2 = InputBox(100, 300, 140, 32)
input_boxes = [input_box1, input_box2]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
for box in input_boxes:
box.handle_event(event)
for box in input_boxes:
box.update()
screen.fill((30, 30, 30))
for box in input_boxes:
box.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
There are also third party modules available like pygame_textinput.
Use the KEYDOWN event to get the input from the keyboard (see pygame.event). The key that was pressed can be obtained from the key attribute of the pygame.event.Event object. unicode contains a single character string that is the fully translated character. Add the character to the text when a key is pressed.
Two special keys need to be dealt with. If RETURN is pressed, the input is finished. If BACKSPACE is pressed, the last character of the input text must be removed:
repl.it/#Rabbid76/PyGame-TextInput
import pygame
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text = ""
input_active = True
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
input_active = True
text = ""
elif event.type == pygame.KEYDOWN and input_active:
if event.key == pygame.K_RETURN:
input_active = False
elif event.key == pygame.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
window.fill(0)
text_surf = font.render(text, True, (255, 0, 0))
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Use the algorithm in a pygame.sprite.Sprite class. Handle the event in the update method.Determine whether the mouse clicks in the text entry field with collidepoint (see How to detect when a rectangular object, image or sprite is clicked) and activate the text input box:
class TextInputBox(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
Pass the list of events to the update method of the Group that contains the Sprite:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
Minimal example: repl.it/#Rabbid76/PyGame-SpriteTextInput
import pygame
class TextInputBox(pygame.sprite.Sprite):
def __init__(self, x, y, w, font):
super().__init__()
self.color = (255, 255, 255)
self.backcolor = None
self.pos = (x, y)
self.width = w
self.font = font
self.active = False
self.text = ""
self.render_text()
def render_text(self):
t_surf = self.font.render(self.text, True, self.color, self.backcolor)
self.image = pygame.Surface((max(self.width, t_surf.get_width()+10), t_surf.get_height()+10), pygame.SRCALPHA)
if self.backcolor:
self.image.fill(self.backcolor)
self.image.blit(t_surf, (5, 5))
pygame.draw.rect(self.image, self.color, self.image.get_rect().inflate(-2, -2), 2)
self.rect = self.image.get_rect(topleft = self.pos)
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
self.active = self.rect.collidepoint(event.pos)
if event.type == pygame.KEYDOWN and self.active:
if event.key == pygame.K_RETURN:
self.active = False
elif event.key == pygame.K_BACKSPACE:
self.text = self.text[:-1]
else:
self.text += event.unicode
self.render_text()
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
text_input_box = TextInputBox(50, 50, 400, font)
group = pygame.sprite.Group(text_input_box)
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
You can find a great module for Pygame text input here.
I have been using it for a while and I really like it. A tutorial how to use it is included in the description.
However, I have added the possibility to draw a (coloured) rectangle around the text, by adding a rect and a rect_color parameter to the *_init_() function and adding
if self.rect != None:
pygame.draw.rect(screen, self.rect_color, self.rect) #screen is my pygame display surface
to the update(self, events) function.
The pygame_gui module allows you to create a text_input box from the user by creating a UITextEntryLine instance. You'll need to set up an instance as in the quick start guide.
Create the text_input:
from pygame.rect import Rect
from pygame_gui.elements.ui_text_entry_line import UITextEntryLine
text_input = UITextEntryLine(relative_rect=Rect(0, 0, 100, 100), manager=manager)
Get the text if enter is clicked:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
if event.user_type == pygame_gui.UI_TEXT_ENTRY_FINISHED:
if event.ui_element == text_input:
entered_text = event.text
I have written a class that can handle text input
minimal example:
import pygame as pg
from pgtextbox import pgtextbox
pg.init()
screen=pg.display.set_mode((1000,500))
textbox=pgtextbox(200,20)
textbox.insertAtCurser('Hallo')
while True:
e = pg.event.wait(30000)
if e.type == pg.QUIT:
raise StopIteration
textbox.addPgEvent(e)#uses keydown events
print(textbox.text)
screen.fill((0,0,0))
screen.blit(textbox.render(),(10,0))
pg.display.flip()
pg.display.quit()
pgtextbox class:
import pygame as pg
class pgtextbox:#By K1521
def __init__(self,width=100,height=10,fontname=None):
self.surface=pg.Surface((width,height))
self.text=""
self.width=width
self.height=height
self.font=pg.font.Font(fontname,pgtextbox.getMaxFontSize(fontname,lineheight=height))
self.curserindex=0
self.cursersurface=pg.Surface((self.font.size("|")[0]//2,self.font.size("|")[1]))
self.cursersurface.fill((255,255,255))
#self.cursersurface=self.font.render("|",False,(255,255,255),(0,0,0))
self.offsety=int((height-self.font.get_linesize())/2)
self.offsetx=0
def curserpos(self):
return self.font.size(self.text[:self.curserindex])[0]
def addPgEvent(self,event):
if event.type==pg.KEYDOWN:
if event.key==pg.K_BACKSPACE:
self.deleteAtCurser()
elif event.key==pg.K_RIGHT:
self.offsetCurser(1)
elif event.key==pg.K_LEFT:
self.offsetCurser(-1)
else:
self.insertAtCurser(event.unicode)
def render(self):
self.surface.fill((0,0,0))
width=self.width-self.cursersurface.get_width()
text=self.font.render(self.text,False,(255,255,255),(0,0,0))
if self.curserindex>=0:
curserpos=self.curserpos()+self.offsetx
curserposnew=max(0,min(curserpos,width))
self.offsetx+=curserposnew-curserpos
curserpos=curserposnew
#if curserpos<0:
#self.offsetx-=curserpos
#curserpos=0
#if curserpos>width:
#curserpos=curserpos-width
#self.offsetx-=curserpos
else:
#self.offsetx=min(width-text.get_width(),0)
self.offsetx=0
self.surface.blit(text,(self.offsetx,self.offsety))
if self.curserindex>=0:
self.surface.blit(self.cursersurface,(curserpos,self.offsety))
#print((curserpos,self.offsety))
return self.surface
def insertAtCurser(self,t):
if self.curserindex<0:
self.curserindex=len(self.text)
self.text=self.text[:self.curserindex]+t+self.text[self.curserindex:]
self.curserindex+=len(t)
def deleteAtCurser(self,length=1):
if self.curserindex<0:
self.curserindex=len(self.text)
newcurserindex=max(0,self.curserindex-length)
self.text=self.text[:newcurserindex]+self.text[self.curserindex:]
self.curserindex=newcurserindex
def offsetCurser(self,i):
self.curserindex=max(min(self.curserindex+i,len(self.text)),0)
#staticmethod
def longestline(self,fontname,lines):
size=pg.font.Font(fontname,1000)
return max(lines,key=lambda t:size(t)[0])
#staticmethod
def getMaxFontSize(fontname,width=None,lineheight=None,line=None):
def font(size):
return pg.font.Font(fontname,size)
fontsize=float("inf")# inf
if width:
aproxsize=width*1000//font(1000).size(line)[0]
while font(aproxsize).size(line)[0]<width:
aproxsize+=1
while font(aproxsize).size(line)[0]>width:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
if lineheight:
aproxsize=lineheight*4//3
while font(aproxsize).get_linesize()<lineheight:
aproxsize+=1
while font(aproxsize).get_linesize()>lineheight:
aproxsize-=1
fontsize=min(aproxsize,fontsize)
return fontsize
#staticmethod
def rendermultilinetext(text,width=None,height=10,fontname=None,antialias=False,color=(255,255,255),background=None):
if(len(text)-text.count("\n")==0):
return pg.Surface((0,0))
def font(size):
return pg.font.Font(fontname,size)
text=text.split("\n")
fontsize=1000000000# inf
longestline=None
if height:
longestline=pgtextbox.longestline(fontname,lines)
fontsize=pgtextbox.getMaxFontSize(fontname,width,lineheight,longestline)
font=font(fontsize)
width=font.size(longestline)[0]
lineheight=font.get_linesize()
heigth=len(text)*lineheight
textsurface=pg.Surface((width,heigth))
if background:
textsurface.fill(background)
for i,line in enumerate(text):
textsurface.blit(font.render(line,antialias,color,background),(0,i*lineheight))
return textsurface