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...