I have come as far as drawing a rectangle in pygame however I need to be able to get text like "Hello" into that rectangle. How can I do this? (If you can explain it as well that would be much appreciated. Thank-you)
Here is my code:
import pygame
import sys
from pygame.locals import *
white = (255,255,255)
black = (0,0,0)
class Pane(object):
def __init__(self):
pygame.init()
pygame.display.set_caption('Box Test')
self.screen = pygame.display.set_mode((600,400), 0, 32)
self.screen.fill((white))
pygame.display.update()
def addRect(self):
self.rect = pygame.draw.rect(self.screen, (black), (175, 75, 200, 100), 2)
pygame.display.update()
def addText(self):
#This is where I want to get the text from
if __name__ == '__main__':
Pan3 = Pane()
Pan3.addRect()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit();
Thank you for your time.
You first have to create a Font (or SysFont) object. Calling the render method on this object will return a Surface with the given text, which you can blit on the screen or any other Surface.
import pygame
import sys
from pygame.locals import *
white = (255,255,255)
black = (0,0,0)
class Pane(object):
def __init__(self):
pygame.init()
self.font = pygame.font.SysFont('Arial', 25)
pygame.display.set_caption('Box Test')
self.screen = pygame.display.set_mode((600,400), 0, 32)
self.screen.fill((white))
pygame.display.update()
def addRect(self):
self.rect = pygame.draw.rect(self.screen, (black), (175, 75, 200, 100), 2)
pygame.display.update()
def addText(self):
self.screen.blit(self.font.render('Hello!', True, (255,0,0)), (200, 100))
pygame.display.update()
if __name__ == '__main__':
Pan3 = Pane()
Pan3.addRect()
Pan3.addText()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit();
Note that your code seems a little bit strange, since usually you do all the drawing in the main loop, not beforehand. Also, when you make heavy use of text in your program, consider caching the result of Font.render, since it is a very slow operation.
Hi!
To be honestly there is pretty good ways to write text in any place of current rect.
And now i'll show how to do it pretty easy.
First of all we need to create object of rect instance:
rect_obj = pygame.draw.rect(
screen,
color,
<your cords and margin goes here>
)
Now rect_obj is object of pygame.rect instance. So, we are free to manipulate with this methods. But, beforehand lets create our rendered text object like this:
text_surface_object = pygame.font.SysFont(<your font here>, <font size here>).render(
<text>, True, <color>
)
Afterall we are free to manipulate with all methods, as i mensioned before:
text_rect = text_surface_object.get_rect(center=rect_obj.center)
What is this code about?
We've just got center cords of out current rect, so easy!
Now, you need to blit ur screen like this:
self.game_screen.blit(text_surface_object, text_rect)
Happy coding! :)
Related
I am working with Pygame currently, and I made a simple function to create window instances much like Windows 10 UI. the code I made doesn't give any errors or any unwanted outputs. It just seems not to be working properly, what I mean by "not working properly"; it just doesn't seem to be moving the frames that are meant to be dragged around by a master frame...
This is my code:
import pygame
from pyautogui import size
import datetime
pygame.init()
infoObject = pygame.display.Info()
surface = pygame.display.set_mode((900, 700))
run = True
clock = pygame.time.Clock()
def draw_text(text, font, text_col, x,y):
img = font.render(text, True, text_col)
rect = img.get_rect()
rect.center = (x,y)
surface.blit(img, rect)
return rect
class make_a_window():
def __init__(self,app,width=750,height=500):
self.app_name = str(app)
self.width = width
self.height = height
def run(self):
self.top_frame = pygame.draw.rect(surface, "#C0C0C0", pygame.Rect(0,0,int(self.width),40))#master frame
self.main_frame = pygame.draw.rect(surface, (255,255,255), pygame.Rect(0,40,int(self.width),int(self.height)))
self.red_frame_for_exit_btn_X = pygame.draw.rect(surface, (255,0,0), pygame.Rect(self.width-42,0,42,40))
self.exit_btn_X = draw_text("x", pygame.font.SysFont("calibri",25), "black", self.width-20, 15)
self.mouse_position = pygame.mouse.get_pos()
if pygame.mouse.get_pressed()[0] == 1:
if self.top_frame.collidepoint(self.mouse_position):
#moving the frames
self.top_frame.move(self.mouse_position[0],self.mouse_position[1])
self.main_frame.move(self.mouse_position[0]-40,self.mouse_position[1])
self.red_frame_for_exit_btn_X.move(self.mouse_position[0]-42,self.mouse_position[1])
self.exit_btn_X.move(self.mouse_position[0]-20,self.mouse_position[1])
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
run = False
app = make_a_window("hello")
app.run()
pygame.display.update()
clock.tick(60)
Sorry for my bad English. and thanks for the help, I really appreciate it 😃!
There is some logic error from line 32 to 41.
Firstly you should use the event queue by pygame.event.get() to track mouse activities(this is really important) and secondly why are you recording the mouse position before hand you are checking for its collision. Instead you should insert your
{self.mouse_position = pygame.mouse.get_pos()}
inside the collision checking if statement (rather that would not work smoothly until you use pygame.event.get())
One more thing that the function
pygame.Rect().move()
takes x and y offesets as its arguments not x and y coordinates.
So, mainly give focus on your event loop and the destination positions of your manual window. Maybe I would share the correct code later (don't wait for it.)
The method pygame.Rect.move doesn't move the rectangle itself, but it returns new rectangle that is moved. In compare, the method pygame.Rect.move_ip move the object in place.
However, these methods do not move anything that has been drawn on the screen. These methods simply move a rectangle representing an area of the screen. This rectangle can later be used to draw something on the screen at a new location.
Create the pygame.Rect objects in the class's constructor and use them to draw the objects. Use move_ip to move the rectangles:
class make_a_window():
def __init__(self,app,width=750,height=500):
self.app_name = str(app)
self.width = width
self.height = height
self.top_frame = pygame.Rect(0,0,int(self.width),40)
self.main_frame = pygame.Rect(0,40,int(self.width),int(self.height))
self.red_frame_for_exit_btn_X = pygame.Rect(self.width-42,0,42,40)
self.exit_btn_X = pygame.Rect(self.width-20, 15, 0, 0)
def run(self):
pygame.draw.rect(surface, "#C0C0C0", self.top_frame)
pygame.draw.rect(surface, (255,255,255), self.main_frame)
pygame.draw.rect(surface, (255,0,0), self.red_frame_for_exit_btn_X)
draw_text("x", pygame.font.SysFont("calibri",25), "black", self.exit_btn_X.topleft)
self.mouse_position = pygame.mouse.get_rel()
if pygame.mouse.get_pressed()[0] == 1:
if self.top_frame.collidepoint(self.mouse_position):
#moving the frames
move_rel = pygame.mouse.get_rel()
self.top_frame.move_ip(*move_rel)
self.main_frame.move_ip(*move_rel)
self.red_frame_for_exit_btn_X.move_ip(*move_rel)
self.exit_btn_X.move_ip(*move_rel)
I think my understanding of Pygame is a little bit weak. I would appreciate any help in general about the intricacies of the code (since this was given by the teacher) or simply how I can at least make the obstacle visible.
def draw(screen, background, boids, obstaclearray):
#redrawing the whole window
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
pygame.display.update(dirty)
Above is where I actually do the drawing and attempt to draw the circle.
The CircularObstacle class is a very simple class that looks like this:
import pygame
class CircularObstacle():
def __init__(self, x, y, radius): #MAYBE ADD A SIZE
self.x = x
self.y = y
self.radius = radius
The problem is that the circle only draws itself when the boids have went over it, which is really weird. I think it has to do with the way the pygame has been setup with and the Surfaces and everything, so below is all the code in main. Of course the obstacle does not work as intended, but I plan to fix that later, first I want to at least get a circle to show.
Below is my full code because I believe it is crucial to solving the issue:
import pygame
from pygame.locals import *
import argparse
import sys
from boid import Boid
from Obstacle import CircularObstacle
def add_boids(boids,num_boids):
for boid in range (num_boids):
boids.add(Boid())
def update(dt, boids):
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN:
mods = pygame.key.get_mods()
if event.key == pygame.K_q:
# quit
pygame.quit()
sys.exit(0)
elif event.key == pygame.K_UP:
# add boids
if mods & pygame.KMOD_SHIFT:
add_boids(boids, 100)
else:
add_boids(boids, 10)
elif event.key == pygame.K_DOWN:
# remove boids
if mods & pygame.KMOD_SHIFT:
boids.remove(boids.sprites()[:100])
else:
boids.remove(boids.sprites()[:10])
#ADD STUFF LIKE THE SLIDER AND STUFF
for b in boids:
b.update(dt, boids)
def draw(screen, background, boids, obstaclearray):
#redrawing the whole window
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
pygame.display.update(dirty)
default_boids = 0
default_geometry = "1000x1000"
# Initialise pygame.
pygame.init()
pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.KEYUP])
# keep a good framerate so the graphics are better
fps = 60.0
fpsClock = pygame.time.Clock()
# Set up pygamme window
window_width, window_height = 800,600
flags = DOUBLEBUF
screen = pygame.display.set_mode((window_width, window_height), flags)
screen.set_alpha(None)
background = pygame.Surface(screen.get_size()).convert()
background.fill(pygame.Color('black'))
boids = pygame.sprite.RenderUpdates()
add_boids(boids, default_boids)
obstaclearray = []
defaultcircleobstacle = CircularObstacle(200,200,13)
obstaclearray.append(defaultcircleobstacle)
#The "game loop"
dt = 1/fps # here dt means the amount of time elapsed since the last frame
#it seems like thie is a forever loop but in reality this is not since in the update method we provide functinality to quit the program
while True:
update(dt, boids)
draw(screen, background, boids, obstaclearray)
dt = fpsClock.tick(fps)
When you call pygame.display.update() you have 2 options. You can call it without any parameter. In this case the complete screen is updated.
pygame.display.update()
Or call it with a list of rectangular regions that need to be updated. In this case, only the rectangular areas will be updated.
pygame.display.update(rect_list)
You do the 2nd option, but the areas where the circles are drawn are not in the dirty list, therefore this regions are not updated.
pygame.display.update(dirty)
Either update the whole screen with pygame.display.update() or add the regions of the circles to the dirty list:
def draw(screen, background, boids, obstaclearray):
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
dirty_rect = pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
dirty.append(dirty_rect)
pygame.display.update(dirty)
Everything is working fine except that font thing, i don't know why it's happening and it's not even showing any error. And not displaying the Text on screen.
# import library here
import pygame
import time
import sys
# display init
display_width = 800
display_height = 600
# game initialization done
pygame.init()
# game display changed
gameDisplay = pygame.display.set_mode((display_width, display_height))
# init font object with font size 25
font = pygame.font.SysFont(None, 25)
def message_to_display(msg, color):
screen_text = font.render(msg, True, color)
gameDisplay.blit(screen_text, [10, 10])
message_to_display("You Lose", red)
time.sleep(3)
pygame.quit()
# you can signoff now, everything looks good!
quit()
The reason you see nothing is because you haven't updated or 'flipped' the display. After you've created the text Surface and blit it to the gameDisplay Surface you have to update/'flip' the display so it becomes visible to the user.
So, between message_to_display("You Lose", red) and time.sleep(3) you put pygame.display.update()or pygame.display.flip() (it doesn't matter which). Like this:
# import library here
import pygame
import time
import sys
# display init
display_width = 800
display_height = 600
# game initialization done
pygame.init()
# game display changed
gameDisplay = pygame.display.set_mode((display_width, display_height))
# init font object with font size 25
font = pygame.font.SysFont(None, 25)
def message_to_display(msg, color):
screen_text = font.render(msg, True, color)
gameDisplay.blit(screen_text, [10, 10])
message_to_display("You Lose", red)
pygame.display.update() # VERY IMPORTANT! THIS IS WHAT YOU MISSED!
time.sleep(3)
pygame.quit()
# you can signoff now, everything looks good!
quit()
Also, as J.J. Hakala pointed out, you have to define red in message_to_display("You Lose", red) as well.
I'm currently using Pygame to develop a game and I've decided that I'd group all my GUI objects together in a dictionary like so:
gui_objects = {
# The GuiObject parameters define a rect (for positioning) and background colour.
"healthbar" : GuiObject((10, 10, 100, 20), Colour.BLUE),
"mini_map" : GuiObject((10, 400, 50, 50), Colour.WHITE)
}
The reason I'm grouping GUI objects like this is so I can easily modify them like:
gui_objects.get("mini_map").set_enabled(false)
Now, when I want to render my GUI objects to the screen, I simply did this:
for key, value in gui_objects.iteritems():
value.render(screen)
This works, but for some reason, the white "mini_map" GuiObject gets rendered underneath the "healthbar" GuiObject. I decided to put the "mini_map" above the "healthbar" in the dictionary, but that changed nothing. But now here's the weird part. If I render the GUI objects separately, that is, by calling their render() functions separately, like this:
gui_objects.get("healthbar").render(screen)
gui_objects.get("mini_map" ).render(screen)
Then the GuiObjects overlap properly. My question now is, why do my GuiObjects not overlap properly when I render them using the for loop? Yet they overlap just fine when rendered separately?
Unfortunately, I can't upload images because I don't have enough reputation ¬_¬ But, here's the source code:
import pygame
# Just a definition of a few colours
class Colour(object):
WHITE = (255, 255, 255)
GREY = (128, 128, 128)
BLUE = ( 64, 128, 255)
# GuiObject is just a rectangle with a colour at the moment (for testing purposes).
class GuiObject(object):
def __init__(self, rect, colour):
self.rect = rect
self.colour = colour
def render(self, screen):
pygame.draw.rect(screen, self.colour, self.rect)
def main():
############################################################################
# Initialise
pygame.init()
screen = pygame.display.set_mode((800, 600))
# if render_type = 0 then render GuiObjects using a for loop
# if render_type = 1 then render GuiObjects separately.
render_type = 1
gui_objects = {
"hpbar_bg" : GuiObject(( 0, 0, 150, 600), (Colour.BLUE)),
"enemy_hpbar" : GuiObject((10, 10, 200, 400), (Colour.WHITE)),
}
############################################################################
# Main loop
while True:
########################################################################
# Event Handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
########################################################################
# Render
screen.fill((0, 0, 0))
# Here, I render the GuiObjects.
if render_type == 0:
# This for loop messes up overlapping.
for key, value in gui_objects.iteritems():
value.render(screen)
elif render_type == 1:
# This works fine.
gui_objects.get("hpbar_bg").render(screen)
gui_objects.get("enemy_hpbar").render(screen)
pygame.display.flip()
if __name__ == "__main__":
main()
Has anyone got a clue as to why overlapping GuiObjects doesn't work properly when using the for loop?
I hope I've explained myself clearly enough. If not, just ask and I'll try to clarify.
Because dictionaries aren't ordered and it is drawing the objects in a different order than your other method.
A dictionary is just not designed for holding objects that need to stay in order.
You can either:
Use a pygame render group instead.
This would be the pygame-recommended way to store groups of objects that are going to be drawn, however you would have to convert your GUI objects into sprites I believe. EDIT: Also, pygame render groups are still not ordered so this wouldn't solve your particular problem.
Use a list of tuples [(name,value),...] instead (this will be the most similar to your current method because that's actually what the iteritems() dictionary method returns).
Here is your code re-written using method 2:
import pygame
# Just a definition of a few colours
class Colour(object):
WHITE = (255, 255, 255)
GREY = (128, 128, 128)
BLUE = ( 64, 128, 255)
# GuiObject is just a rectangle with a colour at the moment (for testing purposes).
class GuiObject(object):
def __init__(self, rect, colour):
self.rect = rect
self.colour = colour
def render(self, screen):
pygame.draw.rect(screen, self.colour, self.rect)
def main():
############################################################################
# Initialise
pygame.init()
screen = pygame.display.set_mode((800, 600))
# if render_type = 0 then render GuiObjects using a for loop
# if render_type = 1 then render GuiObjects separately.
render_type = 0
gui_objects = [
("hpbar_bg", GuiObject(( 0, 0, 150, 600), (Colour.BLUE))),
("enemy_hpbar", GuiObject((10, 10, 200, 400), (Colour.WHITE))),
]
############################################################################
# Main loop
while True:
########################################################################
# Event Handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
########################################################################
# Render
screen.fill((0, 0, 0))
# Here, I render the GuiObjects.
if render_type == 0:
# This for loop no longer messes up overlapping.
for key, value in gui_objects:
value.render(screen)
pygame.display.flip()
if __name__ == "__main__":
main()
Because you would like to be able to do things like gui_objects.hpbar_bg.set_enabled(False) then I would look into a third option:
Restructuring your code to contain the GUI within a class itself, and then order the drawing of its components within its draw method.
Here is an example of 3 that doesn't deviate too far from what you've got:
import pygame
# Just a definition of a few colours
class Colour(object):
WHITE = (255, 255, 255)
GREY = (128, 128, 128)
BLUE = ( 64, 128, 255)
# GuiObject is just a rectangle with a colour at the moment (for testing purposes).
class GuiObject(object):
def __init__(self, rect, colour):
self.rect = rect
self.colour = colour
self.enabled = True
def render(self, screen):
if self.enabled:
pygame.draw.rect(screen, self.colour, self.rect)
class Gui(object):
def __init__(self):
self.hpbar_bg = GuiObject(( 0, 0, 150, 600), (Colour.BLUE))
self.enemy_hpbar = GuiObject((10, 10, 200, 400), (Colour.WHITE))
self.enabled = True
def render(self, screen):
#render my gui in the order i want
if self.enabled:
self.hpbar_bg.render(screen)
self.enemy_hpbar.render(screen)
def main():
############################################################################
# Initialise
pygame.init()
screen = pygame.display.set_mode((800, 600))
gui = Gui()
#uncomment to get the enabled/disabled behavior
#gui.hpbar_bg.enabled = False
#or disable the whole gui
#gui.enabled = False
############################################################################
# Main loop
while True:
########################################################################
# Event Handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
########################################################################
# Render
screen.fill((0, 0, 0))
# Render GUI
gui.render(screen)
pygame.display.flip()
if __name__ == "__main__":
main()
This question already has answers here:
pygame - How to display text with font & color?
(7 answers)
Closed 2 years ago.
I can't figure out to display text in pygame.
I know I can't use print like in regular Python IDLE but I don't know how.
import pygame, sys
from pygame.locals import *
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = ( 255, 0, 0)
pygame.init()
size = (700, 500)
screen = pygame.display.set_mode(size)
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('P.Earth')
while 1: # main game loop
for event in pygame.event.get():
if event.type == QUIT:
pygame.display.update()
import time
direction = ''
print('Welcome to Earth')
pygame.draw.rect(screen, RED, [55,500,10,5], 0)
time.sleep(1)
This is only the beginning part of the whole program.
If there is a format that will allow me to show the text I type in the pygame window that'd be great. So instead of using print I would use something else. But I don't know what that something else is.
When I run my program in pygame it doesn't show anything.
I want the program to run in the pygame window instead of it just running in idle.
You can create a surface with text on it. For this take a look at this short example:
pygame.font.init() # you have to call this at the start,
# if you want to use this module.
my_font = pygame.font.SysFont('Comic Sans MS', 30)
This creates a new object on which you can call the render method.
text_surface = my_font.render('Some Text', False, (0, 0, 0))
This creates a new surface with text already drawn onto it.
At the end you can just blit the text surface onto your main screen.
screen.blit(text_surface, (0,0))
Bear in mind, that every time the text changes, you have to recreate the surface again, to see the new text.
There's also the pygame.freetype module which is more modern, works with more fonts and offers additional functionality.
Create a font object with pygame.freetype.SysFont() or pygame.freetype.Font if the font is inside of your game directory.
You can render the text either with the render method similarly to the old pygame.font.Font.render or directly onto the target surface with render_to.
import pygame
import pygame.freetype # Import the freetype module.
pygame.init()
screen = pygame.display.set_mode((800, 600))
GAME_FONT = pygame.freetype.Font("your_font.ttf", 24)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((255,255,255))
# You can use `render` and then blit the text surface ...
text_surface, rect = GAME_FONT.render("Hello World!", (0, 0, 0))
screen.blit(text_surface, (40, 250))
# or just `render_to` the target surface.
GAME_FONT.render_to(screen, (40, 350), "Hello World!", (0, 0, 0))
pygame.display.flip()
pygame.quit()
When displaying I sometimes make a new file called Funk. This will have the font, size etc. This is the code for the class:
import pygame
def text_to_screen(screen, text, x, y, size = 50,
color = (200, 000, 000), font_type = 'data/fonts/orecrusherexpand.ttf'):
try:
text = str(text)
font = pygame.font.Font(font_type, size)
text = font.render(text, True, color)
screen.blit(text, (x, y))
except Exception, e:
print 'Font Error, saw it coming'
raise e
Then when that has been imported when I want to display text taht updates E.G score I do:
Funk.text_to_screen(screen, 'Text {0}'.format(score), xpos, ypos)
If it is just normal text that isn't being updated:
Funk.text_to_screen(screen, 'Text', xpos, ypos)
You may notice {0} on the first example. That is because when .format(whatever) is used that is what will be updated. If you have something like Score then target score you'd do {0} for score then {1} for target score then .format(score, targetscore)
This is slighly more OS independent way:
# do this init somewhere
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
font = pygame.font.Font(pygame.font.get_default_font(), 36)
# now print the text
text_surface = font.render('Hello world', antialias=True, color=(0, 0, 0))
screen.blit(text_surface, dest=(0,0))