Kivy Tutorials

Screen Transitions With ScreenManager in Kivy KV Language

This article can be read in about 37 minutes.

This article shows how to use Kivy ScreenManager class to transition between multiple screens. It also shows how to use Transition to set the animation effect when screens transition, and how to use the dynamic class rules of the kv language to write less code.

Kivy ScreenManager

Kivy’s ScreenManager consists of the ScreenManager class and the Screen class. Multiple screen transitions with animation effects are possible. Transition is used to set the animation effect.

Kivy ScreenManager Configuration Diagram.

Types of Transitions

There are eight types of transitions. The default is SlideTransition.

  1. NoTransition
    Instant screen switching without animation.
  2. SlideTransition
    Slides the screen in the [left, right, up or down] direction.
  3. CardTransition
    pop mode: The new screen slides to the previous screen.
    push mode: The new screen slides over the previous screen in the [left, right, up or down] direction.
  4. SwapTransition
    The screen swaps in a wavy transition.
  5. FadeTransition
    Screen fades in and out.
  6. WipeTransition
    The screen wipes from right to left.
  7. FallOutTransition
    Screen fades out toward the center of the screen and a new screen appears behind it.
  8. RiseInTransition
    A new screen appears, gradually growing larger from the center of the screen.

It is difficult to convey this in text, so please check the video. The code for this application is presented in the last chapter.

Example Transition in Kivy ScreenManager

How to Write ScreenManager (1) – Using the Screen Subclass

ScreenManager creates a screen with the Screen class and controls the Screen with the ScreenManager class. at least two Screen classes are required and there are a few things that need to be written.

  1. Import the ScreenManager class, the Screen class, and transitions.
  2. Define subclasses inheriting from the Screen class for the number of screens.
  3. Define subclasses inheriting from the ScreenManager class and return this as root widgets.
  4. Set the name of screen in the current property.
  5. Set the animation effect in the transition property.
  6. Set the name of the screen.
  7. Configure the widget in the Screen subclass.

Code Example

Here is an example of how to write using a subclass of the Screen class.

screenmanager1.py

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition, SlideTransition


class Screen1(Screen):
    pass

class Screen2(Screen):
    pass

class ControlScreenManager(ScreenManager):
    def switch_to_screen1(self):
        self.transition = SlideTransition(direction='left', duration=1)
        self.current = 'screen1'
        
    def switch_to_screen2(self):
        self.transition = FadeTransition(duration=1)
        self.current = 'screen2'

class ScreenManager1(App):
    def build(self):
        return ControlScreenManager()

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

screenmanager1.kv

<ControlScreenManager>:
    Screen1:
    Screen2:

<Screen1>:
    name: 'screen1'
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Screen 1\n\nNext to FadeTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 2'
            on_release: app.root.switch_to_screen2()

<Screen2>:
    name: 'screen2'
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Screen 2\n\nNext to SlideTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 1'
            on_release: app.root.switch_to_screen1()

Python Code Explanation

  1. Import ScreenManager class, Screen class, and transitions.
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition, SlideTransition

Write a list of all transitions to be used

  1. Define subclasses inheriting from the Screen class for the number of screens.
class Screen1(Screen):
    pass

class Screen2(Screen):
    pass

The widget configuration and style are described in the kv file, and event processing is described in the py file. Even if no processing is performed, an empty class definition is required as shown above.

  1. Define a subclass inheriting from the ScreenManager class and return it as the root widget.
class ControlScreenManager(ScreenManager):
    pass

class ScreenManager1(App):
    def build(self):
        return ControlScreenManager()

Define a subclass that inherits from the ScreenManager class. Then return it in the build().

  1. Set the current property to the name of Screen.
def switch_to_screen1(self):
    self.current = 'screen1'
        
def switch_to_screen2(self):
    self.current = 'screen2'

The current property refers to the currently displayed screen, so specify a unique name for the Screen. In this example, the switch_to_screen() method is called from the button defined in the kv file, so the name corresponding to each screen is specified in the method.

<Screen1>:
    name: 'screen1' #The value should be the same as name.
<Screen2>:
    name: 'screen2' #The value should be the same as name.

Must be the same as the value of the name property in the kv file.

  1. Set the animation effect with transition property.
    def switch_to_screen1(self):
        self.transition = SlideTransition(direction='left', duration=1)
        
    def switch_to_screen2(self):
        self.transition = FadeTransition(duration=1)

Specify the animation effect as an argument to the Transition class. The transition property is also specified here. duration property specifies the duration of the interval between screen transitions. The default is 400 ms (0.4 seconds).

KV code Explanation

  1. Set the screen name.
<Screen1>:
    name: 'screen1'
<Screen2>:
    name: 'screen2'

Set the name property to a unique name for the screen; it must be the same value as the current property defined in the py file.

  1. Configure widgets in the Screen subclass.
<Screen1>:
    name: 'screen1'
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Screen 1\n\nNext to FadeTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 2'
            on_release: app.root.switch_to_screen2()

Configure the widgets of the Screen class as class rules. In this example, the Label and Button widgets are defined, and the switch_to_screen() corresponding to the next screen is called on the on_release(). app.root keyword, see the following article.

<Screen1>:
    on_release: app.root.switch_to_screen2()

When transitioning from screen1 to screen2, switch_to_screen2() is called.

<Screen2>:
    on_release: app.root.switch_to_screen1()

In screen2 on the last page, we want to return to screen1, so we call switch_to_screen1().

  1. Overriding Rules
<ControlScreenManager>:
    Screen1:
    Screen2:

We override the rules of the Screen class in the root widget. If we do not do this, we will get a black screen and nothing will be displayed.

How to Write ScreenManager (2) – Using Dynamic Class Rules

Subclasses that inherit from the Screen class do not need to be written in Python code; if you want to describe the process in Python code, you only need to write the necessary Screen subclass, so you do not need to write unnecessary code.

Code Example

screenmanager2.py

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager


class ControlScreenManager(ScreenManager):
    pass
    
class ScreenManager2(App):
    def build(self):
        return ControlScreenManager()

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

screenmanager2.kv

#:import FallOutTransition kivy.uix.screenmanager.FallOutTransition
#:import RiseInTransition kivy.uix.screenmanager.RiseInTransition

<ControlScreenManager>:
    Screen1:
    Screen2:

<Screen1@Screen>:
    name: 'screen1'
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Screen 1\n\nNext to FallOutTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 2'
            on_release:
                app.root.transition = FallOutTransition(duration=1)
                app.root.current = 'screen2'


<Screen2@Screen>:
    name: 'screen2'
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Screen 2\n\nNext to RiseInTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 1'
            on_release:
                app.root.transition = RiseInTransition(duration=1)
                app.root.current = 'screen1'

Python Code Explanation

The subclass inheriting from the Screen class is not necessary, so if you want to describe the process in Python code, you only need to write the subclass, so you don’t need to write unnecessary code.

KV code Explanation

The trasition and current properties can also be written in the kv language.

#:import FallOutTransition kivy.uix.screenmanager.
#:import RiseInTransition kivy.uix.screenmanager.RiseInTransition

The import statement has the following syntax.

#:import < Alias > < Module >

<ControlScreenManager>:
    Screen1:
    Screen2:

Override Screen class rules.

name: 'screen1'.

The name property defines the name of the screen.

on_release:
    app.root.transition = FallOutTransition(duration=1)
    app.root.current = 'screen2'

Set transition and current in the on_release() or on_press event. current and transition cannot be described in the Screen class, so they must be described in the ScreenManager class via app.root.

Passing Data Between Screens

To reference data between screens using ScreenManager, use ids to obtain the data. See the following article for details on ids.

on_enter Method

The on_enter() is a method called just before the ScreenManager screen is displayed. By overriding this method, specific processing can be performed when the screen is displayed; it is useful to use this method to retrieve ids.

get_screen Method

The get_screen() is a method used to retrieve a screen in the ScreenManager. This method is useful for referencing ids data between screens or for performing specific screen operations.

on_kv_post Method

This method is called when a kv file is loaded, and may be used to reference ids in places where ids have not yet been generated.

Code Example

This sample code shows how to pass data between two screens by referencing the ids dictionary: Screen1 changes the Label.text of Screen2, and Screen2 changes the Label.text of Screen1.

screenmanager3.py

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen

class Screen1(Screen):
    def on_kv_post(self, base_widget):
        print(f"Screen1 - ids : {self.ids}")
        if self.ids:
            print(self.ids.label1.text)
        else:
            print("label1 not found in Screen1")

class Screen2(Screen):
    def on_enter(self):
        print(self.ids.label2.text)
        print(f"Screen2 - ids : {self.ids}")

class ControlScreenManager(ScreenManager):
    def get_label1(self):
        screen1 = self.get_screen('screen1')
        print(f"ControlScreenManager - ids : {screen1.ids}")
        if 'label1' in screen1.ids:
            screen1.ids.label1.text = "Update label1 from root widget"
        else:
            print("label1 not found in Screen1")

    def get_label2(self):
        screen2 = self.get_screen('screen2')
        print(f"ControlScreenManager - ids: {screen2.ids}")
        if 'label2' in screen2.ids:
            screen2.ids.label2.text = "Update labe2 from root widget"
        else:
            print("label2 not found in Screen2")


class ScreenManager3(App):
    def build(self):
        return ControlScreenManager()

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

screenmanager3.kv

#:import FallOutTransition kivy.uix.screenmanager.FallOutTransition
#:import RiseInTransition kivy.uix.screenmanager.RiseInTransition

<ControlScreenManager>:
    Screen1:
    Screen2:

<Screen1@Screen>:
    name: 'screen1'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label1
            text: 'Screen 1\n\nNext to FallOutTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 2'
            on_release:
                app.root.transition = FallOutTransition(duration=1)
                app.root.current = 'screen2'
                app.root.get_label2()

<Screen2@Screen>:
    name: 'screen2'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label2
            text: 'Screen 2\n\nNext to RiseInTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 1'
            on_release:
                app.root.transition = RiseInTransition(duration=1)
                app.root.current = 'screen1'
                app.root.get_label1()

Python Code Explanation

This code consists of the following.

<ControlScreenManager>
+--- Screen1
+---+--- Label
+---+---+--- id: label1
+---+--- Button
+--- Screen2
+---+--- Label
+---+---+--- id: label2
+---+--- Button
class Screen1(Screen):
    def on_kv_post(self, base_widget):
        print(f"Screen1 - ids : {self.ids}")
        if self.ids:
            print(self.ids.label1.text)
        else:
            print("label1 not found in Screen1")

Screen1 is displayed when the Kivy application is launched. Normally, we would override the on_enter() method and write the process in it, but in the case of the first screen, the ids dictionary has not yet been generated, so the ids cannot be obtained. Therefore, the on_kv_post() method is used to ensure that ids are referenced when the kv file is finished loading and the ids dictionary is generated.

def on_kv_post(self, base_widget):

Define on_kv_post() to get the id of the first screen. The argument base_widget is passed the root widget ControlScreenManager().

if self.ids:
    print(self.ids.label1.text)
else:
    print("label1 not found in Screen1")

If the ids dictionary exists, processing is performed. If this is not written, an error occurs.
if the ids dictionary could not be obtained, the process is branched with an else statement.

class Screen2(Screen):
    def on_enter(self):
        print(self.ids.label2.text)
        print(f"Screen2 - ids : {self.ids}")

The second screen to be displayed is Screen2. Here, the on_enter() method is defined to refer to the ids dictionary; since the second screen already has an ids dictionary, the on_enter() method can be used.

Screen1 and Screen2 cannot get ids from each other because they are separate widget trees. This is due to the scope limitation of the id. The ControlScreenManager is the root widget and inherits from the ScreenManager, so it can control each Screen.

    def get_label1(self):
        screen1 = self.get_screen('screen1')
        print(f"ControlScreenManager - ids : {screen1.ids}")
        if 'label1' in screen1.ids:
            label1 = screen1.ids.label1
            label1.text = "Update label1 from root widget"
        else:
            print("label1 not found in Screen1")

Normally, when referring to an id in a different scope, you would go back through the ids namespace, but in the case of ScreenManager, each screen has a name, so you can use that name to refer to an id.

  1. Get screen name : get_screen(screen_name)
  2. Referencing an id : screen_name.ids.id_name.property_name
screen1 = self.get_screen('screen1')

The screen name is specified as the argument of get_screen() and assigned to a variable.

if 'label1' in screen1.ids:
    screen1.ids.label1.text = "Update label1 from root widget"

Use Python’s in operator to check for the presence of ‘label1‘ in the ids and execute the process if this is present. Here we are changing the text of label1.

KV code Explanation

<Screen1@Screen>:
    name: 'screen1'
    Button:
        on_release:
            app.root.get_label2()

Screen1 calls get_label2() to change the Label.text of Screen2.

<Screen2@Screen>:
    name: 'screen2'
    Button:
        on_release:
            app.root.get_label1()

Screen2 calls get_label1() to change the Label.text of Screen1.

ScreenManage Trasition Sample

An example code for the video at the beginning of this article is shown below. This code uses the Dynamic class rules; the Python code does not define a Screen class here because it does no processing. This way the code is easier to read and the amount of code seems to be reduced a bit.

Setting the background color of the animation effect with clearcolor()

The clearcolor() method in ScreenManage can be used to set the background color during trasition effects, but it seems to have an effect only on some of the following trasitions.

  • FadeTransition
  • WipeTransition
  • FallOutTransition
  • RiseInTransition

screenmanager_ex.py

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager

class ControlScreenManager(ScreenManager):
    pass

class ScreenManager_ex(App):
    def build(self):
        return ControlScreenManager()

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

screenmanager_ex.kv

#:import NoTransition kivy.uix.screenmanager.NoTransition
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
#:import SwapTransition kivy.uix.screenmanager.SwapTransition
#:import CardTransition kivy.uix.screenmanager.CardTransition
#:import WipeTransition kivy.uix.screenmanager.WipeTransition
#:import FallOutTransition kivy.uix.screenmanager.FallOutTransition
#:import RiseInTransition kivy.uix.screenmanager.RiseInTransition

<ControlScreenManager>:
    Screen1:
    Screen2:
    Screen3:
    Screen4:
    Screen5:
    Screen6:
    Screen7:
    Screen8:

<Screen1@Screen>:
    name: 'screen1'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label_id1
            text: 'Screen 1\n\nNext to SlideTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 2'
            on_release:
                app.root.transition = SlideTransition(direction='left', duration=1)
                app.root.current = 'screen2'

<Screen2@Screen>:
    name: 'screen2'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label_id2
            text: 'Screen 2\n\nNext to FadeTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 3'
            on_release:
                app.root.transition = FadeTransition(duration=1,clearcolor=(255/255, 99/255, 71/255, 0.3))
                app.root.current = 'screen3'

<Screen3@Screen>:
    name: 'screen3'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label_id3
            text: 'Screen 3\n\nNext to SwapTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 4'
            on_release:
                app.root.transition = SwapTransition(duration=1)
                app.root.current = 'screen4'

<Screen4@Screen>:
    name: 'screen4'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label_id4
            text: 'Screen 4\n\nNext to CardTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 5'
            on_release:
                app.root.transition = CardTransition(duration=1)
                app.root.current = 'screen5'


<Screen5@Screen>:
    name: 'screen5'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label_id5
            text: 'Screen 5\n\nNext to WipeTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 6'
            on_release:
                app.root.transition = WipeTransition(duration=1,clearcolor=(255/255, 99/255, 71/255, 0.3))
                app.root.current = 'screen6'

<Screen6@Screen>:
    name: 'screen6'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label_id6
            text: 'Screen 6\n\nNext to FallOutTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 7'
            on_release:
                app.root.transition = FallOutTransition(duration=1,clearcolor=(255/255, 99/255, 71/255, 0.3))
                app.root.current = 'screen7'

<Screen7@Screen>:
    name: 'screen7'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label_id7
            text: 'Screen 7\n\nNext to RiseInTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 8'
            on_release:
                app.root.transition = RiseInTransition(duration=1,clearcolor=(255/255, 99/255, 71/255, 0.3))
                app.root.current = 'screen8'

<Screen8@Screen>:
    name: 'screen8'
    BoxLayout:
        orientation: 'vertical'
        Label:
            id: label_id8
            text: 'Screen 8\n\nNext to NoTransition'
            halign: 'center'
            font_size: 30
        Button:
            text: 'Go to Screen 1'
            on_release:
                app.root.transition = NoTransition(duration=1)
                app.root.current = 'screen1'

Comment

Copied title and URL