Hey guys, in the previous part of the Kivy series we handled the radio button events in the Settings screen. As mentioned before, the text input events will be handled in a separate part, so let’s now move on to the Ready Button’s on_press event. But before we start, here’s some info for you.
Table of Contents
Book Info
And Now Let’s Move On…
At this moment the on_press event is implemented like so:
# File name: settings.kv
#:import settings settings
...
### READY BUTTON ###
RedButton:
text: 'Ready'
on_press: root.manager.current = 'racescreen'
So, it just switches to another screen. By the way, now root.manager and root.game reference the same object, the Game class instance.
Anyway, besides switching to another screen, we want the Ready Button to do much more. We want it to call some methods that will take care of the game’s settings. The methods will be defined in the root widget class, which is SettingsScreen.
The first method will set the players, so let’s name it set_players. The second method will set the slugs, so let’s name it set_slugs.
Finally, the third method should set the number of races (if the game should end after a given number of races) or the time of the game (if the game should end after a specified period of time). However, in this series I’m only going to show you how to implement the first ending condition, which means the game is always going to end when there’s only one player with any money left. The two other ending conditions are implemented in my book, so feel free to grab a copy for yourself. There, I mean in my book, the method is simply named set_game.
So, we’re going to add just the first two callback methods to the on_press event on the Ready Button instance and then implement them in the settings.py file. We could also implement them directly in the Game class in main.py, but I just want the methods used for settings to be in the Settings screen.
In this part we’ll take care of setting the players and in the next part we’ll set the slugs.
The set_players Method
The set_players method will take one argument, the list of players. So we must create the players property on the root widget. In the list we’ll put the four PlayerSettings instances, which we will reference by ids. We could also pass the four players directly as arguments to the method, but this way will be more concise. Here’s the kv file:
# File name: settings.kv
#:import settings settings
...
<SettingsScreen>:
...
game: root.manager
# Here we have the players property set to a list of players.
# Each player is here represented by a PlayerSettings instance.
# The instances are referenced by ids.
players: [_player1, _player2, _player3, _player4]
...
# the players rows
# player 1
PlayerSettings:
id: _player1
label_text: 'Player 1'
# player 2
PlayerSettings:
id: _player2
label_text: 'Player 2'
# player 3
PlayerSettings:
id: _player3
label_text: 'Player 3'
# player 4
PlayerSettings:
id: _player4
label_text: 'Player 4'
...
### READY BUTTON ###
RedButton:
text: 'Ready'
on_press:
# The first method we want to call when this event is triggered
# is the set_players method. It's defined in the SettingsScreen
# class, so on the root widget. We pass the list of players
# (actually PlayerSettings instances) to it.
root.set_players(root.players)
# We still want the screen to change.
root.manager.current = 'racescreen'
Now, by setting a player I mean assigning the name and initial money that we enter in the appropriate text inputs to the name and initial_money properties respectively. To do that, we have to modify the PlayerSettings rule. Here’s the code with comments:
# File name: settings.kv
#:import settings settings
<PlayerCount>:
...
<PlayerSettings>:
# We will need two properties, one for the name text from the
# NameInput and one for the intial money text from the NumInput.
# We will reference the two text inputs by ids.
name: _player_name.text
player_initial_money: _player_initial_money.text
Regular80x30Label:
text: root.label_text
NameInput:
id: _player_name
# Let's add a hint text for the app user so that they know
# what kind of input is expected.
hint_text: "Enter the name of " + root.label_text
BoxLayout:
RegularLabel:
text: ""
size_hint_x: None
width: 280
DollarLabel:
NumInput:
id: _player_initial_money
# Let's add a hint test for the app user with the default
# amount of initial money. We could use a variable for that
# in the SlugraceApp class, but this time let's just
# hard-code it.
hint_text: '1000'
<SettingsScreen>:
...
And now let’s implement the method in the settings.py file. But before we do that, there’s one more thing I’d like to talk about for a while. Each player will have a name and an amount of money at the beginning of the game. These pieces of information will be stored in the name and initial_money properties that we defined in the Player class. We want the name to be no more than 10 characters long. We also want the initial money to be between $10 and $5000. We could naturally hard-code these limitations, but instead I’ll use some variables for them on the application level, so in the SlugraceApp class. I decided to do it that way not only because hard-coding things doesn’t seem good practice in general, but also because I want to use this opportunity to show you how to access data defined on app level in any place in the code. You already know that in the kv files you can simply use the app variable, but this time I’ll show you how to do it in Python files. So, first let’s define the three variables in the SlugraceApp class in main.py:
# File name: main.py
...
class SlugraceApp(App):
# General Settings
initial_money_min = 10
initial_money_max = 5000
max_name_length = 10
def build(self):
return Game()
if __name__ == '__main__':
from kivy.core.window import Window
Window.clearcolor = (1, 1, .8, 1)
SlugraceApp().run()
The names of the variables are pretty self-explanatory, I think. We will be using the first two variables in this part and the third one a bit later, when we add text validation to our code.
And now let’s go to the settings.py file and implement the set_players method. You will find the explanations in comments:
# File name: settings.py
...
class SettingsScreen(Screen):
# Here's the method used for the players settings.
def set_players(self, players):
# In this method we will be using the three variables that we just defined
# in the SlugraceApp class. To access the class in Python we have to call
# the get_running_app method defined in the App class.
from kivy.app import App
app = App.get_running_app()
# Now we will iterate over the players stored in the players list in the Game
# class.
for i, player in enumerate(self.game.players):
# First let's set each player's name. If the text input for the name was
# left empty, so when the app user did not enter a name for a particular
# player, the player's name will be set to something generic like Player 1,
# Player 2, etc.
player.name = 'Player ' + str(i + 1) if not players[i].name else players[i].name
# Now let's set the player's initial money. As you remember it may be any value
# between $10 and $5000. These two extreme values are saved in the two variables
# we created in the SlugraceApp class, initial_money_min and initial_money_max.
# Now we can easily access these variables using the app variable we just created
# at the beginning of this method.
player.initial_money = (1000 if not players[i].player_initial_money
else max(app.initial_money_min,
min(int(players[i].player_initial_money), app.initial_money_max)))
# When the game begins, a player's money is the same as initial money.
player.money = player.initial_money
In the next part of the series we’ll take care of slug settings. And now let’s make the players’ data that was set in the Settings screen visible in the other screens.
Player Data in the Other Screens
After we choose some names for the players and assign them some initial money, we want this infomation to be visible in several places throughout the application. The first place is the Players’ Stats area in the Race screen. Here we need the players’ names and their current money, which is stored in the money property.
Players’ Stats
So, let’s go to the race.kv file and find the Players’ Stats area. It should look like this:
# File name: race.kv
#:import race race
...
<RaceScreen>:
...
# Players' Stats
BoxLayout:
...
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
...
As you can see, the name and money properties on the PlayerStats instances are set to some hard-coded values. We want them to be set to the values that we set in the Settings screen. It’s easy to fix:
# File name: race.kv
#:import race race
...
<RaceScreen>:
...
# Players' Stats
BoxLayout:
...
BoldLabel:
text: "Players' Stats"
PlayerStats:
# We access the Game class through root.game.
name: root.game.player1.name
money: root.game.player1.money
PlayerStats:
name: root.game.player2.name
money: root.game.player2.money
PlayerStats:
name: root.game.player3.name
money: root.game.player3.money
PlayerStats:
name: root.game.player4.name
money: root.game.player4.money
# Buttons
...
Now let’s check out if it works. Run the app and set the names and initial money values for some of the players:
When you hit the Ready Button, the names of the Players will be set to Jenny, Player 2, Mike and Player 4. The initial money and money values of the first two players will be set to $3000 and $2000 respectively and to the default $1000 for the other two. These data should be now visible in the Race Screen, in the Players’ Stats area. Press the Ready Button. You should see this:
The Bets Screen
The next place where the player data should be visible is in the Bets screen. Here’s the code we have now:
# File name: bets.kv
#:import bets bets
...
<BetsScreen>:
...
### PLAYER BETS ###
BoxLayout:
orientation: 'vertical'
# player 1
Bet:
player_name: 'Player 1'
bet_amount: 1000
max_bet_amount: 1000
player_group: 'player1'
# player 2
Bet:
player_name: 'Player 2'
bet_amount: 1000
max_bet_amount: 1000
player_group: 'player2'
# player 3
Bet:
player_name: 'Player 3'
bet_amount: 1000
max_bet_amount: 1000
player_group: 'player3'
# player 4
Bet:
player_name: 'Player 4'
bet_amount: 1000
max_bet_amount: 1000
player_group: 'player4'
### GO BUTTON ###
...
As you can see, here the names of the players are again set to the generic names which should be used only if no other name is entered. You can change it by accessing the Player objects in the Game class, just like you did before.
You should also change the values of the max_bet_amount property on each Bet instance. A player can’t bet more than they have, so this value should be set to the player’s money amount.
So, here’s the code:
# File name: bets.kv
#:import bets bets
...
<BetsScreen>:
...
### PLAYER BETS ###
BoxLayout:
orientation: 'vertical'
# player 1
Bet:
player_name: root.game.player1.name
bet_amount: 1000
max_bet_amount: root.game.player1.money
player_group: 'player1'
# player 2
Bet:
player_name: root.game.player2.name
bet_amount: 1000
max_bet_amount: root.game.player2.money
player_group: 'player2'
# player 3
Bet:
player_name: root.game.player3.name
bet_amount: 1000
max_bet_amount: root.game.player3.money
player_group: 'player3'
# player 4
Bet:
player_name: root.game.player4.name
bet_amount: 1000
max_bet_amount: root.game.player4.money
player_group: 'player4'
### GO BUTTON ###
...
Let’s check it out again. Run the app and set some players’ names and initial money values:
Now when you hit the Ready Button, the new settings should be visible in both the Players’ Stats and in the Bets screen:
The Results Screen
Yet another place where the player data should be visible is the Results screen. Here’s the code as it is now:
# File name: results.kv
#:import results results
...
<ResultsScreen>:
...
game: root.manager.parent.parent.parent.manager
...
### PLAYER RESULTS ###
BoxLayout:
orientation: 'vertical'
# player 1
Result:
player_name: 'Player 1'
money_before: 1000
bet_amount: 300
slug_name: 'Speedster'
result_info: '- won'
gain_or_loss: 400
current_money: 1400
odds: 2.54
# player 2
Result:
player_name: 'Player 2'
money_before: 1000
bet_amount: 300
slug_name: 'Speedster'
result_info: '- lost'
gain_or_loss: 400
current_money: 600
odds: 1.59
# player 3
Result:
player_name: 'Player 3'
money_before: 1000
bet_amount: 300
slug_name: 'Trusty'
result_info: '- won'
gain_or_loss: 400
current_money: 1400
odds: 2.24
# player 4
Result:
player_name: 'Player 4'
money_before: 1000
bet_amount: 300
slug_name: 'Speedster'
result_info: '- lost'
gain_or_loss: 400
current_money: 600
odds: 1.85
### NEXT RACE BUTTON ###
...
At this moment we can change two things, player_name and current_money. Just set them to the players’ name and money properties respectively. OK, here’s the code:
# File name: results.kv
#:import results results
...
<ResultsScreen>:
...
game: root.manager.parent.parent.parent.manager
...
### PLAYER RESULTS ###
BoxLayout:
orientation: 'vertical'
# player 1
Result:
player_name: root.game.player1.name
…
current_money: root.game.player1.money
odds: 2.54
# player 2
Result:
player_name: root.game.player2.name
...
current_money: root.game.player2.money
odds: 1.59
# player 3
Result:
player_name: root.game.player3.name
...
current_money: root.game.player3.money
odds: 2.24
# player 4
Result:
player_name: root.game.player4.name
...
current_money: root.game.player4.money
odds: 1.85
### NEXT RACE BUTTON ###
...
Let’s check it out again. Run the app like before and enter some data in the Settings screen:
Hit the Ready Button to go to the Race screen. Here you should see the player data in the Players’ Stats area and in the Bets screen, just like before:
Now hit the Go Button in the Bets screen and you will see the player data in the Results screen as well:
And now let’s move on to set the slugs.