Book Image

Kivy Blueprints

Book Image

Kivy Blueprints

Overview of this book

Table of Contents (17 chapters)
Kivy Blueprints
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
The Python Ecosystem
Index

Making the clock tick


UI frameworks are mostly event-driven, and Kivy is no exception. The distinction from the "usual" procedural code is simple—the event-driven code needs to return to the main loop often; otherwise, it will be unable to process events from a user (such as pointer movement, clicks, or window resize), and the interface will "freeze". If you're a longtime Microsoft Windows user, you are probably familiar with programs that are unresponsive and freeze very often. It is crucial to never let this happen in our apps.

Practically, this means that we can't just code an infinite loop like this in our program:

# Don't do this
while True:
    update_time()  # some function that displays time
    sleep(1)

Technically, it might work, but the application's UI will stay in the "not responding" state until the application gets killed (forcefully stopped) by the user or an operating system. Instead of taking this faulty approach, we need to keep in mind that there is a main loop running inside Kivy, and we need to take advantage of it by utilizing events and timers.

Event-driven architecture also means that in many places, we will listen to events to respond to various conditions, be it user input, network events, or timeouts.

One of the common events that many programs listen to is App.on_start. A method with this name, if defined on the application class, will be called as soon as the app is fully initialized. Another good example of an event that we will find in many programs is on_press, which fires when the user clicks, taps, or otherwise interacts with a button.

Speaking of time and timers, we can easily schedule our code to run in the future using a built-in Clock class. It exposes the following static methods:

  • Clock.schedule_once: Runs a function once after a timeout

  • Clock.schedule_interval: Runs a function periodically

Note

Anyone with a JavaScript background will easily recognize these two functions. They are exactly like window.setTimeout and window.setInterval in JS. Indeed, the Kivy programming model is very similar to JavaScript even if the API looks completely different.

It's important to understand that all timed events that originate from Clock run as a part of Kivy's main event loop. This approach is not synonymous to threading, and scheduling a blocking function like this may prevent other events from being invoked in a timely manner, or at all.

Updating the time on the screen

To access the Label widget that holds time, we will give it a unique identifier (id). Later, we can easily look up widgets based on their id property—again, a concept which is very similar to web development.

Modify clock.kv by adding the following:

Label:
    id: time

That's it! Now we can access this Label widget from our code directly using the root.ids.time notation (root in our case is BoxLayout).

Updates to the ClockApp class include the addition of a method to display time, update_time, which looks like this:

def update_time(self, nap):
    self.root.ids.time.text = strftime('[b]%H[/b]:%M:%S')

Now let's schedule the update function to run once per second after the program starts:

def on_start(self):
    Clock.schedule_interval(self.update_time, 1)

If we run the application right now, we'll see that the time displayed is being updated every second. To paraphrase Neil Armstrong, that is one small step for mankind, but a sizable leap for a Kivy beginner.

It's worth noting how the argument to strftime combines Kivy's BBCode-like tags described earlier with the function-specific C-style format directives. For the unfamiliar, here's a quick and incomplete reference on strftime formatting essentials:

Format string (case-sensitive)

Resulting output

%S

Second as two digits, typically 00 to 59

%M

Minute as two digits, 00 to 59

%H

Hour as per 24-hour clock, 00 to 23

%I

Hour as per 12-hour clock, 01 to 12

%d

Day of the month, 01 to 31

%m

Month (numeric), 01 to 12

%B

Month (string), for example, "October"

%Y

Year as four digits, such as 2016

Tip

For the most complete and up-to-date documentation on displaying time, please refer to the official reference manual—in this case, Python standard library reference, located at https://docs.python.org/.

Binding widgets using properties

Instead of hardcoding an ID for each widget that we need to access from Python code, we can also create a property and assign it in a Kivy language file. The motivation for doing so is mostly the DRY principle and cleaner naming, at a cost of a few more lines of code.

Such a property can be defined as follows:

# In main.py
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout

class ClockLayout(BoxLayout):
    time_prop = ObjectProperty(None)

In this code fragment, we make a new root widget class for our application based on BoxLayout. It has a custom property, time_prop, which is going to reference Label we need to address from Python code.

Additionally, in the Kivy language file, clock.kv, we have to bind this property to a corresponding id. Custom properties look and behave no different from the default ones and use exactly the same syntax:

ClockLayout:
    time_prop: time

    Label:
        id: time

This code makes the Label widget accessible from the Python code without knowing the widget's ID, using the newly defined property, root.time_prop.text = "demo".

The described approach is more portable than the previously shown one and it eliminates the need to keep widget identifiers from the Kivy language file in sync with the Python code, for example, when refactoring. Otherwise, the choice between relying on properties and accessing widgets from Python via root.ids is a matter of coding style.

Later in this book, we'll explore more advanced usage of Kivy properties, facilitating nearly effortless data binding.