[Python] How to activate one button at a time per data row?

Discussão em 'Python' iniciado por Stack, Outubro 24, 2024.

  1. Stack

    Stack Membro Participativo

    Using Pygame, this game uses Pandas to extract data from an Excel sheet to uniquely show possible data points that the user can pick from.

    df = pd.read_excel('dataFiles\Mythology Data.xlsx')
    headsList = ["Play","Names", "Biological entities", "Powers", "Locations", "Careers", "Objects"]

    """This is to display each data point as a circle button per column."""
    buttons_list = []
    background_circles = []
    group_backgrounds = []
    backgroundColor = (*random_color(), 100)
    def addButtons():
    global backgroundColor, headsList
    background_data = []
    for amount, head in enumerate(headsList):
    newAmount = min(amount * 8, len(df[head].dropna()))
    dataList = random.sample(list(df[head].dropna()), newAmount)
    num_buttons = len(dataList)
    radius = 110 * (amount + 1)
    h = screenWidth // 2
    k = screenHeight // 2
    # Add buttons and associate with background/text
    for i, name in enumerate(dataList):
    angle = (2 * math.pi / num_buttons) * i
    x = h + int(radius * math.cos(angle))
    y = k + int(radius * math.sin(angle))
    button = button_file.CircleButton(x, y, (radius, angle), 50, str(name), random_color(), (169, 169, 169))
    button.head = head
    buttons_list.append(button)
    background_data = [h, k, radius, backgroundColor]
    background_circles.append(pygame.draw.circle(screen, background_data[3], (background_data[0], background_data[1]), background_data[2]))
    # Store background info and corresponding text
    group_backgrounds.append((h, k, radius + 55, background_data[3])) # Store background circle info
    group_texts.append(headsList[amount])
    addButtons()



    This script identifies each data head/category in two ways. 'Background color identification' and the 'distance shown between the circle buttons' (shown earlier).

    """Demonstrating each data category using colored backgrounds + text"""
    group_texts = []
    def draw_background_with_text():
    # Iterate over each group background and text
    for i, data in enumerate(group_backgrounds):
    # Draw the full circle background
    circle_surface = pygame.Surface((screenWidth, screenHeight), pygame.SRCALPHA)
    pygame.draw.circle(circle_surface, data[3], (data[0], data[1]), data[2])
    screen.blit(circle_surface, (0, 0))
    # Display corresponding text along the circle's edge
    font = pygame.font.SysFont(None, 36)
    text = group_texts
    angle_step = 2 * math.pi / len(text)
    for j, char in enumerate(text):
    angle = j * angle_step
    x = data[0] + (data[2] - 40) * math.cos(angle - math.pi/2)
    y = data[1] + (data[2] - 40) * math.sin(angle - math.pi/2)
    char_surface = font.render(char, True, (0, 0, 0))
    char_rect = char_surface.get_rect(center=(x, y))
    rotated_char = pygame.transform.rotate(char_surface, math.degrees(angle))
    screen.blit(rotated_char, rotated_char.get_rect(center=char_rect.center))


    The issue, currently, is allowing the user to pick a new data point and deactivating the rest of the circles that are categorized under the same data head. → By default, to activate "create_clicked" in the buttons, the user must left-click, this process is then indicated by changing the circle color to red.

    def buttonDeactive_update():
    # Create a dictionary to store the last activated button for each head
    active_buttons = {}

    # First pass: Find the most recently activated button for each head
    for btn in buttons_list:
    if btn.create_clicked:
    active_buttons[btn.head] = btn

    # Second pass: Deactivate all buttons except the most recent one in each head
    for btn in buttons_list:
    if btn.head in active_buttons:
    # If this button is not the most recently activated one in its head group
    if btn != active_buttons[btn.head]:
    btn.create_clicked = False


    This part of the script is to show the current state of the circle button script.

    # circle button
    class CircleButton:
    def __init__(self, x, y, master_position, radius, text, inactive_color, active_color):
    self.x = x
    self.y = y
    self.master_position = master_position
    self.angle = 0
    self.radius = radius
    self.text = text
    self.active_color = active_color
    self.inactive_color = inactive_color
    self.color = self.inactive_color
    self.textColor = (255, 255, 255)
    self.font_size = min(self.radius // len(self.text) + 10, self.radius // 2)
    self.font = pygame.font.Font(None, self.font_size)
    self.head = None
    self.define_clicked = False
    self.create_clicked = False
    self.create_mode = (255, 0, 0)

    def draw(self, screen):
    self.x, self.y = self.x, self.y
    pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius)
    text_surface = self.font.render(self.text, True, self.textColor)
    text_rect = text_surface.get_rect(center=(self.x, self.y))
    screen.blit(text_surface, text_rect)

    def handle_event(self, event, panel_rect):
    mouse_pos = pygame.mouse.get_pos()
    distance = math.sqrt((mouse_pos[0] - self.x) ** 2 + (mouse_pos[1] - self.y) ** 2)
    if distance < self.radius and self.x - self.radius > panel_rect:
    self.color = self.active_color
    self.textColor = (0, 0, 0)
    if event.type == pygame.MOUSEBUTTONDOWN:
    #print(event.button)
    if event.button == 1: # right click
    self.define_clicked = not(self.define_clicked)
    if event.button == 3: # left click
    self.create_clicked = not(self.create_clicked)
    else:
    self.define_clicked = False
    self.color = self.inactive_color
    self.textColor = (255, 255, 255)

    if self.create_clicked:
    self.color = self.create_mode
    self.textColor = (255, 255, 255)



    The first attempt failed in an unexpected and interesting way. I don't understand how it failed, but this was observed. This script wouldn't allow the user to pick a button that was located counterclockwise from the currently activated button. As well as not allowing the user to pick a new button when the user reached (assuming) the first item in the list. [Unless they deactivated the current activated button].

    def buttonDeactive_update():
    head_activated = {head: False for head in headsList} # Keeps track of which head has an active button

    for btn in buttons_list:
    if btn.create_clicked: # If the current button is clicked
    # If no other button from this head is active, mark this button as active
    if not head_activated[btn.head]:
    head_activated[btn.head] = True
    else:
    # If another button from the same head is already active, deactivate this button
    btn.create_clicked = False


    Although similar to the other one, I mainly focused on the user clicking any element they would like without needing to move in a clockwise direction. This failed when the user clicked on the (assuming) first item in the list.

    def buttonDeactive_update():
    # Dictionary to track the latest clicked button per head
    latest_clicked_button = {head: None for head in headsList}

    # Iterate through all buttons to find the latest clicked button for each head
    for btn in buttons_list:
    if btn.create_clicked:
    latest_clicked_button[btn.head] = btn # Track the most recently clicked button for each head

    # Now deactivate all buttons except the latest clicked one per head
    for btn in buttons_list:
    if btn != latest_clicked_button[btn.head]:
    btn.create_clicked = False

    Continue reading...

Compartilhe esta Página