I have recently been trying Kivy for a personal project I wanted to do: an app that lets you create a DnD character with a few details. That character would store on a JSON file and load up on the main screen. The problem is, when using checkboxes for the gender selection, it doesn't work at all and I can't find the problem. Here is the main.py file: from kivy.app import App from kivy.uix.screenmanager import ScreenManager, Screen from kivy.graphics import Color, Line from kivy.uix.button import Button from kivy.properties import ObjectProperty, ListProperty import random import json import os class MainScreen(Screen): def get_random_color(self): # Returns a random RGBA color return [random.random() for _ in range(3)] + [0.8] def add_character_button(self, character_data): # Create a button for each saved character with a random color and only the name button = Button( text=character_data["name"], size_hint_y=None, height=50, background_normal='', background_color=character_data['color'] ) button.custom_color = button.background_color.copy() button.bind(pos=self.update_border, size=self.update_border) # Add a border to the button with button.canvas.before: Color(0, 0, 0, 1) # Set black color for border self.border_line = Line(rectangle=(button.x, button.y, button.width, button.height), width=1) button.bind(on_release=lambda btn: self.show_character_details(character_data)) self.ids.character_list.add_widget(button) def update_border(self, button, _): # Update border position and size when button size or position changes self.border_line.rectangle = (button.x, button.y, button.width, button.height) def load_user_characters(self): # Load characters from a JSON file and add them to the screen if os.path.exists('characters.json'): with open('characters.json', 'r') as f: characters = json.load(f) for character in characters: self.add_character_button(character) def show_character_details(self, character_data): character_details_screen = self.manager.get_screen('character_details') character_details_screen.set_character_data(character_data) # Pass character data self.manager.current = 'character_details' class SecondScreen(Screen): character_name = ObjectProperty(None) character_age = ObjectProperty(None) character_power1 = ObjectProperty(None) character_power2 = ObjectProperty(None) def save_character(self): main_screen = self.manager.get_screen('main') name = self.ids.character_name.text age = self.ids.character_age.text gender = 'Man' if self.ids.man_checkbox.active else 'Woman' if self.ids.woman_checkbox.active else 'Other' power1 = self.ids.character_power1.text power2 = self.ids.character_power2.text color = main_screen.get_random_color() character_data = { 'name': name, 'age': age, 'gender': gender, 'power1': power1, 'power2': power2, 'color': color } # Save character to the list self.save_characters_to_file(character_data) # Add button to the main screen dynamically main_screen.add_character_button(character_data) # Clear the inputs after saving self.ids.character_name.text = '' self.ids.character_age.text = '' self.ids.character_power1.text = '' self.ids.character_power2.text = '' self.ids.man_checkbox.active = False self.ids.woman_checkbox.active = False # Switch back to the main screen self.manager.current = 'main' def save_characters_to_file(self, new_character): characters = [] # Load existing characters from file if it exists if os.path.exists('characters.json'): with open('characters.json', 'r') as f: characters = json.load(f) # Add the new character characters.append(new_character) # Save updated character list to file with open('characters.json', 'w') as f: json.dump(characters, f) selected_gender = None def set_gender(self, checkbox, value): if value == True: # If this checkbox is active # Uncheck the others if checkbox == self.ids.man_checkbox: self.ids.woman_checkbox.active = False elif checkbox == self.ids.woman_checkbox: self.ids.man_checkbox.active = False class CharacterDetailsScreen(Screen): character_color = ListProperty([1, 1, 1, 1]) #default character_data = None #default def on_enter(self): if self.character_data: # Check if character_data is set self.ids.character_name.text = self.character_data['name'] self.ids.character_age.text = self.character_data['age'] self.ids.character_gender.text = self.character_data['gender'] self.ids.character_power1.text = self.character_data['power1'] self.ids.character_power2.text = self.character_data['power2'] self.character_color = self.character_data['color'] def set_character_data(self, character): self.character_data = character # Store character data class RosterApp(App): def build(self): sm = ScreenManager() sm.add_widget(MainScreen(name='main')) sm.add_widget(SecondScreen(name='second')) sm.add_widget(CharacterDetailsScreen(name='character_details')) # Load characters into the main screen when the app starts sm.get_screen('main').load_user_characters() return sm if __name__ == '__main__': RosterApp().run() And my Kivy file: <MainScreen>: FloatLayout: orientation: 'vertical' # Light yellow background FloatLayout: canvas.before: Rectangle: pos: self.pos size: self.size source: 'img.png' # Round button in the bottom right corner Button: text: "" size_hint: None, None size: 50, 50 pos_hint: {'right': 0.95, 'bottom': 0.05} background_normal: 'plus.webp' background_color: 0.737, 0.635, 0.514, 1 # light almost-off golden border: (50, 50, 50, 50) # Simulates a round border canvas.before: Color: rgba: 0.086, 0.086, 0.086, 0.75 # Same dark gray color Ellipse: # Draw an ellipse to make the button round pos: self.pos size: self.size on_release: app.root.current = 'second' # dark gray top bar with app name FloatLayout: size_hint_y: None height: 50 pos_hint: {'top': 1} canvas.before: Color: rgba: 0.086, 0.086, 0.086, 0.75 # dark gray color Rectangle: pos: self.pos size: self.size Label: text: "DnD Roster Panel" color: 1, 1, 1, 1 # white text pos_hint: {'center_x': 0.5, 'center_y': 0.5} ScrollView: size_hint: (1, 0.8) pos_hint: {'top': 0.9} BoxLayout: id: character_list orientation: 'vertical' size_hint_y: None height: self.minimum_height # Adjusts height to fit content spacing: 10 padding: 10 <SecondScreen>: FloatLayout: canvas.before: Rectangle: pos: self.pos size: self.size source: 'img.png' FloatLayout: orientation: 'vertical' FloatLayout: size_hint_y: None height: 50 pos_hint: {'top': 1} canvas.before: Color: rgba: 0.086, 0.086, 0.086, 0.75 # dark gray color Rectangle: pos: self.pos size: self.size Label: text: "DnD Roster Panel" color: 1, 1, 1, 1 # white text pos_hint: {'center_x': 0.5, 'center_y': 0.5} ScrollView: size_hint: 1, 0.85 # Make it take up the remaining space pos_hint: {'top': 0.85} do_scroll_x: False # Disable horizontal scrolling BoxLayout: orientation: 'vertical' size_hint_y: None height: self.minimum_height padding: [20, 20, 20, 20] # Add some padding for spacing spacing: 20 # Add some space between elements Label: text: 'Name' color: 1, 1, 1, 1 halign: "center" TextInput: id: character_name hint_text: "" multiline: False size_hint_y: None height: 40 Label: text: 'Age' color: 1, 1, 1, 1 halign: "center" TextInput: id: character_age hint_text: "" multiline: False input_filter: 'int' size_hint_y: None height: 40 Label: text: 'Select Gender' color: 1, 1, 1, 1 halign: "center" GridLayout: cols: 2 spacing: 40 padding: 20 size_hint_y: None height: self.minimum_height Label: text: "Man" CheckBox: id: man_checkbox on_active: root.set_gender(self, self.active) # Uncheck female if male is checked Label: text: "Woman" CheckBox: id: woman_checkbox on_active: root.set_gender(self, self.active) # Uncheck male if female is checked Label: text: 'First Power' color: 1, 1, 1, 1 halign: "center" TextInput: id: character_power1 hint_text: "" multiline: False size_hint_y: None height: 40 Label: text: 'Second Power' color: 1, 1, 1, 1 halign: "center" TextInput: id: character_power2 hint_text: "" multiline: False size_hint_y: None height: 40 Button: text: "Save Character" size_hint_y: None height: 50 on_release: root.save_character() # Call save_character method <CharacterDetailsScreen>: FloatLayout: canvas.before: Rectangle: pos: self.pos size: self.size source: 'img.png' FloatLayout: orientation: 'vertical' FloatLayout: size_hint_y: None height: 50 pos_hint: {'top': 1} canvas.before: Color: rgba: self.parent.parent.character_color Rectangle: pos: self.pos size: self.size Label: text: 'DnD Roster Panel' color: 1, 1, 1, 1 # white text pos_hint: {'center_x': 0.5, 'center_y': 0.5} Label: id: character_name text: 'Name' size_hint_y: None pos_hint: {'center_x': 0.5, 'top': 0.85} height: 20 font_size: 30 GridLayout: cols: 2 spacing: 10 padding: 20 size_hint_y: None height: self.minimum_height # Adjusts height to fit content # Age Label: text: "Age: " size_hint_y: None height: 50 canvas.before: Color: rgba: 1, 1, 1, 1 # Black border color Line: rectangle: (self.x, self.y, self.width, self.height) Label: id: character_age text: "Age:" size_hint_y: None height: 50 canvas.before: Color: rgba: 1, 1, 1, 1 # Black border color Line: rectangle: (self.x, self.y, self.width, self.height) # Gender Label: text: "Gender: " size_hint_y: None height: 50 canvas.before: Color: rgba: 1, 1, 1, 1 # Black border color Line: rectangle: (self.x, self.y, self.width, self.height) Label: id: character_gender text: "Gender:" size_hint_y: None height: 50 canvas.before: Color: rgba: 1, 1, 1, 1 # Black border color Line: rectangle: (self.x, self.y, self.width, self.height) # First Power Label: text: "First Power:" size_hint_y: None height: 50 canvas.before: Color: rgba: 1, 1, 1, 1 # Black border color Line: rectangle: (self.x, self.y, self.width, self.height) Label: id: character_power1 text: "First Power:" size_hint_y: None height: 50 canvas.before: Color: rgba: 1, 1, 1, 1 # Black border color Line: rectangle: (self.x, self.y, self.width, self.height) # Second Power Label: text: "Second Power:" size_hint_y: None height: 50 canvas.before: Color: rgba: 1, 1, 1, 1 # Black border color Line: rectangle: (self.x, self.y, self.width, self.height) Label: id: character_power2 text: "Second Power:" size_hint_y: None height: 50 canvas.before: Color: rgba: 1, 1, 1, 1 # Black border color Line: rectangle: (self.x, self.y, self.width, self.height) Button: text: "" size_hint: None, None size: 50, 50 pos_hint: {'left': 0.95, 'bottom': 0.05} background_normal: 'back.png' background_color: 0.737, 0.635, 0.514, 1 # light almost-off golden border: (50, 50, 50, 50) # Simulates a round border canvas.before: Color: rgba: 0.086, 0.086, 0.086, 0.75 # Same dark gray color Ellipse: # Draw an ellipse to make the button round pos: self.pos size: self.size on_release: app.root.current = 'main' I would like the app to let you check and uncheck the checkboxes, but restricting the selection to only one box (only one gender). The code itself might also be very messy, but for now it was working fine XD Continue reading...