How does pygame.Surface.set_at() work in a for loop?

Viewed 104

I'm writing code for pixel art and I am saving a Surface as a file. However the .set_at() method doesn't appear to be working.

class SelectRect:

    def __init__(self, xpos: int, ypos: int, w: int, h: int, rect_colour: tuple or list):
        self.rect = pygame.Rect(xpos, ypos, w, h)
        self.colour = rect_colour
        self.x = xpos
        self.y = ypos

    def render(self, surf):
        pygame.draw.rect(surf, self.colour, self.rect)


selected_rects = []
colour_rects = []

for i in range(15):
    colour_rects.append(SelectRect(512, 16 * i, TILE_SIZE, TILE_SIZE, colours[i]))


colour = WHITE

l_mouse_down = False
r_mouse_down = False

export_surf = pygame.Surface((32, 32))
export_surf.set_at((32, 32), CYAN)


rects = []
y = 0
for i in range(32):
    x = 0
    for o in range(32):
        rects.append(SelectRect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE, None))
        x += 1
    y += 1


while True:

    screen.fill((32, 32, 32))

    mx, my = pygame.mouse.get_pos()

    if l_mouse_down:
        for rect in rects:
            if rect.rect.collidepoint(mx, my):
                if rect not in selected_rects:
                    selected_rects.append(rect)
                rect.colour = colour

        for rect in colour_rects:
            if rect.rect.collidepoint(mx, my):
                colour = rect.colour

    if r_mouse_down:
        for rect in rects:
            if rect.rect.collidepoint(mx, my):
                if rect in selected_rects:
                    selected_rects.remove(rect)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                sys.exit()
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_e:    # todo, export in jpeg or png, sup2
                for rect in selected_rects:
                    export_surf.set_at((rect.x, rect.y), rect.colour)
            if event.key == pygame.K_c:
                selected_rects.clear()
            if event.key == pygame.K_f:
                for rect in rects:
                    selected_rects.append(rect)
                    rect.colour = colour
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                l_mouse_down = True
            if event.button == 3:
                r_mouse_down = True
        if event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                l_mouse_down = False
            if event.button == 3:
                r_mouse_down = False

    for rect in rects:
        if rect not in selected_rects:
            pygame.draw.rect(screen, (64, 64, 64), rect.rect)
        else:
            rect.render(screen)
    for rect in colour_rects:
        rect.render(screen)

    pygame.draw.rect(screen, colour, [512, 496, 16, 16])

    screen.blit(export_surf, (0, 0))

    pygame.display.update()
    clock.tick(framerate)

tl;dr:

I have a grid of rects (actually SelectRects), which store a colour. When I export, I loop through those rects and change the corresponding pixel on the export_surface to that colour. This does not seem to be working. I have tried to draw a circle with a radius of 1, but this also doesn't work.

1 Answers

If the size of the surface is 32 x 32, then the coordinate (32, 32) is out of bounds. The coordinate of the top left pixel is (0, 0) and the coordinate of the bottom right pixels is (31, 31):

export_surf.set_at((32, 32), CYAN)

export_surf = pygame.Surface((32, 32))
export_surf.set_at((31, 31), CYAN)

The coordinate (rect.x, rect.y) is also out of bounds, because rect specifies a position on the screen.

You have to divide (rect.x, rect.y) by the size of a tile:

export_surf.set_at((rect.x // TILE_SIZE, rect.y // TILE_SIZE), rect.colour)

export_surf.set_at((rect.x, rect.y), rect.colour)
Related