Skip to content
Home » Kivy Part 38 – Slugrace – Kivy Properties in the Race Screen

Kivy Part 38 – Slugrace – Kivy Properties in the Race Screen

Spread the love

In the previous part we added some Kivy properties to the Settings screen. In this part we’ll add some to the Race screen.

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…

The Slugs’ Stats

Open the race.kv file and scroll down until you find the Slugs’ Stats section:

 # File name: race.kv
  
 ...
  
 <RaceScreen>:
     ...
     
     ### INFO, STATS AND BUTTONS ###
     GridLayout:
         ...
  
         # Slugs' Stats           
         BoxLayout:
             ...
             
             BoxLayout:
                 spacing: 10
                 RegularLabel:
                     text: "Speedster"
                 RegularLabel:
                     text: '7 wins'
                 RegularLabel:   
                     text: '70%'
                     
             BoxLayout:
                 spacing: 10
                 RegularLabel:
                     text: "Trusty"
                 RegularLabel:
                     text: '1 win'
                 RegularLabel:   
                     text: '10%'
             
             BoxLayout:
                 spacing: 10
                 RegularLabel:
                     text: "Iffy"
                 RegularLabel:
                     text: '0 wins'
                 RegularLabel:   
                     text: '0%'
             
             BoxLayout:
                 spacing: 10
                 RegularLabel:
                     text: "Slowpoke"
                 RegularLabel:
                     text: '2 wins'
                 RegularLabel:   
                     text: '20%'
  
         # Players' Stats           
         ... 

Here the code is pretty simple and actually we could leave it as is, but we can still simplify it slightly with some properties. You can see four BoxLayouts, one for each slug. Each contains three RegularLabels. So, in the race.py file we’ll add the SlugStats class that inherits from BoxLayout and define three Kivy properties in it, a StringProperty and two NumericProperties. We’ll name the StringProperty name and the NumericProperties wins and win_percent and set their default values to an empty string, 0 and 0 respectively. We have to import the NumericProperty and StringProperty classes first.

So, here’s the code:

 # File name: race.py
  
 import kivy
 from kivy.app import App
 from kivy.uix.boxlayout import BoxLayout
 from kivy.config import Config
  
 # Import the classes that you will need.
 from kivy.properties import NumericProperty, StringProperty
  
 # Configuration
 ...
  
 class SlugStats(BoxLayout):
     name = StringProperty('')
     wins = NumericProperty(0)
     win_percent = NumericProperty(0)
  
 class RaceScreen(BoxLayout):
     ... 

Then, in the kv file we have to add the SlugStats rule and set the text properties of the three RegularLabels to name, wins and win_percent respectively.

Now, as for the three properties, we must make sure that:

– the text on the first RegularLabel displays just the value of the name property

– the text on the second RegularLabel displays the value of the wins property followed by the word ‘win’ or ‘wins’ depending on whether the value of wins is 1 or anything else. We can use concatenation to add the ‘win’ or ‘wins’ word and use the ternary if statement to decide which of the two should be chosen.

– the text on the third RegularLabel displays the value of the win_percent property followed by the % sign.

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

Finally we’ll replace the BoxLayouts in the Slugs’ Stats subarea by instances of the SlugStats class. We’ll use the same values as before. And here’s the kv file:

 # File name: race.kv
  
 ...
  
 <SlugStats>:     
     spacing: 10
     RegularLabel:
         text: root.name
     RegularLabel:
         text: str(root.wins) + (' win' if root.wins == 1 else ' wins')
     RegularLabel:   
         text: str(root.win_percent) + '%'
  
 <RaceScreen>:
     ...
     
     ### INFO, STATS AND BUTTONS ###
     ...
  
         # Slugs' Stats           
         ...
  
             BoldLabel:
                 text: "Slugs' Stats"
  
             SlugStats:
                 name: 'Speedster'
                 wins: 7
                 win_percent: 70
  
             SlugStats:
                 name: 'Trusty'
                 wins: 1
                 win_percent: 10
  
             SlugStats:
                 name: 'Iffy'
                 wins: 0
                 win_percent: 0
  
             SlugStats:
                 name: 'Slowpoke'
                 wins: 2
                 win_percent: 20
                         
         # Players' Stats           
         ... 

If we run the program, it will work as before.

Python Jumpstart Course

Learn the basics of Python, including OOP.

with lots of exercises, easy to follow

The course is available on Udemy.

The Players’ Stats

Just below the Slug’s Stats section is the Players’ Stats section. Let’s simplify this part too. In the Python file let’s add the PlayerStats class that inherits from BoxLayout and add two properties to it: the StringProperty name and the NumericProperty money. Let’s initialize them to an empty string and 0 respectively:

 # File name: race.py
  
 ...
  
 class SlugStats(BoxLayout):
     ...
  
 class PlayerStats(BoxLayout):
     name = StringProperty('')
     money = NumericProperty(0)
  
 class RaceScreen(BoxLayout):
     ... 

Then, in the kv file, let’s add the corresponding rule. There are two RegularLabels. The first one’s text should be set to the name property and the second one’s text should read: ‘has $’ followed by the value of the money property. Finally, let’s use instances of the PlayerStats class in the code instead of the BoxLayouts. We’ll set the properties to the same values for now:

 # File name: race.kv
  
 ...
  
 <SlugStats>:     
     ...
  
 <PlayerStats>: 
     RegularLabel:
         text: root.name
     RegularLabel:
         text: 'has $' + str(root.money)
  
 <RaceScreen>:
     ...
     
     ### INFO, STATS AND BUTTONS ###
     ...
                         
         # Players' Stats           
         BoxLayout:
             ...
  
             BoldLabel:
                 text: "Players' Stats"   
  
             PlayerStats:
                 name: 'Player 1'
                 money: 1000        
  
             PlayerStats:
                 name: 'Player 2'
                 money: 800    
  
             PlayerStats:
                 name: 'Player 3'
                 money: 1300    
  
             PlayerStats:
                 name: 'Player 4'
                 money: 1200                         
            
         # Buttons
         ... 

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.

Slug Info in the Track Area

We’re still in the Race screen. If you scroll down, you will find the Track area with the labels with slug info. They look like they can be simplified as well:

 # File name: race.kv
  
 ...
  
 <RaceScreen>:
     ...
                                   
     ### THE TRACK ###
     ...
  
         # track 
         ...
  
             # labels with slug info
             BoxLayout:
                 orientation: 'vertical'
                 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 wins'                  
                     
             # the odds labels
             ... 

As you can see, they look pretty much the same. There are some differences, though:

– the center_y property inside the pos_hint property is different,

– the names of the slugs are different,

– the wins stats are different.

When defining our class, we have to take the differences into account. So, in the Python file, let’s add the SlugInfo class that inherits from BoxLayout and let’s add three properties to it:

– the NumericProperty y_position, initialized to 0,

– the StringProperty name, initialized to an empty string,

– the NumericProperty wins, initialized to 0.

Here’s the Python file:

 # File name: race.py
  
 ...
  
 class SlugStats(BoxLayout):
     ...
  
 class PlayerStats(BoxLayout):
     ...
  
 class SlugInfo(BoxLayout):
     y_position = NumericProperty(0)
     name = StringProperty('')
     wins = NumericProperty(0)
  
 class RaceScreen(BoxLayout):
     ... 

Then, in the kv file let’s add the corresponding rule. In the rule you should set the orientation, size_hint and size properties to the same values as they are in the BoxLayouts. You should also set the pos_hint property, which requires a dictionary. So, in the dictionary x should be set to .004, so just as it is in the BoxLayouts, but center_y should be set to the y_position property.

Then, in the two WhiteLabels, we must set the text properties to the name and wins properties respectively. In the latter case we’ll use the same concatenated string as in the SlugStats rule (so with the ternary if statement).

Finally, let’s replace the four BoxLayouts in the code with instances of the SlugInfo class. For the name and y_position properties use the same values as before, but the wins properties for the particular slugs should be set to 7, 1, 0 and 2 respectively so that they match the numbers in the Slugs’ Stats subarea. Here’s the kv file:

 # File name: race.kv
  
 ...
  
 <SlugStats>:     
     ...
  
 <PlayerStats>: 
     ...
  
 <SlugInfo>:
     orientation: 'vertical'
     size_hint: None, None
     size: 100, 50
     pos_hint: {'x': .004, 'center_y': root.y_position}
  
     WhiteNameLabel:
         text: root.name                 
  
     WhiteWinsLabel:
         text: str(root.wins) + (' win' if root.wins == 1 else ' wins')
  
 <RaceScreen>:
     ...
                                   
     ### THE TRACK ###
     ...
  
         # track 
         ...
  
             # labels with slug info
             SlugInfo:
                 y_position: .875
                 name: 'Speedster'
                 wins: 7
  
             SlugInfo:
                 y_position: .625
                 name: 'Trusty'
                 wins: 1
  
             SlugInfo:
                 y_position: .375
                 name: 'Iffy'
                 wins: 0
  
             SlugInfo:
                 y_position: .125
                 name: 'Slowpoke'
                 wins: 2
                                                
             # the odds labels
             ... 

The Slug Images

We’re not yet done with the Race screen. Here come the four slug images:

 # File name: race.kv
  
 ...
  
 <RaceScreen>:
     ...
                                   
     ### THE TRACK ###
     BoxLayout:  
         ...
  
         # track 
         ...            
  
             # slug images
  
             # Speedster
             RelativeLayout:
                 pos_hint: {'x': .09, 'center_y': .875}
                 size_hint: None, None
                 size: 143, 30
                 Image:
                     source: 'atlas://assets/slugs/slugs/speedsterBody'
  
                 # 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: 'atlas://assets/slugs/slugs/speedsterEye'
                     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: 'atlas://assets/slugs/slugs/speedsterEye'
                     pos_hint: {'x': .95, 'y': .3}
                     size_hint: 0.25, 0.25
  
             # Trusty
             RelativeLayout:
                 pos_hint: {'x': .09, 'center_y': .625}
                 size_hint: None, None
                 size: 143, 30
  
                 # the body image
                 Image:
                     source: 'atlas://assets/slugs/slugs/trustyBody'
  
                 # 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: 'atlas://assets/slugs/slugs/trustyEye'
                     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: 'atlas://assets/slugs/slugs/trustyEye'
                     pos_hint: {'x': .95, 'y': .3}
                     size_hint: 0.25, 0.25
  
             # Iffy
             RelativeLayout:
                 pos_hint: {'x': .09, 'center_y': .375}
                 size_hint: None, None
                 size: 143, 30
  
                 # the body image
                 Image:
                     source: 'atlas://assets/slugs/slugs/iffyBody'
  
                 # 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: 'atlas://assets/slugs/slugs/iffyEye'
                     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: 'atlas://assets/slugs/slugs/iffyEye'
                     pos_hint: {'x': .95, 'y': .3}
                     size_hint: 0.25, 0.25
  
             # Slowpoke
             RelativeLayout:
                 pos_hint: {'x': .09, 'center_y': .125}
                 size_hint: None, None
                 size: 143, 30
  
                 # the body image
                 Image:
                     source: 'atlas://assets/slugs/slugs/slowpokeBody'
  
                 # 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: 'atlas://assets/slugs/slugs/slowpokeEye'
                     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: 'atlas://assets/slugs/slugs/slowpokeEye'
                     pos_hint: {'x': .95, 'y': .3}
                     size_hint: 0.25, 0.25
         
         # winner
         ... 

These look pretty complex, right? That’s mainly because they are. But don’t worry, your task is easier than it looks.

Each slug is represented by a RelativeLayout that contains three images, the body image and two eye images. Now, what are the differences? If we know this, we’ll know what properties we need. So, the four slug images differ in the following:

– the body and eye images have different sources,

– just like with the SlugInfo widgets, the center_y property inside the pos_hint property of the RelativeLayout for each slug is different.

And that’s essentially it. All the other properties are shared in some way. So, we will need three properties, two for the sources of the body and eye images and one for the center_y value inside the pos_hint property. So, in the Python file let’s add the SlugImage class that inherits from RelativeLayout (let’s not forget to import the RelativeLayout class first) and let’s add the following properties:

– the StringProperty body_image, initialized to an empty string,

– the StringProperty eye_image, initialized to an empty string,

– the NumericProperty y_position, initialized to 0.

Here’s the Python file:

 # File name: race.py
  
 import kivy
 from kivy.app import App
 from kivy.uix.boxlayout import BoxLayout
 from kivy.uix.relativelayout import RelativeLayout
 from kivy.config import Config
 from kivy.properties import NumericProperty, StringProperty
  
 # Configuration
 ...
  
 class SlugStats(BoxLayout):
     ...
  
 class PlayerStats(BoxLayout):
     ...
  
 class SlugInfo(BoxLayout):
     ...
  
 class SlugImage(RelativeLayout):
     body_image = StringProperty('')
     eye_image = StringProperty('')
     y_position = NumericProperty(0)
  
 class RaceScreen(BoxLayout):
     ... 

Then, in the kv file let’s add the SlugImage rule. Let’s follow these guidelines:

1. The root’s size_hint and size properties should be set like before, so to (None, None) and (143, 30) respectively.

2. In the root’s pos_hint property x should be set like before, so to .09, and center_y should be set to the y_position property.

3. To make the code more concise later in the code, let’s set the source of the body image to the string ‘assets/slugs/’ followed by the value of the body_image property and the file format extension. This will allow us to set the body_image property later in the code to something like ‘speedsterBody’ instead of the full path.

4. We’ll use the same strategy to set the source properties of the eye images accordingly. The rest of the eye images should be implemented exactly like before.

Finally, let’s replace the four RelativeLayouts in the code by instances of the SlugImage class. Let’s set the properties to the same values. Remember to set the body_image and eye_image properties just to the names of the particular images, not the full paths. Here’s the kv file:

 # File name: race.kv
  
 ### CLASS RULES ###
  
 ...
  
 <SlugStats>:     
     ...
  
 <PlayerStats>: 
     ...
  
 <SlugInfo>:
     ...
  
 <SlugImage>:
     pos_hint: {'x': .09, 'center_y': root.y_position}
     size_hint: None, None
     size: 143, 30
  
     # the body image
     Image:
         source: 'assets/slugs/' + root.body_image + '.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/' + root.eye_image + '.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/' + root.eye_image + '.png'
         pos_hint: {'x': .95, 'y': .3}
         size_hint: 0.25, 0.25
  
 <RaceScreen>:
     ...
                                   
     ### THE TRACK ###
     BoxLayout:  
         ...
  
         # track 
         RelativeLayout:
             ...            
  
             # slug images
             # Speedster
             SlugImage:
                 body_image: 'speedsterBody'
                 eye_image: 'speedsterEye'
                 y_position: .875
             
             # Trusty
             SlugImage:
                 body_image: 'trustyBody'
                 eye_image: 'trustyEye'
                 y_position: .625
  
             # Iffy
             SlugImage:
                 body_image: 'iffyBody'
                 eye_image: 'iffyEye'
                 y_position: .375
  
             # Slowpoke
             SlugImage:
                 body_image: 'slowpokeBody'
                 eye_image: 'slowpokeEye'
                 y_position: .125
                    
         # 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: 'atlas://assets/silhouettes/silhouettes/Trusty'
     
     ### THE BETS ###
     Label:
         canvas:
             Color:
                 rgba: .2, .1, 0, 1
             Line:
                 rounded_rectangle: self.x, self.y, self.width, self.height, 10        
                 width: 2
  
         color: .2, .1, 0, 1
         text: 'The Bets' 

That’s it as far as the Race screen is concerned. Run the app again to make sure everything works as before. And in the next part we’ll move on to the Bets screen and add some Kivy properties there.


Spread the love

Leave a Reply