我的 Kivy 代码未检测到复选框选择

问题描述 投票:0回答:1

我最近一直在尝试使用 Kivy 来实现我想做的个人项目:一个可以让你创建带有一些细节的 DnD 角色的应用程序。该角色将存储在 JSON 文件中并加载到主屏幕上。问题是,当使用复选框进行性别选择时,它根本不起作用,我找不到问题。

这是main.py文件:

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()

还有我的 Kivy 文件:

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

我希望该应用程序允许您选中和取消选中复选框,但将选择限制为只有一个框(仅限一种性别)。

代码本身也可能非常混乱,但目前它运行良好 XD

checkbox kivy selection
1个回答
0
投票

您可以通过使用

group
CheckBox
属性来获得您想要的行为。请参阅文档

试试这个:

        GridLayout:
            cols: 2
            spacing: 40
            padding: 20
            size_hint_y: None
            height: self.minimum_height
            Label:
                text: "Man"
            CheckBox:
                id: man_checkbox
                group: "gender"

            Label:
                text: "Woman"
            CheckBox:
                id: woman_checkbox
                group: "gender"
© www.soinside.com 2019 - 2024. All rights reserved.