Home > OS >  How to blit objects from a list on screen after sorting by attribute?
How to blit objects from a list on screen after sorting by attribute?

Time:10-03

I tried to make a simple line sorting algorithm by height in Python using pygame.

The idea was simple. Generate 100 instances of objects with height attributes of a random value from a class into a list and then draw them onto the display. After clicking the right keyboard arrow, use the sort function to sort objects in the list by height attribute and redraw them again.

For some reason, it always keeps drawing the unsorted/list version of the list. So either it doesn't sort the objects by attribute well or I made a mistake somewhere else.

Can anyone help me find the problem?

import pygame
import random
import operator
from colors import color_dict

# Initialize pygame essentials
pygame.init()

# Load the background image
image = pygame.image.load("bg.jpg")
image_rect = image.get_rect()

# Screen settings
DISPLAY_WIDTH = 1000
DISPLAY_HEIGHT = 400

# Colors
WHITE = (255, 255, 255)
SCREEN_BG_COLOR = (30, 30, 30)

# Time / FPS
FPS = 1000

# Set the main preview display/window with width and height
display = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))

# Draw the display shape sized and positioned as display
# so we can use its coordinates for positioning other elements
display_rect = display.get_rect()

pygame.display.set_caption("Sorting algorithm")
clock = pygame.time.Clock()

# Width of the line (change for the total number of lines)
line_width = 10

# Total number of lines in the screen
total_lines = int(display.get_width() / line_width)

# Store all drawn lines in the list
drawn_lines = []

line_group = pygame.sprite.Group()


class Line(pygame.sprite.Sprite):
    """A class to manage a single line parameters"""

    def __init__(self, color):
        super(Line, self).__init__()
        self.width = line_width  # 10
        self.height = random.randint(-400, 0)
        self.color = color
        self.surface = pygame.Surface((self.width, abs(self.height)))
        self.surface.fill(self.color)
        self.rect = self.surface.get_rect()
        self.rect.x = display_rect.bottomleft[0]
        self.rect.bottom = display_rect.bottom


def generate_lines():
    """Generate lines across the screen"""
    
    # Set the random color for every line generation cycle
    new_color = random.choice(list(color_dict))
    new_color = str(new_color)

    # Create the total_lines amount of objects and fill the line list with them
    line_x_position = 0

    for _ in range(total_lines):
        line = Line(new_color)
        line.rect.x  = line_x_position
        drawn_lines.append(line)
        line_group.add(line)
        line_x_position  = line_width


def draw_lines():
    """Draw the generated lines from the line list"""
    for line in drawn_lines:
        display.blit(line.surface, line.rect)


def redraw_lines():
    """Empty the list filled with line objects.
    Generate new lines and redraw them on the screen again."""
    drawn_lines.clear()
    generate_lines()
    draw_lines()


def sort():
    """Sort the objects inside the line list by height"""
    drawn_lines.sort(key=operator.attrgetter('height'))


def draw_sorted():
    for line in drawn_lines:
        display.blit(line.surface, line.rect)


def main():
    """The main loop"""

    is_generated = False
    is_sorted = False

    while True:
        # Handle the user input events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    redraw_lines()
                elif event.key == pygame.K_RIGHT:
                    sort()
                    is_sorted = True

        # Generate lines in list
        if not is_generated:
            generate_lines()
            is_generated = True

        # Draws the background image
        display.fill(SCREEN_BG_COLOR)

        if not is_sorted:
            draw_lines()
        else:
            draw_sorted()

        # Refresh/update the images on the display
        pygame.display.update()
        clock.tick(FPS)


main()
pygame.quit()

CodePudding user response:

Each Line is drawn with the size and position stored in the the line.rect attribute. Therefore, it is not enough to sort the list. You'll also need to update the x-coordinate of the line rectangle. You have 2 possibilities.

Option 1: Update the line.rect.x coordinate when sorting the list:

def sort():
    """Sort the objects inside the line list by height"""
    drawn_lines.sort(key=operator.attrgetter('height'))
    line_x_position = 0
    for line in drawn_lines:
        line.rect.x = line_x_position
        line_x_position  = line_width

Option 2: Calculate the x-coordinate depending on the position in the list when drawing the line:

def draw_sorted():
    line_x_position = 0
    for line in drawn_lines:
        sorted_line_rect = line.rect.copy()
        sorted_line_rect.x = line_x_position
        line_x_position  = line_width
        display.blit(line.surface, sorted_line_rect)
  • Related