Python countdown - python

I currently took a snippet off the web that allows for 4 pictures to be taken with a camera. This works well.
I've then tried to take another snippet off the web that would count down before it takes a picture, this gave me great headache, and I'm wondering if someone much smarter can figer this out for me...
surface = pygame.display.set_mode((0,0))
fontObj = pygame.font.Font("freesansbold.ttf", 100)
textSurfaceObj = fontObj.render("3", True, (255, 0, 0))
textRectObj = textSurfaceObj.get_rect()
textRectObj.center = (surface.get_width() / 2, surface.get_height() / 2)
def show_image(image_path):
screen = init_pygame()
img=pygame.image.load(image_path)
img = pygame.transform.scale(img,(transform_x,transfrom_y))
screen.blit(img,(offset_x,offset_y))
pygame.display.flip()
def init_pygame():
pygame.init()
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
pygame.display.set_caption('Pictures')
pygame.mouse.set_visible(False) #hide the mouse cursor
return pygame.display.set_mode(size, pygame.FULLSCREEN)
print "Taking pics"
now = time.strftime("%Y-%m-%d-%H:%M:%S")
try:
for i, filename in enumerate(camera.capture_continuous(config.file_path + now + '-' + '{counter:02d}.jpg')):
print(filename)
for y in range(3,0,-1):
surface.fill((0,0,0,0))
textSurfaceObj = fontObj.render(str(y), True, (255, 0, 0))
surface.blit(textSurfaceObj, textRectObj)
pygame.display.update()
pygame.time.wait(1000)
sleep(capture_delay)
if i == total_pics-1:
break
finally:
camera.stop_preview()
camera.close()
It returns me this:
Traceback (most recent call last):
File "pics.py", line 58, in <module>
fontObj = pygame.font.Font("freesansbold.ttf", 100)
pygame.error: font not initialized
I was under the impression that if pygame.init() was done, fonts should be initialised?

That's because you invoke:
pygame.font.Font("freesansbold.ttf", 100)
before you call:
pygame.init()
probably in line 58 of your file pics.py.You've provided the answer to your question yourself. You might be smarter than you think.

Related

pygame.surfarray.pixels3d doesn’t give correct pixel values with a text surface

So, I was trying to get all the pixels occupied by some text, but I realised pygame.surfarray.pixels3d doesn’t give the expected output.
Here is what I tried:
import pygame
# import numpy as np
pygame.init()
width, height = 800, 800
win = pygame.display.set_mode((width, height))
font = pygame.font.SysFont("Monospace", 100, True)
text = font.render("test", True, (255, 255, 255))
# saves the correct image with text
pygame.image.save(text,"image.png")
# gives all (255,255,255)?!
pixels = pygame.surfarray.pixels3d(text).copy()
# remake surface from these pixels - doesn’t give the text.
text = pygame.surfarray.make_surface(pixels)
text_rect = text.get_rect(center=(width // 2, height // 2))
def main():
run = True
win.fill((0, 0, 0))
win.blit(text, text_rect)
pygame.display.update()
while run:
for e in pygame.event.get():
if e.type == pygame.QUIT:
run = False
break
if __name__ == "__main__":
main()
pygame.surfarray.pixels3d works perfectly for other surfaces, so I don’t understand if this is a bug in pygame or am I doing something wrong?
Thanks in advance.
EDIT:
I have also tried using surfarray.array3d, but no luck.
You only read the color channels of the Surface also has an alpha channel. Get the color channels (array3d or pixels3d) and the alpha channel (array_alpha or pixels_alpha) and stick them together. Recreate the Surface with pygame.image.frombuffer:
pixels_rgb = pygame.surfarray.array3d(text)
pixels_alpha = pygame.surfarray.array_alpha(text).reshape((*pixels_rgb.shape[0:2], 1))
pixels_rgba = np.concatenate((pixels_rgb, pixels_alpha), 2)
text = pygame.image.frombuffer(pixels_rgba.transpose((1, 0, 2)).copy(order='C'), text.get_size(), 'RGBA')
Alternatively you can create a text with an opaque black background. Set a black color key (set_colorkey) and convert it with convert_alpha:
text = font.render("test", True, (255, 255, 255), (0, 0, 0))
text.set_colorkey(0)
text = text.convert_alpha()
Complete code
import pygame
import numpy as np
pygame.init()
width, height = 800, 800
win = pygame.display.set_mode((width, height))
font = pygame.font.SysFont("Monospace", 100, True)
text1 = font.render("test 1", True, (255, 255, 255))
text2 = font.render("test 2", True, (255, 255, 255), (0, 0, 0))
text2.set_colorkey(0)
text2 = text2.convert_alpha()
pixels_rgb = pygame.surfarray.array3d(text1)
pixels_alpha = pygame.surfarray.array_alpha(text1).reshape((*pixels_rgb.shape[0:2], 1))
pixels_rgba = np.concatenate((pixels_rgb, pixels_alpha), 2)
text1 = pygame.image.frombuffer(pixels_rgba.transpose((1, 0, 2)).copy(order='C'), text1.get_size(), 'RGBA')
pixels = pygame.surfarray.pixels3d(text2).copy()
text2 = pygame.surfarray.make_surface(pixels)
text1_rect = text1.get_rect(center=(width // 2, height // 2 - 50))
text2_rect = text1.get_rect(center=(width // 2, height // 2 + 50))
def main():
run = True
win.fill((0, 0, 0))
win.blit(text1, text1_rect)
win.blit(text2, text2_rect)
pygame.display.update()
while run:
for e in pygame.event.get():
if e.type == pygame.QUIT:
run = False
if __name__ == "__main__":
main()

writing text with pygame from function in seperate .py file

I have a program that needs to often write text on the screen on a textbox. Since I'm already using pygame for something else here and have a separate file for my functions, I thought about going about it this way. As it stands right now my code looks like this:
In the main file:
import mainengine as me, pygame
pygame.init()
pygame.font.init()
screenSize = width, height = 1280, 720
screen = pygame.display.set_mode(screenSize)
pygame.display.set_caption('test')
bg = pygame.image.load("test.jpg")
screen.blit(bg, [0, 0])
me.text('test', 'how do code')
pygame.display.flip()
Then in the secondary file:
def text(speaker, text):
import pygame
white = (255, 255, 255)
gray = (200, 200, 200)
Sfont = pygame.font.Font('freesansbold.ttf', 24)
Lfont = pygame.font.Font('freesansbold.ttf', 18)
tb = pygame.image.load("textbox.png")
tbRect = tb.get_rect()
tbRect = tbRect.move(20, 550)
screen.blit(tb, tbRect)
screen.blit(Sfont.render(speaker, True, gray), [tbRect.x + 10, tbRect.y + 10])
screen.blit(Lfont.render(text, True, white), [tbRect.x + 10, tbRect.y + 35])
This is the error I get when I run the main file:
Traceback (most recent call last):
File "C:\Users\Denis\Desktop\Stuff\Misc Misc\Python\Scripts\Game\Main\test.py", line 18, in <module>
me.text('test', 'how do code')
File "C:\Users\Denis\Desktop\Stuff\Misc Misc\Python\Scripts\Game\Main\mainengine.py", line 66, in text
screen.blit(tb, tbRect)
NameError: name 'screen' is not defined
Putting this in a function would make the code a lot cleaner, so It'd be nice if there was a way to pull it off, chances are its not exactly what I have here. I'm not exactly the best at software development.
Thanks in advance!
I recommend to add an argument for the target surface to the function:
def text(surf, speaker, text):
# [...]
Furthermore, do not load the image and do not recreate the pygame.font.Font, every time when a text is rendered. Create the objects once at initialization. Loading a pygame.Surface and creating a pygame.font.Font object are very time consuming operations:
import pygame
pygame.init()
Sfont = pygame.font.Font('freesansbold.ttf', 24)
Lfont = pygame.font.Font('freesansbold.ttf', 18)
tb = pygame.image.load("textbox.png")
def text(surf, speaker, text):
white = (255, 255, 255)
gray = (200, 200, 200)
tbRect = tb.get_rect(topleft = (20, 550))
surf.blit(tb, tbRect)
surf.blit(Sfont.render(speaker, True, gray), [tbRect.x + 10, tbRect.y + 10])
surf.blit(Lfont.render(text, True, white), [tbRect.x + 10, tbRect.y + 35])
Pass the target surface to the function. For instance:
me.text(screen, 'test', 'how do code')

How do I include this variable in the font.render line?

I am trying to make the outcome of the dice roll be included in a string of text blit to the screen, "You rolled a" + roll
I believe the problem is related to the while loop "while running:" or the order in which I have written the code but I have no idea how to fix it. Any help would be appreciated. I am new to StackOverflow so I apologize in advance if the title/explanation isn't clear enough.
from random import *
import pygame
import sys
from pygame import rect
"""SETTINGS"""
global roll
clock = pygame.time.Clock()
fps = 60
WHITE = (255, 255, 255)
GREY = (200, 200, 200)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
WIDTH = 520
HEIGHT = 500
bg = (255, 255, 255)
"""functions"""
def dice():
roll = randint(1, 6)
print("You rolled a ", roll)
"""init"""
pygame.init()
pygame.font.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Dice")
"""dice image"""
image = pygame.image.load("diceImage.gif").convert()
image2 = image.get_rect()
imageUsed = pygame.transform.scale(image, (WIDTH, HEIGHT))
"""text object"""
surf = pygame.Surface((WIDTH, 60))
font = pygame.font.SysFont("comicsansms", 37)
text = font.render("Click the dice to roll a number", True, (30, 128, 190))
surf2 = pygame.Surface((400, 60))
text2 = font.render(("You rolled a", roll), True, (30, 128, 190))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
dice()
mouse_pos = event.pos
if image2.collidepoint(mouse_pos):
print('button was pressed at {0}'.format(mouse_pos))
screen.fill(bg)
screen.blit(imageUsed, (0, 0))
screen.blit(surf, (0, 0))
screen.blit(surf2, (50, 450))
screen.blit(text, (0, 0))
screen.blit(text2, (50, 450))
pygame.display.update()
clock.tick(fps)
Error message:
Traceback (most recent call last):
File "C:/Users/lee/Documents/PYTHON/Dice/Dice.py", line 50, in <module>
text2 = font.render(("You rolled a", roll), True, (30, 128, 190))
NameError: name 'roll' is not defined
You've put the global statement at the wrong place. The global statement means that the listed identifiers are to be interpreted as globals in the current scope.
Declare and init roll in global namespace, but use the global statement in the function dice, to set the variabel roll in global namespace:
roll = 1
def dice():
global roll
roll = randint(1, 6)
print("You rolled a ", roll)
You have to convert the numerical value roll to a string by str() before you can use it in render() and render the text Surface:
text2 = font.render(("You rolled a " + str(roll)), True, (30, 128, 190))
In the dice() function, roll = won't actually refer to or modify the top-level roll variable, that's where you need the global statement. I'm not sure which IDE you're using, but when pasting the relevant code, mine immediately notifies me that roll = randint(1, 6) Shadows name 'roll' from outer scope.
I think the dice() function should be renamed to something more descriptive, and lose the printing. I'm not knowledgable enough about PyGame to tell if you can get rid of the global variable entirely.
Also, import * is generally bad practice.

How to display text in pygame? [duplicate]

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))

How do I solve an attribute error?

So like I said before my code (Or my current project that I am working on) is riddled with errors. So far I have at least solved a dozen errors or more and honestly I just give up. I mean God knows how many more there are.
The current problem that I am having is an AttributeError which is in my opinion one of the easiest errors to fix however I seem to have gone in to complete spaghetti mode and I have no clue on how to fix the problem.
{The error itself:
Traceback (most recent call last):
File "C:\Users\Burak\Desktop\boxtrial.py", line 87, in <module>
myScreen.addPane("1")
File "C:\Users\Burak\Desktop\boxtrial.py", line 67, in addPane
myPane.drawPane()
File "C:\Users\Burak\Desktop\boxtrial.py", line 19, in drawPane
self.Screen.blit(self.font.render(textToDisplay, True, (black)), (250, 115))
AttributeError: 'Pane' object has no attribute 'Screen'
}
I will list the code below but I feel as if I should explain what I am trying to do so you have some sort of understanding of the code.
Basically in the main loop I call upon the "Class Screen" which helps to create a PyGame screen that comes up once run. On that screen I am trying to get rectangles to appear on the screen in fixed positions (The coordinates are specific but the ones I use on the code are just for test purposes). I then have another class that is called "Pane" and this class is there so that I can draw many instances of the class pane within screen (If that makes sense).
If someone can help me get rid of the error that would be of grate help, but if you think that this is not a good way of solving the problem then please be my guest to come up with or teach me of a better way to do the same thing.
{The code:
import pygame
import sys
from pygame.locals import *
white = (255,255,255)
black = (0,0,0)
objs = []
MAIN_BUTTON = 1
class Pane():
def __init__(self, textToDisplay, coordinates, screen):
self.textToDisplay = textToDisplay
self.coordinates = coordinates
self.screen = screen
def drawPane(self):
self.Screen.blit(self.font.render(textToDisplay, True, (black)), (250, 115))
pygame.draw.rect(self.screen, (black), self.coordinates, 2)
pygame.display.update()
class Screen():
#constants/array(?) outlining the x,y boundaries of each of x10 panes
#Note to self - Remember to change co-ordinate values
NoOfPanes = 0
Panes = []
def __init__(self):
pygame.init()
pygame.display.set_caption('Box Test')
self.font = pygame.font.SysFont('Arial', 25)
Screen = pygame.display.set_mode((1000,600), 0, 32)
self.screen = Screen
self.screen.fill((white))
pygame.display.update()
def addPane(self, textToDisplay):
paneLocs = [(175, 75, 200, 100),
(0, 0, 200, 100),
(600, 400, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100)
]
if self.NoOfPanes > 10:
print("Limit Reached")
else:
myPane = Pane(textToDisplay, paneLocs[self.NoOfPanes], Screen)
myPane.drawPane()
self.NoOfPanes = self.NoOfPanes + 1
pygame.display.update()
def mousePosition(self):
global clickPos
global releasePos
for event in pygame.event.get():
if event.type == MAIN_BUTTON:
self.Pos = pygame.mouse.get_pos()
return MAIN_BUTTON
else:
return False
if __name__ == '__main__':
myScreen = Screen()
myScreen.addPane("1")
myScreen.addPane("2")
myScreen.addPane("3")
myScreen.addPane("4")
while True:
ev = pygame.event.get()
for event in ev:
if event.type == pygame.MOUSEBUTTONUP:
posx,posy = pygame.mouse.get_pos()
if (posx >= 175 and posx <= 375) and (posy >= 75 and posy <= 175):
print("BOB") #Bob was there just for test purposes
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit();
Fix your case.
class Pane():
def __init__(self, textToDisplay, coordinates, screen):
...
self.screen = screen
def drawPane(self):
self.Screen.... # <<< HERE

Categories