Skip to content
Home » Kivy Part 31 – Adding the Graphical Assets to the Slugrace Project

Kivy Part 31 – Adding the Graphical Assets to the Slugrace Project

Spread the love

In the previous part we added a background image to the Players area of the Settings screen. Today we’ll see how to add graphical assets to our project.

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…

There are graphical assets in the Race screen. Here’s what they look like in the final version:

race screen

So, there is the track image, the four images of the slugs in top view and the silhouette image of the winning slug. Besides, there are some labels on top of the track, which we are going to add as well.

The Track Image

Let’s start with the racetrack.png image. You can find the file in the assets folder. If you hover your mouse over the file, you can see how big the image is. It’s 1000 x 200 px. And this is the size we want it to be at all times during the game. Even if you resize the app window, the size of the racetrack image shouldn’t change. So, we need a fixed size for the widget.

Now, what widget are we actually going to use? Well, there are several options, but the easiest one seems to be the Image widget, which is available in Kivy out of the box. All you have to do is set its source property to the path to the image file.

So, let’s try to implement the track image. At this moment we have some placeholder code in the Track area of the Race screen that displays a label with the word ‘TRACK’. Here’s the kv code:

# File name: race.kv

...

<RaceScreen>:
    ...
                                  
    ### THE TRACK ###
    BoxLayout:  
        ...

        # track        
        Label: 
            color: .2, .1, 0, 1           
            text: 'TRACK'
        
        # winner
        ...
    
    ### THE BETS ###
    ...

And this is what the screen looks like after resizing the app window slightly:

race screen

So, let’s replace the label with an Image widget:

# File name: race.kv

...
                                  
    ### THE TRACK ###
    BoxLayout:  
        ...

        # track        
        Image:
            # Here's the path to the image file.
            source: 'assets/racetrack.png'

            # The image should be a fixed size
            size_hint: None, None
            size: 1000, 200

            # Let's also center the image vertically within its layout.
            pos_hint: {'center_y': .5}
        
        # winner
        ...
    
    ### THE BETS ###
    ...

Now run the app and try resizing the window. As you can see, the image is always the same size and it’s always vertically centered:

track image

Now, the track image is not the only thing we need in the track subarea. There are also the top-view images of the four slugs and some labels. So, it would be convenient to enclose all these elements in another container. It will be easier for us to use coordinates relative to the track subarea when we add the other widgets, so a good candidate for the container is the RelativeLayout. So, let’s add the layout and put the track image in it:

# File name: race.kv

...
                                  
    ### THE TRACK ###
    BoxLayout:  
        ...

        # track 

        # Let's put the track image in a RelativeLayout, along with some
        # other widgets that we are going to add in a minute.   
        RelativeLayout:
            # Let's move the sizing and positioning code to the container.
            size_hint: None, None
            size: 1000, 200
            pos_hint: {'center_y': .5}    
            Image:
                source: 'assets/racetrack.png'
                # size_hint: None, None
                # size: 1000, 200
                # pos_hint: {'center_y': .5}
        
        # winner
        ...
    
    ### THE BETS ###
    ...

You may have noticed that I moved the sizing and positioning code to the layout now. There will be no difference if you run the app now, but it will make our life easier when we then add the other widgets… Speaking of which, let’s add the white labels and the four slug images.

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 White Labels

Now, with the track image in place, let’s position the labels on it. First of all, let’s add some class rules. There are three types of labels: the medium-sized ones for the names of the slugs, the small ones for the wins and the big ones for the odds. Let’s name the classes WhiteNameLabel, WhiteWinsLabel and WhiteOddsLabel respectively. Here are the class rules:

# File name: race.kv

### CLASS RULES ###

<RegularLabel@Label>:
    color: .2, .1, 0, 1
    text_size: self.size
    halign: 'left'
    valign: 'center'

<BoldLabel@RegularLabel>:    
    bold: True

# the white labels
<WhiteOddsLabel@BoldLabel>:
    font_size: 32
    color: 1, 1, 1, 1    

<WhiteNameLabel@BoldLabel>:
    font_size: 18 
    color: 1, 1, 1, 1       

<WhiteWinsLabel@BoldLabel>:
    font_size: 14   
    color: 1, 1, 1, 1
    
...  

As you can see, these are just simple labels that inherit from the BoldLabel class, each of them with a different font size. Later in the series, when win discuss properties, we will create a custom widget that will hold the slug name and wins information, but for now let’s just create four separate BoxLayouts, one for each slug.

Now, the positions of the widgets in the RelativeLayout will be relative to the container. To better understand where the vertical positions that I’m using come from, here’s a little image of the RelativeLayout with the track image and the proportions that you can see in the code:

race track

We just want each BoxLayout with the two slug info labels to be centered on the slug’s own lane. By the way, the four lanes are separated by white lines, which are part of the image. The starting line and the finish line are also there.

And here’s the code:

# File name: race.kv

...
                                  
    ### THE TRACK ###
    BoxLayout:  
        ...

        # track 
        RelativeLayout:
            size_hint: None, None
            size: 1000, 200
            pos_hint: {'center_y': .5}    
            Image:
                source: 'assets/racetrack.png'        

            # labels with slug info
            # We'll use a vertical BoxLayout for each slug.
            BoxLayout:
                orientation: 'vertical'

                # The BoxLayout will have a fixed size that will help
                # us keep the text where we want it. To position the
                # BoxLayout we're using the pos_hint property with 
                # values that will place the layout just slightly to
                # the right of the left border of the parent layout
                # (RelativeLayout) and near the top. These values are 
                # relative to the RelativeLayout.
                size_hint: None, None
                size: 100, 50
                pos_hint: {'x': .004, 'center_y': .875}

                WhiteNameLabel:
                    text: 'Speedster'                 

                WhiteWinsLabel:
                    text: '0 wins'       

            BoxLayout:
                orientation: 'vertical'
                size_hint: None, None
                size: 100, 50
                pos_hint: {'x': .004, 'center_y': .625}

                WhiteNameLabel:
                    text: 'Trusty'                 

                WhiteWinsLabel:
                    text: '0 wins'   

            BoxLayout:
                orientation: 'vertical'
                size_hint: None, None
                size: 100, 50
                pos_hint: {'x': .004, 'center_y': .375}

                WhiteNameLabel:
                    text: 'Iffy'                 

                WhiteWinsLabel:
                    text: '0 wins'   

            BoxLayout:
                orientation: 'vertical'
                size_hint: None, None
                size: 100, 50
                pos_hint: {'x': .004, 'center_y': .125}

                WhiteNameLabel:
                    text: 'Slowpoke'                 

                WhiteWinsLabel:
                    text: '0' 
        
        # winner
        ...
    
    ### THE BETS ###
    ...

If you now run the app and resize the window, you will see the name and win info on the track:

race screen

Even if you resize the window, the labels will remain on the track, relative to the RelativeLayout their containing BoxLayouts are in.

Python Jumpstart Course

Learn the basics of Python, including OOP.

with lots of exercises, easy to follow

The course is available on Udemy.

The last thing to do, as far as labels are concerned, are the big odds labels. They should be positioned near the finish line. Here’s the code:

# File name: race.kv

...
                                  
    ### THE TRACK ###
    BoxLayout:  
        ...

        # track 
        RelativeLayout:
            ...

            # labels with slug info
            ...                  
                    
            # the odds labels
            WhiteOddsLabel:
                text: '1.42'                
                pos_hint: {'x': .77, 'center_y': .875}

            WhiteOddsLabel:
                text: '1.61'                
                pos_hint: {'x': .77, 'center_y': .625}

            WhiteOddsLabel:
                text: '2.53'                
                pos_hint: {'x': .77, 'center_y': .375}

            WhiteOddsLabel:
                text: '2.89'                
                pos_hint: {'x': .77, 'center_y': .125}           
        
        # winner
        ...
    
    ### THE BETS ###
    ...

Now we have all the labels in place:

race screen

Now we need the top view images of the four slugs.

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.

The Top View Slug Images

You can find the images in the slugs subfolder of the assets folder. There are actually two images for each slug: body (235 x 49 px) and eye (54 x 12 px). Each slug image should then actually consist of three smaller images: the body and two eyes. To make the code less repetitive, we will later create a custom widget and then add four instances of it in the track subarea, but for now, let’s add just four RelativeLayouts each containing the respective body and two eyes.

As you can see in the final version of the Race screen, the slugs will rotate their eyes. For now, the eyes will be immobile, but still, we will rotate them. To do that, we’ll use some canvas context instructions like PushMatrix and PopMatrix to save and restore the context respectively, as well as Rotate, to actually rotate the eyes. And one more thing, when I use the word ‘eye’ in the context of our project, I mean the whole tentancle with the eye at the end. Sorry for not being biologically correct.

Here’s the code with explanations in comments. It’s lengthy, but pretty straightforward, so just take your time to examine it carefully:

# File name: race.kv

...
                                  
    ### THE TRACK ###
    BoxLayout:  
        ...

        padding: 10

        # track 
        RelativeLayout:
            ...

            # labels with slug info
            ...                  
                    
            # the odds labels
            ...
            

            # slug images

            # Speedster - the first slug

            # By putting the body image and two instances of the eye
            # image in a RelativeLayout, we will be able to position 
            # the eyes relative to the body easily.
            # The exact values that I'm using in the size and pos_hint
            # properties were determined by trial and error and just
            # seem to be working fine for our purposes.
            RelativeLayout:
                pos_hint: {'x': .09, 'center_y': .875}
                size_hint: None, None
                size: 143, 30

                # the body image
                Image:
                    source: 'assets/slugs/speedsterBody.png'
                    
                # the left eye image
                Image:
                    canvas.before:
                        # Let's save the context before rotation.
                        PushMatrix

                        # Let's rotate the eye 30 degrees counterclockwise
                        # on the Z axis around the point which is horizontally
                        # on the left border of the eye image and vertically
                        # halfway its height.
                        Rotate:
                            angle: 30
                            axis: 0, 0, 1
                            origin: self.x, self.center_y
                    canvas.after:
                        # Let's restore the context.
                        PopMatrix                    

                    source: 'assets/slugs/speedsterEye.png'

                    # The position of the eye is relative to the RelativeLayout
                    # it's in along with the body image and the other eye image.
                    pos_hint: {'x': .95, 'y': .45}

                    # Let's also scale the eye image down to look well-proportioned.
                    size_hint: 0.25, 0.25

                # the right eye image
                Image:
                    canvas.before:
                        PushMatrix
                        Rotate:
                            angle: -30
                            axis: 0, 0, 1
                            origin: self.x, self.center_y
                    canvas.after:
                        PopMatrix

                    source: 'assets/slugs/speedsterEye.png'
                    pos_hint: {'x': .95, 'y': .3}
                    size_hint: 0.25, 0.25

            # Trusty - the second slug
            RelativeLayout:
                pos_hint: {'x': .09, 'center_y': .625}
                size_hint: None, None
                size: 143, 30

                # the body image
                Image:
                    source: 'assets/slugs/trustyBody.png'

                # the left eye image
                Image:
                    canvas.before:
                        PushMatrix
                        Rotate:
                            angle: 30
                            axis: 0, 0, 1
                            origin: self.x, self.center_y
                    canvas.after:
                        PopMatrix

                    source: 'assets/slugs/trustyEye.png'
                    pos_hint: {'x': .95, 'y': .45}
                    size_hint: 0.25, 0.25

                # the right eye image
                Image:
                    canvas.before:
                        PushMatrix
                        Rotate:
                            angle: -30
                            axis: 0, 0, 1
                            origin: self.x, self.center_y
                    canvas.after:
                        PopMatrix

                    source: 'assets/slugs/trustyEye.png'
                    pos_hint: {'x': .95, 'y': .3}
                    size_hint: 0.25, 0.25

            # Iffy - the third slug
            RelativeLayout:
                pos_hint: {'x': .09, 'center_y': .375}
                size_hint: None, None
                size: 143, 30

                # the body image
                Image:
                    source: 'assets/slugs/iffyBody.png'

                # the left eye image
                Image:
                    canvas.before:
                        PushMatrix
                        Rotate:
                            angle: 30
                            axis: 0, 0, 1
                            origin: self.x, self.center_y
                    canvas.after:
                        PopMatrix

                    source: 'assets/slugs/iffyEye.png'
                    pos_hint: {'x': .95, 'y': .45}
                    size_hint: 0.25, 0.25

                # the right eye image
                Image:
                    canvas.before:
                        PushMatrix
                        Rotate:
                            angle: -30
                            axis: 0, 0, 1
                            origin: self.x, self.center_y
                    canvas.after:
                        PopMatrix

                    source: 'assets/slugs/iffyEye.png'
                    pos_hint: {'x': .95, 'y': .3}
                    size_hint: 0.25, 0.25

            # Slowpoke - the fourth slug
            RelativeLayout:
                pos_hint: {'x': .09, 'center_y': .125}
                size_hint: None, None
                size: 143, 30

                # the body image
                Image:
                    source: 'assets/slugs/slowpokeBody.png'

                # the left eye image
                Image:
                    canvas.before:
                        PushMatrix
                        Rotate:
                            angle: 30
                            axis: 0, 0, 1
                            origin: self.x, self.center_y
                    canvas.after:
                        PopMatrix

                    source: 'assets/slugs/slowpokeEye.png'
                    pos_hint: {'x': .95, 'y': .45}
                    size_hint: 0.25, 0.25

                # the right eye image
                Image:
                    canvas.before:
                        PushMatrix
                        Rotate:
                            angle: -30
                            axis: 0, 0, 1
                            origin: self.x, self.center_y
                    canvas.after:
                        PopMatrix

                    source: 'assets/slugs/slowpokeEye.png'
                    pos_hint: {'x': .95, 'y': .3}
                    size_hint: 0.25, 0.25
        
        # winner
        ...
    
    ### THE BETS ###
    ...

If you now run the code, you will see the images of the slugs on the track:

slug images

This looks much better. The only thing that we still have to take care of is the winner image.

The Winner Image

At this moment we have the following code that creates the winner subarea of the Track area:

# File name: race.kv

...
                                  
    ### THE TRACK ###
    ...

        # track 
        ...
        
        # winner
        BoxLayout:
            orientation: 'vertical'
            size_hint: (.18, 1)

            Label:
                color: .2, .1, 0, 1
                text: "The winner is"
                font_size: 24
                size_hint: 1, .2
                bold: True

            Label:
                color: .2, .1, 0, 1
                text: "Trusty"
                font_size: 32
                size_hint: 1, .2
                bold: True

            Label:
                color: .2, .1, 0, 1
                text: 'WINNER'
    
    ### THE BETS ###
    ...

And this is what it looks like when you run the app:

winner area

Let’s replace the label with the text ‘WINNER’ with an image of a slug. Which slug exactly appears here will later depend on which one has won a particular race, but for now let’s just choose one of them as a placeholder, for example Trusty. You will find the image file in the silhouettes subfolder of the assets folder. The name of the file is Trusty.png.

Here’s the code:

# File name: race.kv

...
                                  
    ### THE TRACK ###
    ...
        
        # winner
        BoxLayout:
            orientation: 'vertical'
            size_hint: (.18, 1)

            Label:
                color: .2, .1, 0, 1
                text: "The winner is"
                font_size: 24
                size_hint: 1, .2
                bold: True

            Label:
                color: .2, .1, 0, 1
                text: "Trusty"
                font_size: 32
                size_hint: 1, .2
                bold: True

            Image:
                source: 'assets/silhouettes/Trusty.png'
    
    ### THE BETS ###
    ...

And you should now see the silhouette of Trusty on the right. This slug is going to be a permanent winner for some time:

winner area

Spread the love

2 thoughts on “Kivy Part 31 – Adding the Graphical Assets to the Slugrace Project”

  1. To be honest, this tutorial is the best of its sort that I’ve found anywhere, and I’ve scoured YouTube and Google. Your explanations make ‘building the clock’ possible: the other tutorials merely tell you what time it is, metaphorically speaking. Thank you for it! (subscribed)

    I was wondering if it were possible to see the entire ‘race.kv’ file at this stage. I’ve been following this closely, and am really struggling with the positioning of the racetrack image and the white labels. pos_hint: {whatever} produces the same result regardless of what I replace ‘whatever’ with, none of it like your ‘final race screen’ screenshot at the top of this page.

Leave a Reply