Modify alpha of Surface pixels directly in pygame - python

I have a Surface in PyGame. I want to modify the alpha values of pixels directly. I've tried doing it with the various methods that access the alpha values, but they don't seem to work.
from screeninfo import get_monitors
import pygame, os, numpy.random, pygame.surfarray
pygame.init()
FPS = 60
CLOCK = pygame.time.Clock()
monitor_info = get_monitors()
x = 0
y = 0
width = monitor_info[0].width
height = monitor_info[0].height
if len(monitor_info) > 1:
if monitor_info[0].x < 0:
x = 0
else:
x = monitor_info[0].width
width = monitor_info[1].width
height = monitor_info[1].height
os.environ['SDL_VIDEO_WINDOW_POS'] = "{},0".format(x)
pygame.display.init()
pygame.mouse.set_visible(False)
screen_info = pygame.display.Info()
screen_size = width, height
base_screen = pygame.display.set_mode(screen_size, pygame.NOFRAME)
base_screen.fill([100, 100, 100])
board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 255, 255, 255])
base_screen.blit(surface, (0,0))
pygame.display.flip()
pixels = numpy.random.uniform(low=0, high=255, size=(board_size[0], board_size[1], 3))
transparency = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
while True:
events = pygame.event.get()
ms = CLOCK.tick(FPS)
print('\r {} '.format(ms), end='')
# pygame.surfarray.blit_array(surface, pixels)
aa = pygame.surfarray.pixels_alpha(surface)
aa = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
del aa
# for i in range(board_size[0]):
# for j in range(board_size[1]):
# a = surface.get_at((i,j))
# a[3] = 0
# surface.set_at((i,j), a)
base_screen.blit(surface, (0,0))
pygame.display.flip()
I've tried both things in the loop (pixels_array, and get_at/set_at) but neither works--the image just stays white (if I set the initial alpha to 0, it stays transparent). Does anyone know how to set per-pixel alpha values for a Surface?

I found your problem!! The reason why you can't see alpha is:
a) you first set surface alpha to 255, surface.fill([255, 255, 255, 255])
b) I believe aa = pygame.surfarray.pixels_alpha(surface) aa = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8') aren't working, however pygame.surfarray.blit_array(surface, pixels) do work (produce colours) but I don't think they have any actual Alpha.
c) you need to fill the base_screen and THEN blit you surface. Such a common mistake but this is the main the problem.
And finally, Tim Robert's comment about the for loop, will definitely get you your alpha!
Here is it re-written to work (without screeninfo as I don't have that library currently):
import pygame, os, numpy.random, pygame.surfarray
from random import randint
pygame.init()
FPS = 60
CLOCK = pygame.time.Clock()
x = 50
y = 50
width = 500
height = 500
os.environ['SDL_VIDEO_WINDOW_POS'] = "{},0".format(x)
#pygame.display.init(), don't actually need this
pygame.mouse.set_visible(False)
screen_info = pygame.display.Info()
screen_size = width, height
base_screen = pygame.display.set_mode(screen_size, pygame.NOFRAME)
base_screen.fill([100, 100, 100])
board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 0, 0]) ###could also be surface.fill([255,0,0,255]) to set the whole surface's alpha straight up if you didn't want to change each pixel later
while True:
#just so you can quit this program
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
raise SystemExit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
pygame.quit()
raise SystemExit()
ms = CLOCK.tick(FPS)
for i in range(board_size[0]):
for j in range(board_size[1]):
a = surface.get_at((i,j))
a[3] = randint(0, 128)#OR SET TO REQUIRED ALPHA VALUE
surface.set_at((i,j), a)
##################################################
base_screen.fill([100, 100, 100]) #NEED TO DO THIS
##################################################
base_screen.blit(surface, (0,0))
pygame.display.flip()
(by the way, I used red as the second surface colour as I thought it would stand out better)
Edit
As Eternal Ambiguity stated in the comments, here is the much, much faster version, in place of the for loops:
aa = pygame.surfarray.pixels_alpha(surface)
aa[:] = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
del aa

pygame.Surface.blit() does not replace the pixels in the target surface with the source surface. It mixes (blends) the pixels of the surfaces. Actually you are blending the new surface with the old previous surface every frame. This means that the area of the surface appears uniformly colored within milliseconds. You have to clear the screen in every frame:
while True:
# [...]
base_screen.fill((0, 0, 0))
base_screen.blit(surface, (0,0))
pygame.display.flip()
Minimal example:
import pygame
pygame.init()
width, height = 200, 200
base_screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 0, 0])
for x in range(board_size[0]):
for y in range(board_size[1]):
a = surface.get_at((x, y))
a[3] = round(y / board_size[1] * 255)
surface.set_at((x, y), a)
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
base_screen.fill((100, 100, 100))
base_screen.blit(surface, surface.get_rect(center = base_screen.get_rect().center))
pygame.display.flip()

Related

Python PyMunk acts weirdly

here is the problem :
My pymunk code is from https://pymunk-tutorial.readthedocs.io/en/latest/shape/shape.html
But when i launch the program, a weird force acts on the segment.
See the result : https://youtu.be/s4a0RLb6Y6k
See the code : (without useless part about dependencies)
import pymunk
import pymunk.pygame_util
import pygame
from pygame.locals import *
pygame.init()
width, height = 1280, 720
window = pygame.display.set_mode((width, height), FULLSCREEN)
draw_options = pymunk.pygame_util.DrawOptions(window)
window.fill((0,0,0))
run = True
space = pymunk.Space()
space.gravity = 0, -1000
b0 = space.static_body
sol = pymunk.Segment(b0, (0, 30), (width, 30), 10)
sol.elasticity = 1
body = pymunk.Body(mass=1, moment=1000)
body.position = (width/2, 50)
segment = pymunk.Segment(body, (width/2-20,50), (width/2+20,50), 1)
segment.elasticity = 0.95
space.add(sol, body, segment)
while run == True:
space.step(0.01)
window.fill((0,0,0))
pygame.draw.line(window, (0, 255, 0), (0, height-30), (width, height-30), 1)
space.debug_draw(draw_options)
pygame.display.flip()
pygame.time.Clock().tick(60)
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
run = False
if event.type == QUIT:
run = False
mouse_x, mouse_y = pygame.mouse.get_pos()
pass
continue
I don't see where is the problem..
Thanks by advance
One common reason why strange behavior like that in the video happens is because center of gravity of the shape is not in the actual center. Try changing so that you make the Segment around the its center (0,0), where the body is.
Something like this maybe
segment = pymunk.Segment(body, (-20,0), (20,0), 1)
If you want to move it you can adjust the position of the body instead.
So the a an b values are relative from the center of mass of the body precited.
(with a and b from shape = pymunk.Segment(body, a, b, thickness) )
Thanks very much !

[Pygame]: Move surface towards position

Is there an algorithm to change a set of coordinates to make it move towards another set of coordinates?
Like, if I have ax,ay=(20,30) a
#fonctions
import pygame, sys
from pygame.locals import *
pygame.init()
fpsClock = pygame.time.Clock()
windows= pygame.display.set_mode((1200,500), 0, 32)
pygame.display.set_caption('testing')
size = width, height = (32, 32)
PNGImg = pygame.Surface(size)
sob_X=575
sob_Y=250
FPS = 15 # frames per second setting
RED= (255, 0, 0) #red color
while True:
windows.fill((54, 141, 197))
pygame.draw.circle(windows, RED, (70, 80), 20, 0)
windows.blit(PNGImg, (sob_X,sob_Y))
dis_X=sob_X-600 #cicle X_Cordinate
dis_Y=sob_X-870 #cicle Y_Cordinate
if dis_X<0:
sob_X=sob_X+ dis_X
if sob_X==70 and dis_Y<0:
sob_Y=sob_X+dis_Y
elif sob_X==70 and dis_Y>0:
sob_Y=sob_Y-dis_Y
elif dis_X>0:
sob_X=sob_X -dis_X
if sob_X==70 and dis_Y<0:
sob_Y=sob_Y+dis_Y
elif sob_X==70 and dis_Y>0:
sob_Y=sob_Y-dis_Y
windows.blit(PNGImg, (sob_X,sob_Y))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
fpsClock.tick(FPS)
pygame.quit()
nd bx,by=(40,60)
How can I change ax and ay (coordinate of the image"surface") to equal bx and by? (Preferably an algorithm achievable in python.)
i tried to make algorithme but in the end don't work
thanks you
Replying here as I can't comment yet, but from the question itself, do you mean to do ax, ay = bx, by ? You can simply do that, or even individual ones like ax = by.
With an example from the terminal:
>>> ax,ay=(20,30)
>>> bx,by=(40,60)
>>> ax, ay = bx, by
>>> ax, ay
(40, 60)

How to fade in a text or an image with PyGame

Can someone help me on this ? :
I try to make a title that appear smoothly on the user screen :
def image(name,x,y,u):
screen.blit(name,(x,y))
if u = 1
pygame.display.update()
Window == 'main'
While windows = 'main':
image(background,0,0)
image(title, 640, 120)
pygame.display.update()
But texte appaers suddently and not as I would like...
You want to use transparency, and pygame uses three different types:
There are three types of transparency supported in pygame: colorkeys, surface alphas, and pixel alphas. Surface alphas can be mixed with colorkeys, but an image with per pixel alphas cannot use the other modes. Colorkey transparency makes a single color value transparent. Any pixels matching the colorkey will not be drawn. The surface alpha value is a single value that changes the transparency for the entire image. A surface alpha of 255 is opaque, and a value of 0 is completely transparent.
Per pixel alphas are different because they store a transparency value for every pixel. This allows for the most precise transparency effects, but it also the slowest. Per pixel alphas cannot be mixed with surface alpha and colorkeys.
So let's use colorkey to create a transparent Surface and blit some text to it, and then use surface alpha to create the fade in effect by calling set_alpha and the Surface:
import pygame
def main():
screen = pygame.display.set_mode((300, 100))
FONT = pygame.font.SysFont(None, 64)
text = FONT.render('Hello World', False, pygame.Color('darkorange'))
surf = pygame.Surface(text.get_rect().size)
surf.set_colorkey((1,1,1))
surf.fill((1,1,1))
surf.blit(text, (0, 0))
clock = pygame.time.Clock()
alpha = 0
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
alpha = (alpha + 1) % 256
surf.set_alpha(alpha)
screen.fill(pygame.Color('dodgerblue'))
screen.blit(surf, (20, 20))
clock.tick(120)
print(alpha)
pygame.display.update()
if __name__ == '__main__':
pygame.init()
main()
pygame.Surface.blit() already does what you want. It bends a surface to an other surface, depended on its alpha channel. All you have to do is to set the alpha channel by pygame.Surface.set_alpha():
image.set_alpha(min(1.0,alpha)*255)
Write a function, which "blends" a surface to the screen. In the following function BlendSurface, the parameter alpha is the opacity value of the surface in range[0, 1]. 0.0 would generate a completely transparent (invisible) surface. 1.0 would generate a completely opaque surface:
def BlendSurface(image, pos, alpha):
image.set_alpha(min(1.0,alpha)*255)
screen.blit(image, pos)
See the simple demo program:
import pygame
import pygame.font
pygame.init()
BLUE = ( 0, 0, 255)
YELLOW = (255, 255, 0)
size = (800,600)
screen = pygame.display.set_mode(size)
def BlendSurface(image, pos, alpha):
image.set_alpha(min(1.0,alpha)*255)
screen.blit(blendImage, pos)
clock = pygame.time.Clock()
font = pygame.font.SysFont('Times New Roman', 100)
text = font.render('blend text', False, YELLOW)
i = 0
done = False
while not done:
clock.tick(60)
i += 1
if i > 200:
i = 0
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLUE)
BlendSurface(text, (100, 100), i/200)
pygame.display.flip()

Drawing a square wave in pygame

This python code illustrates a sin wave in a pygame window.
I want to draw a square wave in this very same fashion as well, though I have no idea what this code might be or how to draw a square wave / how one is constructed in python.
Does anybody know how I can do this? Is it possible with the same imports or will I need new ones?
Code:
import sys, pygame, math
from pygame.locals import *
# set up a bunch of constants
WHITE = (255, 255, 255)
DARKRED = (128, 0, 0)
RED = (255, 0, 0)
BLACK = ( 0, 0, 0)
BGCOLOR = WHITE
WINDOWWIDTH = 640 # width of the program's window, in pixels
WINDOWHEIGHT = 480 # height in pixels
WIN_CENTERX = int(WINDOWWIDTH / 2) # the midpoint for the width of the window
WIN_CENTERY = int(WINDOWHEIGHT / 2) # the midpoint for the height of the window
FPS = 160 # frames per second to run at
AMPLITUDE = 80 # how many pixels tall the waves with rise/fall.
# standard pygame setup code
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Trig Waves')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
# variables that track visibility modes
showSine = True
pause = False
xPos = 0
step = 0 # the current input f
posRecord = {'sin': []} # keeps track of the ball positions for drawing the waves
# making text Surface and Rect objects for various labels
sinLabelSurf = fontObj.render('sine', True, RED, BGCOLOR)
sinLabelRect = sinLabelSurf.get_rect()
instructionsSurf = fontObj.render('Press Q to toggle wave. P to pause.', True, BLACK, BGCOLOR)
instructionsRect = instructionsSurf.get_rect()
instructionsRect.left = 10
instructionsRect.bottom = WINDOWHEIGHT - 10
# main application loop
while True:
# event handling loop for quit events
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# check for key presses that toggle pausing and wave visibility
if event.type == KEYUP:
if event.key == K_q:
showSine = not showSine
elif event.key == K_p:
pause = not pause
# fill the screen to draw from a blank state
DISPLAYSURF.fill(BGCOLOR)
# draw instructions
DISPLAYSURF.blit(instructionsSurf, instructionsRect)
# sine wave
yPos = -1 * math.sin(step) * AMPLITUDE
posRecord['sin'].append((int(xPos), int(yPos) + WIN_CENTERY))
if showSine:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, RED, (int(xPos), int(yPos) + WIN_CENTERY), 10)
sinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20)
DISPLAYSURF.blit(sinLabelSurf, sinLabelRect)
# draw the waves from the previously recorded ball positions
if showSine:
for x, y in posRecord['sin']:
pygame.draw.circle(DISPLAYSURF, DARKRED, (x, y), 4)
# draw the border
pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1)
pygame.display.update()
FPSCLOCK.tick(FPS)
if not pause:
xPos += 0.5
if xPos > WINDOWWIDTH:
xPos = 0
posRecord = {'sin': []}
step = 0
else:
step += 0.008
step %= 2 * math.pi
Python ver.2.6, Pygame ver.1.9.2
I made some modifications to add squared wave.
See places with ### HERE.
import sys, pygame, math
from pygame.locals import *
# set up a bunch of constants
WHITE = (255, 255, 255)
DARKRED = (128, 0, 0)
RED = (255, 0, 0)
BLACK = ( 0, 0, 0)
GREEN = ( 0, 255, 0) ### HERE
BLUE = ( 0, 0, 255) ### HERE
BGCOLOR = WHITE
WINDOWWIDTH = 640 # width of the program's window, in pixels
WINDOWHEIGHT = 480 # height in pixels
WIN_CENTERX = int(WINDOWWIDTH / 2) # the midpoint for the width of the window
WIN_CENTERY = int(WINDOWHEIGHT / 2) # the midpoint for the height of the window
FPS = 160 # frames per second to run at
AMPLITUDE = 80 # how many pixels tall the waves with rise/fall.
# standard pygame setup code
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Trig Waves')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
# variables that track visibility modes
showSine = True
showSquare = True ### HERE
pause = False
xPos = 0
step = 0 # the current input f
### HERE
posRecord = {'sin': [], 'square': []} # keeps track of the ball positions for drawing the waves
# making text Surface and Rect objects for various labels
### HERE
squareLabelSurf = fontObj.render('square', True, BLUE, BGCOLOR)
squareLabelRect = squareLabelSurf.get_rect()
sinLabelSurf = fontObj.render('sine', True, RED, BGCOLOR)
sinLabelRect = sinLabelSurf.get_rect()
instructionsSurf = fontObj.render('Press Q to toggle wave. P to pause.', True, BLACK, BGCOLOR)
instructionsRect = instructionsSurf.get_rect()
instructionsRect.left = 10
instructionsRect.bottom = WINDOWHEIGHT - 10
### HERE
yPosSquare = AMPLITUDE # starting position
# main application loop
while True:
# event handling loop for quit events
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# check for key presses that toggle pausing and wave visibility
if event.type == KEYUP:
if event.key == K_q:
showSine = not showSine
elif event.key == K_p:
pause = not pause
# fill the screen to draw from a blank state
DISPLAYSURF.fill(BGCOLOR)
# draw instructions
DISPLAYSURF.blit(instructionsSurf, instructionsRect)
# sine wave
yPos = -1 * math.sin(step) * AMPLITUDE
posRecord['sin'].append((int(xPos), int(yPos) + WIN_CENTERY))
if showSine:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, RED, (int(xPos), int(yPos) + WIN_CENTERY), 10)
sinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20)
DISPLAYSURF.blit(sinLabelSurf, sinLabelRect)
# draw the waves from the previously recorded ball positions
if showSine:
for x, y in posRecord['sin']:
pygame.draw.circle(DISPLAYSURF, DARKRED, (x, y), 4)
### HERE - drawing horizontal lines
# square
posRecord['square'].append((int(xPos), int(yPosSquare) + WIN_CENTERY))
if showSquare:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, GREEN, (int(xPos), int(yPosSquare) + WIN_CENTERY), 10)
squareLabelRect.center = (int(xPos), int(yPosSquare) + WIN_CENTERY + 20)
DISPLAYSURF.blit(squareLabelSurf, squareLabelRect)
# draw the waves from the previously recorded ball positions
if showSquare:
for x, y in posRecord['square']:
pygame.draw.circle(DISPLAYSURF, BLUE, (x, y), 4)
# draw the border
pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1)
pygame.display.update()
FPSCLOCK.tick(FPS)
if not pause:
xPos += 0.5
if xPos > WINDOWWIDTH:
#sine ### HERE
xPos = 0
posRecord['sin'] = []
step = 0
# square ### HERE
yPosSquare = AMPLITUDE
posRecord['square'] = []
else:
#sine ### HERE
step += 0.008
#step %= 2 * math.pi
# square ### HERE
# jump top and bottom every 100 pixels
if xPos % 100 == 0:
yPosSquare *= -1
# add vertical line
for x in range(-AMPLITUDE, AMPLITUDE):
posRecord['square'].append((int(xPos), int(x) + WIN_CENTERY))

Pygame, how to draw a shape to the screen and delete the previous surface?

So I have this code, and it does what it's supposed to fine. What I want it to do is randomly scale the square by different amounts, which it does. My problem lies with the blit function, my square only seems to scale up because blit doesn't delete the old shape it just copies the new one to the surface.
How can I make the shape expand and shrink, and not just expand?
My code:
import sys, random, pygame
from pygame.locals import *
pygame.init()
w = 640
h = 480
screen = pygame.display.set_mode((w,h))
morphingShape = pygame.Surface((20,20))
morphingShape.fill((255, 137, 0)) #random colour for testing
morphingRect = morphingShape.get_rect()
def ShapeSizeChange(shape, screen):
x = random.randint(-21, 20)
w = shape.get_width()
h = shape.get_height()
if w + x > 0 and h + x > 0:
shape = pygame.transform.smoothscale(shape, (w + x, h + x))
else:
shape = pygame.transform.smoothscale(shape, (w - x, h - x))
shape.fill((255, 137, 0))
rect = shape.get_rect()
screen.blit(shape, rect)
return shape
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
morphingShape = ShapeSizeChange(morphingShape, screen)
pygame.display.update()
On every frame (each iteration of the While loop) you should erase the screen. By default the screen (window) color is black, so you should clear the screen by calling screen.fill( (0,0,0) ). Below is the full code, now working as you expect:
import sys, random, pygame
from pygame.locals import *
pygame.init()
w = 640
h = 480
screen = pygame.display.set_mode((w,h))
morphingShape = pygame.Surface((20,20))
morphingShape.fill((255, 137, 0)) #random colour for testing
morphingRect = morphingShape.get_rect()
# clock object that will be used to make the animation
# have the same speed on all machines regardless
# of the actual machine speed.
clock = pygame.time.Clock()
def ShapeSizeChange(shape, screen):
x = random.randint(-21, 20)
w = shape.get_width()
h = shape.get_height()
if w + x > 0 and h + x > 0:
shape = pygame.transform.smoothscale(shape, (w + x, h + x))
else:
shape = pygame.transform.smoothscale(shape, (w - x, h - x))
shape.fill((255, 137, 0))
rect = shape.get_rect()
screen.blit(shape, rect)
return shape
while True:
# limit the demo to 50 frames per second
clock.tick( 50 );
# clear screen with black color
# THIS IS WHAT WAS REALLY MISSING...
screen.fill( (0,0,0) )
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
morphingShape = ShapeSizeChange(morphingShape, screen)
pygame.display.update()
Note that just the addition of screen.fill( (0,0,0) ) solves your question.

Categories