Skip to content
Home » Kivy Part 18 – Slugrace – Settings Screen GUI

Kivy Part 18 – Slugrace – Settings Screen GUI

Spread the love

In the previous part we started adding some code to the Python and kv files. In this part we’ll focus on two files: settings.py and settings.kv. Our goal here is to build the GUI for the Settings Screen, which will be the screen that you will see after the app is launched. Here you can choose the number of players, set the players’ names and initial money amounts, as well as decide on an ending condition.

But before we delve into the topic, here’s some info for you.

*****

Book Info

I just published my Kivy book, GUI Programming with Python and Kivy. It’s pretty long (over 800 pages) and comprehensive. And, which also counts, easy to read. The book contains lots of illustrations.

This book covers all the basics that you need to know to start programming GUI applications with Python and Kivy. Throughout the book we are building a GUI application from scratch, a fully functional game using all kinds of tools that Kivy has to offer. It’s our Slugrace project, but covered in a much more in-depth manner.

Each part of the book starts with a theoretical introduction of a topic or idea that we then implement in the project. I assume you have no prior knowledge of the Kivy library, but you should have at least some basic knowledge of the Python programming language, including the object-oriented programming paradigm as this is what we will be using a lot in this book.

Kivy book

The book covers all the basic elements of Kivy that you have to know, like widgets, layouts, Kivy ids and properties, graphics, screens, animation, sound. Finally we’ll deploy the app to Windows. It is pretty comprehensive and after you finish it, I’m sure you’ll be able to create your own awesome GUI apps of any kind, not just games.

I hope you will have at least as much fun reading the book as I had writing it.

As far as this Kivy series is concerned, the following parts will contain the most important elements covered in the book. However, some elements will be presented in a simplified way here on my blog or omitted completely.

____________

If you are interested, you can purchase the book in four versions. Here are the links:

1) ebook – pdf version on my website – in full color

Here you can see the description of the book, sample graphics from the book and the full table of contents.

2) ebook – Kindle version on Amazon – in full color

3) paperback version on Amazon – in black and white

4) paperback version on Amazon – in full color

*****

And Now Let’s Move On…

This is what the Settings Screen is going to look like in the final version of the app:

settings screen

We won’t achieve this stage in this part, but we’ll get started.

As you know, the Settings Screen is divided into several areas. We’re going to discuss and build each area one by one, but first let’s recap on what we have. Here are the settings.py and settings.kv files as they were where we left off . First, the Python file:

# File name: settings.py

import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

class SettingsApp(App):
    def build(self):
        return BoxLayout()

if __name__ == '__main__':
    SettingsApp().run()

And now the kv file:

# File name: settings.kv

<BoxLayout>:
    orientation: 'vertical'
    
    ### SETTINGS LABEL ###
    Label:
        text: 'Settings'
        font_size: 28

    ### THE PLAYERS ###
    Label:
        text: 'The Players'

    ### ENDING CONDITIONS ###
    Label:
        text: 'Ending Conditions'

    ### READY BUTTON ###
    Button:
        text: 'Ready'

And here’s what this code creates for us:

settings screen - stage 1

As you can see, there are four areas: The Settings Label, the Players area, the Ending Conditions area and the Ready button area.

The root widget of the Settings Screen

If you look closer at the kv file, you will see that there is one top level widget, in our case it’s the BoxLayout. The indentation suggests that everything that is below it belongs to it. And this is how we make GUIs in Kivy – there’s always a root widget.

But we don’t want a regular BoxLayout to be the root widget. Let’s create our own specific root widget that behaves like a BoxLayout but is unique for this screen. This can be easily done by means of inheritance. So, let’s create a SettingsScreen class in the Python file and use it as our root widget:

# File name: settings.py

import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

# the layout for the whole settings screen
class SettingsScreen(BoxLayout):
    pass

class SettingsApp(App):
    def build(self):
        # We must return the SettingsScreen now.
        return SettingsScreen()

if __name__ == '__main__':
    SettingsApp().run()

And now the kv file:

# File name: settings.kv

# We're going to use the SettingsScreen as the root of the settings screen.
# It inherits from BoxLayout.
<SettingsScreen>:
    orientation: 'vertical'
    
    ### SETTINGS LABEL ###
    Label:
        text: 'Settings'
        font_size: 28

    ### THE PLAYERS ###  
    Label:
        text: 'The Players'        

    ### ENDING CONDITIONS ###
    Label:
        text: 'Ending Conditions'

    ### READY BUTTON ###
    Button:
        text: 'Ready'

So, there’s hardly any difference, but now we use SettingsScreen, which is a subclass of BoxLayout, as our root widget. In a similar way we’ll be creating the root widgets in the other kv files, but not now.

Now, we’re going to add the widgets that we need to all the four parts of the screen. There’s nothing to change in the first part, though. The Settings label is just what it is, a label. So, let’s move on to the second area, the Players.

As the code will now be getting longer and longer, I will work on it piece by piece, so instead of showing you the whole kv file each time, I will focus just on the part we are currently at. The missing parts will be replaced by dots or omitted completely.

Your Panda3D Magazine

Make Awesome Games and Other 3D Apps

with Panda3D and Blender using Python.

Cool stuff, easy to follow articles.

Get the magazine here (PDF).

The Players area

So, what do we actually want in the Players area? Well, this area will consist of three subareas:

1) Title – just a lable telling us that we are inside the Players area

2) Radiobuttons – they will let us choose the number of players

3) Player name and initial money setup – here we will be able to give names to the players and set their initial money.

These three subareas will be placed one below another, so a vertically oriented BoxLayout seems a good choice as the container. Using labels as placeholders, we could represent the structure of the Players area like so:

# File name: settings.kv

<SettingsScreen>:
    ...

    ### THE PLAYERS ###  
    BoxLayout:
        orientation: 'vertical'
        
        # Title 
        Label:
            text: 'The Players'

        # Radiobuttons 
        Label:
            text: 'Radiobuttons'

        # Player name and initial money setup 
        Label:
            text: 'Player name and initial money setup'
    
    ### ENDING CONDITIONS ###
    ...

If you run the settings.py file now, you will see the general structure so far:

settings screen - stage 2

Again, the title is just a label, but let’s have a look at the radio buttons now.

The Radio Buttons

In Kivy radio buttons are implemented as check boxes. The difference is that with regular check boxes you can check any number of them simultaneously or uncheck all of them. Radio buttons work in a different way. At any given time only one radio button in a group may be checked and there always must be one checked. Besides, there’s a graphical difference: Check boxes are usually squares and radio buttons are circles.

You already saw how to create a check box when we were discussing some of the basic widgets like labels, buttons and some others. If you want to turn a check box into a radio button, all you have to do is set the group property to a name. All check boxes with the same name of the group will be turned into radio buttons.

Now, there will be four radio buttons because we need four possible options: 1 player, 2 players, 3 players or 4 players. We’ll put them in a horizontal BoxLayout. But we also need a label for each radio button, so instead of using just the check boxes, we’ll put each check box along with its accompanying label in a horizontal BoxLayout. To turn the check boxes into radio buttons, we’ll set their group property to ‘players’.

So, as things are slowly becoming more and more complex, let’s jump into the kv code. Here’s the file:

# File name: settings.kv

<SettingsScreen>:
    ...

    ### THE PLAYERS ###  
    BoxLayout:
        orientation: 'vertical'
        
        # Title 
        Label:
            text: 'The Players'

        # Radiobuttons 
        # the BoxLayout for all four radio buttons
        BoxLayout:
            
            # the BoxLayout for the radio button + label for 1 player
            BoxLayout:
                CheckBox:
                    group: 'players'
                Label:
                    text: '1 player'

            # the BoxLayout for the radio button + label for 2 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                Label:
                    text: '2 players'

            # the BoxLayout for the radio button + label for 3 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                Label:
                    text: '3 players'

            # the BoxLayout for the radio button + label for 4 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                Label:
                    text: '4 players'

        # Player name and initial money setup 
        Label:
            text: 'Player name and initial money setup'
        

    ### ENDING CONDITIONS ###
    ...

If you run this program now, you will see the four radio buttons and none of them will be checked. But if you check one of them, you won’t be able to uncheck all anymore. If you check another one, the previous one will be unchecked, so that only one will be checked at any given time. Try it out. It should be quite intuitive because radio buttons are used a lot in desktop and web applications. Here’s what you should see:

settings screen - stage 3

We’re not done with the radio buttons yet, but let’s leave them now as is and move on to the next subarea.

Player name and initial money setup

This subarea is pretty complex. There’s a row for each player that contains :

– a label with a generic player name

– a text input for the new player name

– a horizontal BoxLayout that contains:

    — a label with the dollar symbol

    — a text input for the player’s initial money amount

Besides, there’s going to be a header row at the top so that you know what the other widgets are all about. The structure above resembles the structure in code, so let’s write the code now:

# File name: settings.kv

<SettingsScreen>:
    ...

    ### THE PLAYERS ###  
    BoxLayout:
        orientation: 'vertical'
        
        # Title 
        ...

        # Radiobuttons 
        ...

        # Player name and initial money setup 
        # Everything will be in a vertical BoxLayout.
        BoxLayout:
            orientation: 'vertical'

            # the headers row
            BoxLayout:

                # empty label just to add some space
                Label:
                    text: "" 

                # name header
                Label:
                    text: "Name"

                # money header
                Label:
                    text: "Initial Money"

            # the players rows
            # player 1
            BoxLayout:

                # label with generic player name
                Label:
                    text: 'Player 1'

                # text input for new player name
                TextInput:
                    # no multiline text allowed
                    multiline: False

                # BoxLayout for the money part
                BoxLayout:
                    # empty label to add some space
                    Label:
                        text: ""
                                           
                    # label with dollar sign
                    Label:
                        text: "$"

                    # text input for initial money 
                    TextInput:
                        multiline: False

            # The code for the other players is without comments to make it more concise.
            # player 2
            BoxLayout:
                Label:
                    text: 'Player 2'
                TextInput:
                    multiline: False
                BoxLayout:
                    Label:
                        text: ""
                    Label:
                        text: "$"
                    TextInput:
                        multiline: False

            # player 3
            BoxLayout:
                Label:
                    text: 'Player 3'
                TextInput:
                    multiline: False
                BoxLayout:
                    Label:
                        text: ""
                    Label:
                        text: "$"
                    TextInput:
                        multiline: False

            # player 4
            BoxLayout:
                Label:
                    text: 'Player 4'
                TextInput:
                    multiline: False
                BoxLayout:
                    Label:
                        text: ""
                    Label:
                        text: "$"
                    TextInput:
                        multiline: False             

    ### ENDING CONDITIONS ###
    ...

As you can see, this code is very repetitive. We’re going to see about that when we discuss properties, let it be so for now. And now, if you run your app, you will get something like this:

settings screen - stage 4

Well, this doesn’t look very good, because we didn’t take care of positioning and sizing yet, but all the elements that we needed are present. We’ll position and scale all the widgets when we are done creating them, so now let’s move on the the next area.

The Ending Conditions area

You know that Slugrace is a racing game. But when does it end? Well, this will be left for you to decide. You will be given three options to choose from:

1) The game is over when there is only one player with any money left.

2) The game is over not later than after a given number of races.

3) The game is over not later than the total racing time has elapsed.

So, in the Settings screen you should be able to choose one of these options. The first option will be the default one.

If you should be allowed to choose only one option out of many, radio buttons seem to be a reasonable solution. So, let’s use them. Additionally, for the second and third option there should be a text input where you can enter the number of races or the time respectively. The radio buttons will be placed in a GridLayout and the GridLayout along with a title label will be placed in a vertical BoxLayout. Here’s the kv code:

# File name: settings.kv

<SettingsScreen>:
    ...

    ### THE PLAYERS ###  
    ...

    ### ENDING CONDITIONS ###
    # Everything will be in a vertical BoxLayout.
    BoxLayout:
        orientation: 'vertical'

        # title label
        Label:
            text: "Ending Conditions"

        # radio buttons
        # The radio buttons will be placed in a GridLayout.
        GridLayout:
            rows: 3

            # option 1: money
            CheckBox:
                # All check boxes in this area will be assigned to a group,
                # which will turn them into radio buttons.
                group: 'conditions'
            # Each radio button will be accompanied by a label.
            Label:
                text: "The game is over when there is only one player with any money left."

            # option 2: races
            CheckBox:
                group: 'conditions'
            # The label and the text input will be placed in a horizontal BoxLayout.
            BoxLayout:                
                Label:
                    text: "The game is over not later than after a given number of races."
                TextInput:
                    multiline: False

            # option 3: time
            CheckBox:
                group: 'conditions'
            BoxLayout:                
                Label:
                    text: "The game is over not later than the total racing time has elapsed."
                TextInput:
                    multiline: False

    ### READY BUTTON ###
    ...    

If you now run the program, you will see this:

settings screen - stage 5

Ugly? Well, definitely not very pretty, but it’s slowly taking shape. The radio buttons are not very well visible, but they will when we take care of styles and colors. Besides, the text inputs hide parts of the labels, which also must be taken care of later.

And now there is only one more area left, the Ready Button area. This area is really very simple. The only widget it contains is a button. The code we need for this area is already there, so there’s nothing to change for now.

Finally, with all the widgets we need in place, let’s size and position them inside their layouts so that it all looks better. 

Python Jumpstart Course

Learn the basics of Python, including OOP.

with lots of exercises, easy to follow

The course is available on Udemy.

Sizing and Positioning the Widgets in the Settings Screen

If you don’t specify the size of a widget in an area or the size of the area itself, each area (so each widget or layout) will occupy the same amount of space:

sizing and positioning widgets

But we don’t need so much space for the Settings Label or the Ready Button. On the other hand, we need more space for the two areas in the middle. To sort this out, let’s size the widgets that we want to be of a specific size. You may want to recap on the sizing techniques that I covered in one of the previous parts. Here’s the whole kv file with all size and position settings explained in comments:

# File name: settings.kv

<SettingsScreen>:
    orientation: 'vertical'
    
    ### SETTINGS LABEL ###
    Label:
        text: 'Settings'
        font_size: 28

        # We want the label to occupy the whole width of its container, so the first
        # element in the size_hint tuple must be set to 1. But we want the height to 
        # be a fixed number of pixels, so the second element in hint_size must be set
        # to None, because we're inside a layout. The height of 30 px will be just fine.
        size_hint: (1, None)
        height: 30

    ### THE PLAYERS ###  
    BoxLayout:
        orientation: 'vertical'
        
        # Title         
        Label:
            text: 'The Players'

            # Let's make the text slightly bigger.
            font_size: 20

            # This label should also occupy the whole width, but its height should be 30 px.
            size_hint: (1, None)
            height: 30

        # Radiobuttons 
        BoxLayout:

            # The whole Radiobuttons subarea should occupy 40% of the width 
            # and should be 50 px high.
            size_hint: (.4, None)
            height: 50
            
            # 1 player
            BoxLayout:
                CheckBox:
                    group: 'players'

                    # The check box should occupy half the width of the containing
                    # BoxLayout and its whole height.
                    size_hint: (.5, 1) 
                Label:
                    text: '1 player'

            # 2 players
            BoxLayout:
                CheckBox:
                    group: 'players'

                    # The check box should occupy half the width of the containing
                    # BoxLayout and its whole height.
                    size_hint: (.5, 1) 
                Label:
                    text: '2 players'

            # 3 players
            BoxLayout:
                CheckBox:
                    group: 'players'

                    # The check box should occupy half the width of the containing
                    # BoxLayout and its whole height.
                    size_hint: (.5, 1) 
                Label:
                    text: '3 players'

            # 4 players
            BoxLayout:
                CheckBox:
                    group: 'players'

                    # The check box should occupy half the width of the containing
                    # BoxLayout and its whole height.
                    size_hint: (.5, 1) 
                Label:
                    text: '4 players'

        # Player name and initial money setup 
        BoxLayout:
            orientation: 'vertical'

            # the headers row
            BoxLayout:
                Label:
                    text: "" 

                    # The width of the label should be 80 px.
                    size_hint_x: None                
                    width: 80

                # name header
                Label:
                    text: "Name"

                    # The width of the label should be 700 px.
                    size_hint_x: None                
                    width: 700

                # money header
                Label:
                    text: "Initial Money"

            # the players rows
            # player 1
            BoxLayout:
                Label:
                    text: 'Player 1'

                    # This label should be 80x30 px.
                    size_hint: None, None
                    width: 80
                    height: 30
                
                TextInput:
                    multiline: False

                    # This text input should be 400x30 px.
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""

                        # This label should be 280 px wide.
                        size_hint_x: None                
                        width: 280
                                      
                    Label:
                        text: "$"

                        # This label should be 20x30 px.
                        size_hint: None, None
                        width: 20
                        height: 30

                    TextInput:
                        multiline: False

                        # This text input should be 250x30 px.
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 2
            BoxLayout:
                Label:
                    text: 'Player 2'

                    # This label should be 80x30 px.
                    size_hint: None, None
                    width: 80
                    height: 30

                TextInput:
                    multiline: False

                    # This text input should be 400x30 px.
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""

                        # This label should be 280 px wide.
                        size_hint_x: None                
                        width: 280

                    Label:
                        text: "$"

                        # This label should be 20x30 px.
                        size_hint: None, None
                        width: 20
                        height: 30

                    TextInput:
                        multiline: False

                        # This text input should be 250x30 px.
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 3
            BoxLayout:
                Label:
                    text: 'Player 3'

                    # This label should be 80x30 px.
                    size_hint: None, None
                    width: 80
                    height: 30

                TextInput:
                    multiline: False

                    # This text input should be 400x30 px.
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""

                        # This label should be 280 px wide.
                        size_hint_x: None                
                        width: 280

                    Label:
                        text: "$"

                        # This label should be 20x30 px.
                        size_hint: None, None
                        width: 20
                        height: 30

                    TextInput:
                        multiline: False

                        # This text input should be 250x30 px.
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 4
            BoxLayout:
                Label:
                    text: 'Player 4'

                    # This label should be 80x30 px.
                    size_hint: None, None
                    width: 80
                    height: 30

                TextInput:
                    multiline: False

                    # This text input should be 400x30 px.
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""

                        # This label should be 280 px wide.
                        size_hint_x: None                
                        width: 280

                    Label:
                        text: "$"

                        # This label should be 20x30 px.
                        size_hint: None, None
                        width: 20
                        height: 30

                    TextInput:
                        multiline: False     

                        # This text input should be 250x30 px.
                        size_hint: None, None                        
                        width: 250 
                        height: 30        

    ### ENDING CONDITIONS ###
    BoxLayout:
        orientation: 'vertical' 

        # This area should occupy 40% of the available height.
        size_hint: (1, .4)       

        # title label
        Label:
            text: "Ending Conditions"

            # Let's make the text slightly bigger.
            font_size: 20

            # This label should be 30 px wide.
            size_hint: (1, None)
            height: 30

        # radio buttons
        GridLayout:
            rows: 3

            # option 1: money
            CheckBox:
                group: 'conditions'

                # The width should be 5% of the available width.
                size_hint_x: .05

            Label:
                text: "The game is over when there is only one player with any money left."

            # option 2: races
            CheckBox:
                group: 'conditions'

                # The width should be 5% of the available width.
                size_hint_x: .05

            BoxLayout:                
                Label:
                    text: "The game is over not later than after a given number of races."
                TextInput:
                    multiline: False
                    
                    # The size of the text input should be 250x30 px.
                    size_hint: None, None                    
                    width: 250
                    height: 30

            # option 3: time
            CheckBox:
                group: 'conditions'

                # The width should be 5% of the available width.
                size_hint_x: .05

            BoxLayout:                
                Label:
                    text: "The game is over not later than the total racing time has elapsed."
                TextInput:
                    multiline: False

                    # The size of the text input should be 250x30 px.
                    size_hint: None, None                    
                    width: 250
                    height: 30

    ### READY BUTTON ###
    Button:
        text: 'Ready'

        # The button should be 200x40 px.
        size_hint: (None, None)        
        size: 200, 40

        # And it should be centered.
        pos_hint: {'center_x': 0.5}

Now run the program and you will see this:

better sizing and positioning

As you can see, some og the widgets, like the Initial Money label and text inputs, have been cut off. But don’t worry, the app window will eventually be bigger, so everything will fit in. For now, you can try resizing the window to see the missing parts:

resizing the window

Now it looks better. But still, I don’t quite like it yet. For example the labels would look better if they were left-aligned.

Blender Jumpstart Course

Learn the basics of 3D modeling in Blender.

step-by-step, easy to follow, visually rich

The course is available on Udemy and on Skillshare.

Aligning Label Text

You can align both horizontally and vertically in Kivy. You should use the halign and valign properties respectively. Let’s align all the labels in the kv file horizontally and vertically, to the left in the former case and to the center in the latter. There is one more thing you must be aware of. If you just set the halign and valign properties, you won’t see the effect. This is because the label text does not occupy the whole label. To work around it, you should set the text_size property to the size of the label. As this property is defined inside the label, you can refer to the label using the self variable. So, if you want to align the label to the left horizontally and to the center vertically, the whole code should look like this:

text_size: self.size
halign: 'left'
valign: 'center'

And now let’s align all the labels in the kv file. You can see in the comments where the alignment occurs:

# File name: settings.kv

<SettingsScreen>:
    orientation: 'vertical'
    
    ### SETTINGS LABEL ###
    Label:
        text: 'Settings'
        font_size: 28
        size_hint: (1, None)
        height: 30

        # Align to the left horizontally and to the center vertically.
        text_size: self.size
        halign: 'left'
        valign: 'center'

    ### THE PLAYERS ###  
    BoxLayout:
        orientation: 'vertical'
        
        # Title         
        Label:
            text: 'The Players'
            font_size: 20
            size_hint: (1, None)
            height: 30

            # Align to the left horizontally and to the center vertically.
            text_size: self.size
            halign: 'left'
            valign: 'center'

        # Radiobuttons 
        BoxLayout:
            size_hint: (.4, None)
            height: 50
            
            # 1 player
            BoxLayout:
                CheckBox:
                    group: 'players'
                    size_hint: (.5, 1) 
                Label:
                    text: '1 player'

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

            # 2 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                    size_hint: (.5, 1) 
                Label:
                    text: '2 players'

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

            # 3 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                    size_hint: (.5, 1) 
                Label:
                    text: '3 players'

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

            # 4 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                    size_hint: (.5, 1) 
                Label:
                    text: '4 players'

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

        # Player name and initial money setup 
        BoxLayout:
            orientation: 'vertical'

            # the headers row
            BoxLayout:
                Label:
                    text: ""
                    size_hint_x: None                
                    width: 80

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                # name header
                Label:
                    text: "Name"
                    size_hint_x: None                
                    width: 700

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                # money header
                Label:
                    text: "Initial Money"

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

            # the players rows
            # player 1
            BoxLayout:
                Label:
                    text: 'Player 1'
                    size_hint: None, None
                    width: 80
                    height: 30

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'
                
                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""
                        size_hint_x: None                
                        width: 280

                        # Align to the left horizontally and to the center vertically.
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'
                                      
                    Label:
                        text: "$"
                        size_hint: None, None
                        width: 20
                        height: 30

                        # Align to the left horizontally and to the center vertically.
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    TextInput:
                        multiline: False
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 2
            BoxLayout:
                Label:
                    text: 'Player 2'
                    size_hint: None, None
                    width: 80
                    height: 30

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""
                        size_hint_x: None                
                        width: 280

                        # Align to the left horizontally and to the center vertically.
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    Label:
                        text: "$"
                        size_hint: None, None
                        width: 20
                        height: 30

                        # Align to the left horizontally and to the center vertically.
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    TextInput:
                        multiline: False
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 3
            BoxLayout:
                Label:
                    text: 'Player 3'
                    size_hint: None, None
                    width: 80
                    height: 30

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""
                        size_hint_x: None                
                        width: 280

                        # Align to the left horizontally and to the center vertically.
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    Label:
                        text: "$"
                        size_hint: None, None
                        width: 20
                        height: 30

                        # Align to the left horizontally and to the center vertically.
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    TextInput:
                        multiline: False
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 4
            BoxLayout:
                Label:
                    text: 'Player 4'
                    size_hint: None, None
                    width: 80
                    height: 30

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""
                        size_hint_x: None                
                        width: 280

                        # Align to the left horizontally and to the center vertically.
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    Label:
                        text: "$"
                        size_hint: None, None
                        width: 20
                        height: 30

                        # Align to the left horizontally and to the center vertically.
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    TextInput:
                        multiline: False  
                        size_hint: None, None                        
                        width: 250 
                        height: 30        

    ### ENDING CONDITIONS ###
    BoxLayout:
        orientation: 'vertical' 
        size_hint: (1, .4)       

        # title label
        Label:
            text: "Ending Conditions"
            font_size: 20
            size_hint: (1, None)
            height: 30

            # Align to the left horizontally and to the center vertically.
            text_size: self.size
            halign: 'left'
            valign: 'center'

        # radio buttons
        GridLayout:
            rows: 3

            # option 1: money
            CheckBox:
                group: 'conditions'
                size_hint_x: .05

            Label:
                text: "The game is over when there is only one player with any money left."

                # Align to the left horizontally and to the center vertically.
                text_size: self.size
                halign: 'left'
                valign: 'center'

            # option 2: races
            CheckBox:
                group: 'conditions'
                size_hint_x: .05

            BoxLayout:                
                Label:
                    text: "The game is over not later than after a given number of races."

                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 250
                    height: 30

            # option 3: time
            CheckBox:
                group: 'conditions'
                size_hint_x: .05

            BoxLayout:                
                Label:
                    text: "The game is over not later than the total racing time has elapsed."
                    
                    # Align to the left horizontally and to the center vertically.
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 250
                    height: 30

    ### READY BUTTON ###
    Button:
        text: 'Ready'
        size_hint: (None, None)        
        size: 200, 40
        pos_hint: {'center_x': 0.5}

Now run the program and resize the window so that you can see the missing parts of the widgets. It should look like this:

aligning label text

Now the labels are aligned, but the widgets are packed very tightly, with very little space around them. This doesn’t look good. So, our last thing to do for now is to add some padding and spacing.

Add Padding and Spacing to the Settings Screen

You can add some padding and spacing to the Settings screen. What’s the difference?

Well, padding determines the distance between the layout box and the children. You can use it with 1, 2 or 4 arguments. If there is 1 argument, the padding is the same on each side. If there are 2 arguments, the first one is for horizontal padding and the second for vertical padding. There may also be four arguments, then they determine the padding in the following order: left, top, right, bottom.

On the other hand, spacing determines the distances between children. It takes just one argument. The default value is 0.

So, let’s add padding and spacing to our Settings screen in the code. Here’s the kv file one more time with information about padding and spacing in the comments:

# File name: settings.kv

<SettingsScreen>:
    orientation: 'vertical'

    # Let's add 10 px of spacing and 10 px of padding on each side of the layout box.
    padding: 10
    spacing: 10
        
    ### SETTINGS LABEL ###
    Label:
        text: 'Settings'
        font_size: 28
        size_hint: (1, None)
        height: 30
        text_size: self.size
        halign: 'left'
        valign: 'center'

    ### THE PLAYERS ###  
    BoxLayout:
        orientation: 'vertical'

        # Let's add 10 px of spacing and 10 px of padding on each side of the layout box.
        padding: 10
        spacing: 10
        
        # Title         
        Label:
            text: 'The Players'
            font_size: 20
            size_hint: (1, None)
            height: 30
            text_size: self.size
            halign: 'left'
            valign: 'center'

        # Radiobuttons 
        BoxLayout:
            size_hint: (.4, None)
            height: 50
            
            # 1 player
            BoxLayout:
                CheckBox:
                    group: 'players'
                    size_hint: (.5, 1) 
                Label:
                    text: '1 player'
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

            # 2 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                    size_hint: (.5, 1) 
                Label:
                    text: '2 players'
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

            # 3 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                    size_hint: (.5, 1) 
                Label:
                    text: '3 players'
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

            # 4 players
            BoxLayout:
                CheckBox:
                    group: 'players'
                    size_hint: (.5, 1) 
                Label:
                    text: '4 players'
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

        # Player name and initial money setup 
        BoxLayout:
            orientation: 'vertical'

            # the headers row
            BoxLayout:
                Label:
                    text: ""
                    size_hint_x: None                
                    width: 80
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                # name header
                Label:
                    text: "Name"
                    size_hint_x: None                
                    width: 700
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                # money header
                Label:
                    text: "Initial Money"
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

            # the players rows
            # player 1
            BoxLayout:
                Label:
                    text: 'Player 1'
                    size_hint: None, None
                    width: 80
                    height: 30
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'
                
                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""
                        size_hint_x: None                
                        width: 280
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'
                                      
                    Label:
                        text: "$"
                        size_hint: None, None
                        width: 20
                        height: 30
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    TextInput:
                        multiline: False
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 2
            BoxLayout:
                Label:
                    text: 'Player 2'
                    size_hint: None, None
                    width: 80
                    height: 30
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""
                        size_hint_x: None                
                        width: 280
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    Label:
                        text: "$"
                        size_hint: None, None
                        width: 20
                        height: 30
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    TextInput:
                        multiline: False
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 3
            BoxLayout:
                Label:
                    text: 'Player 3'
                    size_hint: None, None
                    width: 80
                    height: 30
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""
                        size_hint_x: None                
                        width: 280
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    Label:
                        text: "$"
                        size_hint: None, None
                        width: 20
                        height: 30
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    TextInput:
                        multiline: False
                        size_hint: None, None                        
                        width: 250 
                        height: 30

            # player 4
            BoxLayout:
                Label:
                    text: 'Player 4'
                    size_hint: None, None
                    width: 80
                    height: 30
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 400 
                    height: 30

                BoxLayout:
                    Label:
                        text: ""
                        size_hint_x: None                
                        width: 280
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    Label:
                        text: "$"
                        size_hint: None, None
                        width: 20
                        height: 30
                        text_size: self.size
                        halign: 'left'
                        valign: 'center'

                    TextInput:
                        multiline: False  
                        size_hint: None, None                        
                        width: 250 
                        height: 30        

    ### ENDING CONDITIONS ###
    BoxLayout:
        orientation: 'vertical' 
        size_hint: (1, .4)    

        # Let's add 10 px of spacing and 10 px of padding on each side of the layout box.
        padding: 10
        spacing: 10   

        # title label
        Label:
            text: "Ending Conditions"
            font_size: 20
            size_hint: (1, None)
            height: 30
            text_size: self.size
            halign: 'left'
            valign: 'center'

        # radio buttons
        GridLayout:
            rows: 3

            # Let's add 10 px of spacing between widgets.
            spacing: 10

            # option 1: money
            CheckBox:
                group: 'conditions'
                size_hint_x: .05

            Label:
                text: "The game is over when there is only one player with any money left."
                text_size: self.size
                halign: 'left'
                valign: 'center'

            # option 2: races
            CheckBox:
                group: 'conditions'
                size_hint_x: .05

            BoxLayout:                
                Label:
                    text: "The game is over not later than after a given number of races."
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 250
                    height: 30

            # option 3: time
            CheckBox:
                group: 'conditions'
                size_hint_x: .05

            BoxLayout:                
                Label:
                    text: "The game is over not later than the total racing time has elapsed."
                    text_size: self.size
                    halign: 'left'
                    valign: 'center'

                TextInput:
                    multiline: False
                    size_hint: None, None                    
                    width: 250
                    height: 30

    ### READY BUTTON ###
    Button:
        text: 'Ready'
        size_hint: (None, None)        
        size: 200, 40
        pos_hint: {'center_x': 0.5}

If you now run the app, it will look a bit better. Here’s the Settings screen that you will see when you resize the app window a little:

padding and spacing

Well, I’m going to leave off now. Here you can compare what we have with what we’re aiming at. You can see above what we have. And this is one more time the Settings screen we’re aiming at:

settings csreen - final version

So, there’s still a lot of work to do, but I want to take a break now as far as building the other screens is concerned and refactor the code that we developed in this part. As you can see, the code is very repetitive and this should be avoided. So, in the next part we’ll extract quite a lot of the code that is shared by more than one widget into class rules. This will make the code much more concise.


Spread the love
Tags:

Leave a Reply