I am trying to create a pixel-art making program using pygame (yes, I know there are better packages to use). The way that I am drawing pixels is via surface.set_at(). I need to know how to completely delete a pixel from a pygame surface, so that I can have an eraser tool. I do not want to fill the pixel in with the background color of the window, because that would still render when using pygame.image.save() to get an image file from the drawing. Can someone please help? Here is my code:
import pygame
import sys
import math
pygame.init()
clock = pygame.time.Clock()
pygame.display.set_caption("Pixel Studio v1.1")
screen = pygame.display.set_mode((960, 720), pygame.RESIZABLE)
scroll_speed = 5
canvas_size = (32, 16)
color = (255, 255, 255)
canvas_surf = pygame.Surface(canvas_size, pygame.SRCALPHA)
zoom = 12
mouse_down = False
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.image.save(canvas_surf, "canvas.png")
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
mouse_down = True
if event.button == pygame.BUTTON_WHEELUP:
if zoom > 2:
zoom -= scroll_speed
if event.button == pygame.BUTTON_WHEELDOWN:
zoom += scroll_speed
if event.type == pygame.MOUSEBUTTONUP:
if event.button == pygame.BUTTON_LEFT:
mouse_down = False
if zoom < 2:
zoom = 2
screen.fill((50, 50, 50))
#canvas_surf.fill((200, 200, 200))
canvas_surf_scaled = pygame.transform.scale(canvas_surf,(canvas_surf.get_width() * zoom, canvas_surf.get_height() * zoom))
canvas_rect = canvas_surf_scaled.get_rect(center=(screen.get_width()/2, screen.get_height()/2))
mouse_pos = pygame.mouse.get_pos()
if mouse_down:
canvas_surf.set_at((math.floor((mouse_pos[0] - canvas_rect.x) / zoom), math.floor((mouse_pos[1] - canvas_rect.y) / zoom)), color)
pygame.draw.rect(screen, ( 75, 75, 75), canvas_rect, 1,)
screen.blit(canvas_surf_scaled, canvas_rect)
pygame.display.flip()
clock.tick(60)
As discussed in the comments, you can make a pixel transparent by setting it to a value whose alpha value is zero.
#r #g #b #a
canvas_surf.set_at((x, y), (0, 0, 0, 0))
Related
I'm trying to move a rectangle inside of pygame, and making it scalable.
The scalable rectangle code is something like this then I tried to add an event like this:
from pygame imports *
init()
rect1 = (rect1x, rect1y, 300,300)
rect1x = 0
rect1y = 0
while running:
x,y = mouse.get_pos()
if rect1Pressed == True:
rect1x = x
rect1y = y
for evnt in event.get():
if evnt.type == QUIT:
running = False
if evnt.type == MOUSEBUTTONDOWN:
if evnt.button == 1:
if rect1.collidepoint(mouse.get_pos()):
rect1Pressed == True
How would i incorporate the scalable rectangle and being able to make it move with the mouse? So that the window will follow the mouse motion. So it kinda is like a on your laptop where your able to scale the window and also move it around.
You could check which mouse button is pressed in the elif event.type == pg.MOUSEMOTION: block, e.g. if event.buttons[0]:, and then either move or scale the rect depending on the button. To move the rect, just add the event.rel to the rect.x and rect.y attributes.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
rect1 = pg.Rect(100, 100, 161, 100)
rect2 = pg.Rect(300, 200, 161, 100)
selected_rect = 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 rect in (rect1, rect2):
if rect.collidepoint(event.pos):
selected_rect = rect # Select the colliding rect.
elif event.type == pg.MOUSEBUTTONUP:
selected_rect = None # De-select the rect.
elif event.type == pg.MOUSEMOTION:
if selected_rect is not None: # If a rect is selected.
if event.buttons[0]: # Left mouse button is down.
# Move the rect.
selected_rect.x += event.rel[0]
selected_rect.y += event.rel[1]
else: # Right or middle mouse button.
# Scale the rect.
selected_rect.w += event.rel[0]
selected_rect.h += event.rel[1]
selected_rect.w = max(selected_rect.w, 10)
selected_rect.h = max(selected_rect.h, 10)
screen.fill((30, 30, 30))
pg.draw.rect(screen, (0, 100, 250), rect1)
pg.draw.rect(screen, (0, 200, 120), rect2)
pg.display.flip()
clock.tick(30)
I am trying to draw 5 rectangles all of which I can drag and drop across the screen. I am using pygame. I managed to draw 1 rectangle that I can drag and drop but I can't do it with 5. This is my code:
import pygame
from pygame.locals import *
from random import randint
SCREEN_WIDTH = 1024
SCREEN_HEIGHT = 768
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
pygame.display.set_caption("Moving circles")
rectangle = pygame.rect.Rect(20,20, 17, 17)
rectangle_draging = False
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if rectangle.collidepoint(event.pos):
rectangle_draging = True
mouse_x, mouse_y = event.pos
offset_x = rectangle.x - mouse_x
offset_y = rectangle.y - mouse_y
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
rectangle_draging = False
elif event.type == pygame.MOUSEMOTION:
if rectangle_draging:
mouse_x, mouse_y = event.pos
rectangle.x = mouse_x + offset_x
rectangle.y = mouse_y + offset_y
screen.fill(WHITE)
pygame.draw.rect(screen, RED, rectangle)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
I guess this is the most important part:
pygame.draw.rect(screen, RED, rectangle)
Every time I try drawing 5 of them I can't drag any of them. Does anyone have a solution for this?
You can create a list of rectangles and a selected_rect variable which points to the currently selected rect. In the event loop check if one of the rects collides with the event.pos, then set the selected_rect to the rect under the mouse cursor and move it.
I'm using a pygame.math.Vector2 for the offset to save a few lines in the example.
import sys
import pygame as pg
from pygame.math import Vector2
pg.init()
WHITE = (255, 255, 255)
RED = (255, 0, 0)
screen = pg.display.set_mode((1024, 768))
selected_rect = None # Currently selected rectangle.
rectangles = []
for y in range(5):
rectangles.append(pg.Rect(20, 30*y, 17, 17))
# As a list comprehension.
# rectangles = [pg.Rect(20, 30*y, 17, 17) for y in range(5)]
clock = pg.time.Clock()
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
elif event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
for rectangle in rectangles:
if rectangle.collidepoint(event.pos):
offset = Vector2(rectangle.topleft) - event.pos
selected_rect = rectangle
elif event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
selected_rect = None
elif event.type == pg.MOUSEMOTION:
if selected_rect:
selected_rect.topleft = event.pos + offset
screen.fill(WHITE)
for rectangle in rectangles:
pg.draw.rect(screen, RED, rectangle)
pg.display.flip()
clock.tick(30)
pg.quit()
sys.exit()
I'm trying to do a drag-and-drop mechanic in pygame and I'm being partly successful (thanks to answers to questions like this one and tutorials like this other one). The mechanic I'm using goes as follows: I update in every loop the position of the image once the event of pressing the button is detected (and only if the mouse is over the image). To do so, I created a rectangle object by just calling image.get_rect(), but it seems that this rectangle is shifted, with the center of the image laying in the bottom right of the rectangle. I annex both the code an the result:
import pygame, sys
from pygame.locals import *
FPS = 60
fpsClock = pygame.time.Clock()
def main():
pygame.init()
DS = pygame.display.set_mode((400, 400), 0, 32)
pygame.display.set_caption('Drag-n-drop that cat')
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
catImg = pygame.image.load('cat.png') # I load the image
catImgRectObj = catImg.get_rect() # I create the rect object
catx = 200
caty = 200
catImgRectObj.center = [catx, caty]
IsMousePressed = False
while True:
lastPos = catImgRectObj.center
DS.fill(WHITE)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
IsMousePressed = True
elif event.type == MOUSEBUTTONUP:
IsMousePressed = False
if IsMousePressed and isMouseOverObj(catImgRectObj):
catImgRectObj.center = pygame.mouse.get_pos() #I update the center
else:
catImgRectObj.center = lastPos
pygame.draw.rect(DS, BLACK, catImgRectObj) #draw the rect object
DS.blit(catImg, catImgRectObj.center) #draw the cat.
pygame.display.update()
fpsClock.tick(FPS)
def isMouseOverObj(Obj):
return Obj.collidepoint(pygame.mouse.get_pos())
if __name__ == '__main__':
main()
Use
DS.blit(catImg, catImgRectObj)
instead of
DS.blit(catImg, catImgRectObj.center)
to draw the cat.
The catImgRectObj rect already describes where the cat image is, and if you use catImgRectObj.center to blit it on the screen, but shift its top left corner to the center of the desired area.
Also, I would use something like this:
import pygame, sys
from pygame.locals import *
FPS = 60
fpsClock = pygame.time.Clock()
def main():
pygame.init()
DS = pygame.display.set_mode((400, 400), 0, 32)
pygame.display.set_caption('Drag-n-drop that cat')
catImg = pygame.image.load('cat.png').convert_alpha()
catMask = pygame.mask.from_surface(catImg)
catImgRectObj = catImg.get_rect(center=(200, 200))
IsMousePressed = False
while True:
DS.fill(pygame.color.THECOLORS['white'])
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and isMouseOverObj(catMask, catImgRectObj):
IsMousePressed = True
elif event.type == MOUSEBUTTONUP:
IsMousePressed = False
elif event.type == MOUSEMOTION and IsMousePressed:
catImgRectObj.move_ip(event.rel)
DS.blit(catImg, catImgRectObj)
pygame.display.update()
fpsClock.tick(FPS)
def isMouseOverObj(mask, rect):
mouse_pos = pygame.mouse.get_pos()
rel_pos = (mouse_pos[0] - rect.left, mouse_pos[1] - rect.top)
return rect.collidepoint(mouse_pos) and mask.get_at(rel_pos)
if __name__ == '__main__':
main()
to make the collision detection pixel perfect, simplify the code a bit, and to prevent the jumping once you click on the cat.
How can I detect if an image has been touched in pygame on a touch screen? I have searched but I can not find how to detect if a particular image is touched.
To detect if an image has been touched, you can use the MOUSEBUTTONDOWN event. See Pygame mouse clicking detection.
Load the image with pygame.image.load(). Crate the bounding rectangle (pygame.Rect). Create a pygame.Mask from the image with pygame.mask.from_surface:
image = pygame.image.load('image.png').convert_alpha()
image _rect = image.get_rect(topleft = (x, y))
image_mask = pygame.mask.from_surface(image)
Use the MOUSEBUTTONDOWN event to detect if the mouse is clicked in the rectangular area of the image. Check if the corresponding bit in the image mask is set:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
image_x, image_y = event.pos[0] - image_rect.x, event.pos[1] - image_rect.y
if image_rect.collidepoint(event.pos) and image_mask.get_at((image_x, image_y)):
print("touched")
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
font = pygame.font.SysFont(None, 100)
clock = pygame.time.Clock()
text = font.render("Text", True, (255, 255, 0))
bomb = pygame.image.load('Bomb-256.png').convert_alpha()
bomb_rect = bomb.get_rect(center = window.get_rect().center)
bomb_mask = pygame.mask.from_surface(bomb)
click_count = 0
click_text = font.render('touch: ' + str(click_count), True, "black")
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
image_x, image_y = event.pos[0] - bomb_rect.x, event.pos[1] - bomb_rect.y
if bomb_rect.collidepoint(event.pos) and bomb_mask.get_at((image_x, image_y)):
click_count += 1
click_text = font.render('touch: ' + str(click_count), True, "black")
window.fill((255, 255, 255))
window.blit(bomb, bomb_rect)
window.blit(click_text, click_text.get_rect(topleft = (20, 20)))
pygame.display.flip()
pygame.quit()
exit()
If I run this from the raspberry pi desktop the touch is not detected.
But if I execute this from ssh remote it works fine
How can I detect if an image has been touched in pygame on a touch screen? I have searched but I can not find how to detect if a particular image is touched.
To detect if an image has been touched, you can use the MOUSEBUTTONDOWN event. See Pygame mouse clicking detection.
Load the image with pygame.image.load(). Crate the bounding rectangle (pygame.Rect). Create a pygame.Mask from the image with pygame.mask.from_surface:
image = pygame.image.load('image.png').convert_alpha()
image _rect = image.get_rect(topleft = (x, y))
image_mask = pygame.mask.from_surface(image)
Use the MOUSEBUTTONDOWN event to detect if the mouse is clicked in the rectangular area of the image. Check if the corresponding bit in the image mask is set:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
image_x, image_y = event.pos[0] - image_rect.x, event.pos[1] - image_rect.y
if image_rect.collidepoint(event.pos) and image_mask.get_at((image_x, image_y)):
print("touched")
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
font = pygame.font.SysFont(None, 100)
clock = pygame.time.Clock()
text = font.render("Text", True, (255, 255, 0))
bomb = pygame.image.load('Bomb-256.png').convert_alpha()
bomb_rect = bomb.get_rect(center = window.get_rect().center)
bomb_mask = pygame.mask.from_surface(bomb)
click_count = 0
click_text = font.render('touch: ' + str(click_count), True, "black")
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
image_x, image_y = event.pos[0] - bomb_rect.x, event.pos[1] - bomb_rect.y
if bomb_rect.collidepoint(event.pos) and bomb_mask.get_at((image_x, image_y)):
click_count += 1
click_text = font.render('touch: ' + str(click_count), True, "black")
window.fill((255, 255, 255))
window.blit(bomb, bomb_rect)
window.blit(click_text, click_text.get_rect(topleft = (20, 20)))
pygame.display.flip()
pygame.quit()
exit()
If I run this from the raspberry pi desktop the touch is not detected.
But if I execute this from ssh remote it works fine