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
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 am required to add a wordbank for this client at my internship (for one who does not have any programming background whatsoever) to this pygame. The pygame shown below is a skeleton of what I have (obviously cannot show internal code to the internet) and I need a function where it takes the 2 text objects and places them in a separate region in vertical fashion like a wordbank on a history test or something.
How do I implement this GUI-type element into this code using the pygame library?
Essentially This game below runs to populate two textbox objects to be dragged around the screen y the user. I would like a designated (either external or internal) region to place these words in so that when the game loads the words are ordered down in a column to the side of the window.
import sys
import pygame as pg
pg.init()
FONT = pg.font.Font(None, 42)
class Text(pg.sprite.Sprite):
def __init__(self, text, pos, color, *groups):
super().__init__(*groups)
self.image = FONT.render(text, True, color)
self.rect = self.image.get_rect(center=pos)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group(
Text('hello', (100, 300), pg.Color('steelblue1')),
Text('world', (250, 300), pg.Color('sienna1')),
)
selected = 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 sprite in all_sprites:
if sprite.rect.collidepoint(event.pos):
selected = sprite
break
elif event.type == pg.MOUSEBUTTONUP:
selected = None
elif event.type == pg.MOUSEMOTION:
if selected:
selected.rect.center = event.pos
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
sys.exit()
Here's an example that demonstrates what I mentioned in the comment. I just use the area rect to define a region where you can drop the text objects and append them to the text_objects list. If I select one of these objects I just remove them from the list.
import sys
import pygame as pg
pg.init()
FONT = pg.font.Font(None, 42)
class Text(pg.sprite.Sprite):
def __init__(self, text, pos, color, *groups):
super().__init__(*groups)
self.image = FONT.render(text, True, color)
self.rect = self.image.get_rect(center=pos)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group(
Text('hello', (300, 300), pg.Color('steelblue1')),
Text('world', (450, 300), pg.Color('sienna1')),
)
area = pg.Rect(100, 70, 100, 300)
text_objects = []
selected = 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 sprite in all_sprites:
if sprite.rect.collidepoint(event.pos):
selected = sprite
if selected in text_objects:
text_objects.remove(selected)
break
elif event.type == pg.MOUSEBUTTONUP:
if selected and selected.rect.colliderect(area):
selected.rect.centerx = area.centerx
text_objects.append(selected)
selected = None
elif event.type == pg.MOUSEMOTION:
if selected:
selected.rect.center = event.pos
all_sprites.update()
screen.fill((30, 30, 30))
pg.draw.rect(screen, (190, 120, 20), area, 2)
for y, sprite in enumerate(text_objects):
sprite.rect.y = y * sprite.rect.h + area.top
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
sys.exit()
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