Blit user text input to screen - python

I want to blit text that is input by the user to the screen. Each time the user presses Return, the typed text should be blitted to the screen. For text input I use this [text_input module] (https://github.com/Nearoo/pygame-text-input).
Here is the code I came up with so far:
import pygame_textinput
import pygame
pygame.init()
# Set some parameters
duration = 5.0
time = pygame.time.get_ticks()/1000
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
yoffset = 5
# Function that positions user input rects on screen
def renderInput(text, xoffset, yoffset):
font = pygame.font.SysFont("arial", 20)
renderText = font.render(text, False, (0, 0, 0))
rectText = renderText.get_rect()
rectText = rectText.move((0 + xoffset), (screen.get_height()/2 + yoffset))
return renderText, rectText
# Fills the screen once at the beginning
screen.fill((225, 225, 225))
while (pygame.time.get_ticks()/1000) < time + duration:
# creat new text input object on every trial
textinput = pygame_textinput.TextInput()
while True:
# Fills the surface after each keypress
screen.fill((225, 225, 225))
# Check events
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
exit()
# Feed with events every frame
# This evaluates to True once Return is pressed
if textinput.update(events):
userInput = textinput.get_text()
yoffset += 20
break
# Blit surface onto the screen
screen.blit(textinput.get_surface(), (10, 10))
# Update screen
pygame.display.update()
clock.tick(30)
# Blits user input to screen each time "Return" is pressed
# First get input text and the rectangle of the text
text, textrect = renderInput(userInput, 5, yoffset)
# Then blit it to the screen
screen.blit(text, textrect)
pygame.display.update()
My problem is, that the blitting only works if I do not fill the screen after each keypress within the while-loop that handles the input. If I do that, then the text input, however, is not cleared after each time the user presses Return.
So is there a way to have both, redraw after each keypress and have the text displayed below after each time Return is pressed by the user.
Thanks.

If I understand you correctly, the text in the input field should be cleared and it should be blit in the main area of the screen. I'd assign the text to the user_input variable if the user presses enter and then create a new pygame_textinput.TextInput() instance to clear the input field.
I've tried to simplify your code, because the two while loops are a bit confusing and I'm not sure what their purpose is. There should usually be only one while loop in a game.
import pygame
import pygame_textinput
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 20)
textinput = pygame_textinput.TextInput()
user_input = ''
done = False
while not done:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
done = True
if textinput.update(events):
user_input = textinput.get_text()
textinput = pygame_textinput.TextInput()
# Draw everything.
screen.fill((225, 225, 225))
screen.blit(textinput.get_surface(), (10, 10))
user_input_surface = font.render(user_input, True, (30, 80, 100))
screen.blit(user_input_surface, (10, 50))
pygame.display.update()
clock.tick(30)
pygame.quit()
Edit: In this version I append the rendered text surfaces to a list and blit them with an offset.
import pygame
import pygame_textinput
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 20)
textinput = pygame_textinput.TextInput()
user_inputs = []
done = False
while not done:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
done = True
if textinput.update(events):
user_inputs.append(
font.render(textinput.get_text(), True, (30, 80, 100)))
textinput = pygame_textinput.TextInput()
screen.fill((225, 225, 225))
screen.blit(textinput.get_surface(), (10, 10))
for y, text_surf in enumerate(user_inputs):
screen.blit(text_surf, (10, 50+30*y))
pygame.display.update()
clock.tick(30)
pygame.quit()
Edit2: To get a table, you can use modulo for the row offset and floor division for the column offset. The problem with this example is that the text surfaces can overlap if they are too wide.
for n, text_surf in enumerate(user_inputs):
# 5 rows. Offset = 30 pixels.
y_pos = 50 + (n%5) * 30
# After 5 rows add a new column. Offset = 100 pixels.
x_pos = 10 + n // 5 * 100
screen.blit(text_surf, (x_pos, y_pos))

I have edited my code containing your suggestions. Thanks a lot, this really seems to solve my problem. Here is the current version including a timer:
import pygame_textinput
import pygame
pygame.init()
# Set some parameters
duration = 5.0
time = pygame.time.get_ticks()/1000
xoffset = 5
yoffset = 5
screen = pygame.display.set_mode((400, 400))
font = pygame.font.SysFont("arial", 20)
clock = pygame.time.Clock()
# Creates textinput instance and an empty list to store inputs
textinput = pygame_textinput.TextInput()
userInputs = []
# Fills the screen once at the beginning
screen.fill((225, 225, 225))
while (pygame.time.get_ticks()/1000) < time + duration:
# Check events
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
exit()
# Feed with events every frame
# This evaluates to True once Return is pressed
if textinput.update(events):
userInputs.append(font.render(textinput.get_text(), True, (30, 80, 100)))
textinput = pygame_textinput.TextInput()
# Fill screen
screen.fill((225, 225, 225))
# Blit its surface onto the screen
screen.blit(textinput.get_surface(), (screen.get_rect().centerx, screen.get_rect().height/5))
for y, text_surf in enumerate(userInputs):
screen.blit(text_surf, (10, (screen.get_rect().height/4)+30*y))
# Update screen
pygame.display.update()
clock.tick(30)
I do not want to bother you to much, but now I have one more issue left that I am having trouble solving. Is it possible to render the text inputs in a second column once it exits the bottom border of the screen? So for example, if the user types a lot of words, that do not fit under each other, is it possible to move the next text input to the right and make it start next to the first input (create a second column so to speak). Thanks for your help so far, I really apreciatie it.

Related

How do i blit multiple same images but at different coordinates?

im trying to create a game where multiple of the same images will blit randomly along the borders of my window. but I do not know how to blit it multiple times and also along the borders.
Here's the code so far:
import pygame, sys
from pygame.locals import *
import random
pygame.init()
DisplayWidth = 700
DisplayHeight = 400
Display = pygame.display.set_mode((DisplayWidth, DisplayHeight))
Death = False
def PlaceElon():
ElonX = random.randrange(0, 700, 700)
ElonY = random.randrange(0, 400)
x = []
y = []
Elonlist = [x, y]
elon = pygame.image.load('elon.png')
elonbig = pygame.transform.smoothscale(elon, (50, 54))
for x in Elonlist:
x.append(ElonX)
for y in Elonlist:
y.append(ElonY)
Display.blit(elonbig, (Elonlist))
pygame.display.update()
def RunGame():
while not Death:
background = pygame.image.load('background.png')
BigBackground = pygame.transform.smoothscale(background, (DisplayWidth, DisplayHeight))
Display.blit(BigBackground, (0,0))
PlaceElon()
RunGame()
You need to generate a list of coordinates:
Elonlist = []
noOfElons = 10
for _ in range(noOfElons)
x = random.randrange(0, 700 - elon.get_width())
y = random.randrange(0, 400 - elon.get_height())
Elonlist.appned((x, y))
Draw the images in a loop:
for ElonPos in Elonlist:
Display.blit(elon, ElonPos)
However there are some more problems in your application.
Generate the positions and load the images before the application loop:
background = pygame.image.load('background.png')
BigBackground = pygame.transform.smoothscale(background, (DisplayWidth, DisplayHeight))
elon = pygame.image.load('elon.png')
The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()
limit frames per second to limit CPU usage
# main application loop
run = True
while run:
# limit frames per second
clock.tick(60)
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# draw background
Display.blit(BigBackground, (0,0))
# draw the scene
for elonPos in Elonlist:
Display.blit(elon, elonPos)
# update the display
pygame.display.flip()
pygame.quit()
exit()

Is there a way to get pygame to detect what color my cursor is on

I'm trying to make a game where it spawns another circle every time you click on a circle. And the error i'm getting is "TypeError: 'bool' object is not callable". I'm looking for a solution that doesn't completly change the code since i'm new and want to understand the code myself. But at this point i'll take any help.
import pygame
import random
import time
from pygame.math import Vector2
# Define some colors
BLACK = (0, 0, 0)
WHITE = (247, 247, 247)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (225,225,0)
tuple1 = (247, 247, 247, 255)
# Setup
pygame.init()
# Set the width and height of the screen [width,height]
surface = pygame.display.set_mode( (2560, 1440) )
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Hide the mouse cursor
pygame.mouse.set_visible(0)
# Speed in pixels per frame
x_speed = 0
y_speed = 0
# Current position
cursor = pygame.image.load('cursor.png').convert_alpha()
pygame.image.load("pattern.jpg")
background_image = pygame.image.load("pattern.jpg").convert()
circposy = 770
circposx = 1280
# -------- Main Program Loop -----------
while done ==False:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
done = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Drawing Code
surface.fill(WHITE)
# First, clear the screen to WHITE. Don't put other drawing commands
# above this, or they will be erased with this command.\
player_position = pygame.mouse.get_pos()
a = 0
b = 1
p=player_position[a]
o=player_position[b]
player_position = (p,o)
pygame.draw.circle(surface,RED,[circposx,circposy], 40)
tuple2 = surface.get_at(pygame.mouse.get_pos())
print (tuple2)
q = p - 2545
w = o - 2545
surface.blit( cursor, (q, w) )
a=0
result = tuple(map(int, tuple2)) > tuple1
print (result)
while event.type == pygame.MOUSEBUTTONDOWN:
done = True
if result == True():
a+1
surface.fill(WHITE)
pygame.draw.circle(surface,RED,[circposx + randint, circposy+randint],40)
print (a)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit frames per second
clock.tick(144)
# Close the window and quit.
pygame.quit()
cursor.png
The short answer is that the code is trying to call True(), which isn't a function.
while event.type == pygame.MOUSEBUTTONDOWN:
done = True
if result == True(): # <<-- HERE
a+1
surface.fill(WHITE)
pygame.draw.circle(surface,RED,[circposx + randint, circposy+randint],40)
Simply change this to True.
But you will also need to define what randint is a few lines lower. Did you mean random.randint( 0, 500 ) or suchlike? And fixing this leads to another error, because the surrounding loop to this code, is an infinite loop:
while event.type == pygame.MOUSEBUTTONDOWN: # <<-- HERE
done = True
if result == True:
a+1
surface.fill(WHITE)
rand_x = random.randint( 0, 500 )
rand_y = random.randint( 0, 500 )
pygame.draw.circle(surface,RED,[circposx + rand_x, circposy+rand_y],40)
Because there is no way event.type can ever change inside that loop. This should probably read:
if event.type == pygame.MOUSEBUTTONDOWN:
If I may make some suggestions:
Put all your event handling to a single place.
There's some doubling-up of event handling, and it would have prevented that infinite loop.
Move your screen dimensions into variables
SCREEN_WIDTH = 2560
SCREEN_HEIGHT = 1440
Instead of having constant numbers through the code (e.g.: 2545) make these functions of the screen size.
SCREEN_MARGIN = SCREEN_WIDTH - round( SCREEN_WIDTH * 0.10 )
q = p - SCREEN_MARGIN
If you want to detect what color your cursor is on, you can use pyautogui. Make sure you have pyautogui installed. type pip install pyautogui. If it doesn't install successfully, you already have it installed.
# Import everything
import pygame
import pyautogui
from pyautogui import *
# Initialize
pygame.init()
# Get mouse position
mouse_pos = pygame.mouse.get_pos()
x = mouse_pos[0]
y = mouse_pos[1]
# Get Color
r = pyautogui.pixel(x,y)[0]
g = pyautogui.pixel(x,y)[1]
b = pyautogui.pixel(x,y)[2]
color = [r,g,b]
Hopefully, you found this helpful!

pygame.time.wait() crashes the program

In a pygame code I wannted to do a title that changes colors.
I tried to do a simple title that changes colors, but it not even turned the color to blue (or do it for a second), and the program crash. The code:
title_font = pygame.font.SysFont("monospace", TITLE_SIZE)
while True:
title = title_font.render("game", 5, RED)
game_display.blit(title, TITLE_POS)
pygame.display.update()
pygame.time.wait(2000)
title = title_font.render("game", 5, BLUE)
game_display.blit(title, TITLE_POS)
pygame.display.update()
pygame.time.wait(3000)
title = title_font.render("game", 5, RED)
game_display.blit(title, TITLE_POS)
pygame.display.update()
pygame.time.wait(2000)
It also happens with pygame.time.delay(), and I don't know where is the problem...
Don't use pygame.time.wait or delay because these functions make your program sleep for the given time and the window becomes unresponsive. You also need to handle the events (with one of the pygame.event functions) each frame to avoid this.
Here are some timer examples which don't block: Countdown timer in Pygame
To switch the colors, you can just assign the next color to a variable and use it to render the text.
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
title_font = pygame.font.SysFont('monospace', 50)
BACKGROUND_COLOR = pygame.Color('gray12')
BLUE = pygame.Color('blue')
RED = pygame.Color('red')
# Assign the current color to the color variable.
color = RED
timer = 2
dt = 0
done = False
while not done:
# Handle the events.
for event in pygame.event.get():
# This allows the user to quit by pressing the X button.
if event.type == pygame.QUIT:
done = True
timer -= dt # Decrement the timer by the delta time.
if timer <= 0: # When the time is up ...
# Swap the colors.
if color == RED:
color = BLUE
timer = 3
else:
color = RED
timer = 2
screen.fill(BACKGROUND_COLOR)
title = title_font.render('game', 5, color)
screen.blit(title, (200, 50))
pygame.display.flip()
# dt is the passed time since the last clock.tick call.
dt = clock.tick(60) / 1000 # / 1000 to convert milliseconds to seconds.
pygame.quit()

Line is not drawn (It seems that pygame.draw.polygon is not running)

I made Button frame. But Square is not drawn.
class button:
def __init__(self,screen,size): # size -->(width,height)
self.width = size[0]
self.height = size[1]
self.screen = screen
def Buttongenerate(self,TEXT,POS):
font = pygame.font.Font(None,self.height//3)
text = font.render(TEXT,True,(250,250,250))
textsize = font.size(TEXT)
TEXTPOS = [self.width+(self.width-textsize[0])/2,\
self.height+(self.height-textsize[1])/2]
self.screen.blit(text,TEXTPOS)
pygame.draw.polygon(self.screen, (250,250,250), [[POS[0],POS[1]],\
[POS[0]+self.width,POS[1]],[POS[0]+self.width,POS[1]+self.height],\
[POS[0],POS[1]+self.height]], 2)
while True:
screen.fill((0,0,0))
buttontest2 = button(screen,(200,100))
buttontest2.Buttongenerate("TEST",(200,200))
pygame.display.flip()
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
exit(0)
When I run this code , but It seems that pygame.draw.polygon is not running
pygame.draw.polygon works just fine.
Your problem is that you calculate TEXTPOS wrong (I just guess the text should be rendered inside the rectangle). You should use
TEXTPOS = [POS[0]+(self.width-textsize[0])/2, POS[1]+(self.height-textsize[1])/2]
which actually uses POS to calculate the final position of the text.
Otherwise, the text is rendered above the rectangle. Here's the full, running code:
import pygame
pygame.init()
screen = pygame.display.set_mode((600,600))
class button:
def __init__(self,screen,size): # size -->(width,height)
self.width = size[0]
self.height = size[1]
self.screen = screen
def Buttongenerate(self,TEXT,POS):
font = pygame.font.Font(None,self.height//3)
text = font.render(TEXT,True,(250,250,250))
textsize = font.size(TEXT)
TEXTPOS = [POS[0]+(self.width-textsize[0])/2, POS[1]+(self.height-textsize[1])/2]
self.screen.blit(text,TEXTPOS)
pygame.draw.polygon(self.screen, (250,250,250), [[POS[0],POS[1]],[POS[0]+self.width,POS[1]],[POS[0]+self.width,POS[1]+self.height],[POS[0],POS[1]+self.height]], 2)
while True:
screen.fill((0,0,0))
buttontest2 = button(screen,(200,100))
buttontest2.Buttongenerate("TEST",(200,200))
pygame.display.flip()
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
Note that it would be better to create a new surface for the text and rectangle once instead of loading the font, rendering the text, and drawing on the screen surface every iteration of the main loop.

Rendering vanishing text in pygame

I'm making a small game using pygame. When the game starts I want to display "start" and vanish it after a few seconds. How to do that?
First you need a timer variable (there are other questions about timers, so I won't explain them here). I'm just counting the frames in the following example.
To remove the text abruptly you can just keep blitting it until the time is up.
if timer > 0:
screen.blit(txt_surf, (position))
The slowly disappearing text can be achieved by filling the text surface with white and the current alpha value (which is reduced each frame) and by passing the pg.BLEND_RGBA_MULT special flag. That will affect only the alpha channel of the surface.
txt_surf.fill((255, 255, 255, alpha), special_flags=pg.BLEND_RGBA_MULT)
Also, use a copy of the original text surface, otherwise it would subsequently reduce the alpha of the previously modified surface and the text would disappear too quickly.
import pygame as pg
def main():
pg.init()
clock = pg.time.Clock()
screen = pg.display.set_mode((640, 480))
font = pg.font.Font(None, 64)
orig_surf = font.render('Enter your text', True, pg.Color('royalblue'))
txt_surf = orig_surf.copy()
alpha = 255 # The current alpha value of the surface.
timer = 20 # To get a 20 frame delay.
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return
if timer > 0:
timer -= 1
else:
if alpha > 0:
# Reduce alpha each frame, but make sure it doesn't get below 0.
alpha = max(0, alpha-4)
# Create a copy so that the original surface doesn't get modified.
txt_surf = orig_surf.copy()
txt_surf.fill((255, 255, 255, alpha), special_flags=pg.BLEND_RGBA_MULT)
screen.fill((30, 30, 30))
screen.blit(txt_surf, (30, 60))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
Please try adding below code:
for i in range(whatevernumberbutnotover1000):
mytext.set_alpha(i)

Categories