[go: up one dir, main page]

Skip to content

Screens

This chapter covers Textual's screen API. We will discuss how to create screens and switch between them.

What is a screen?

Screens are containers for widgets that occupy the dimensions of your terminal. There can be many screens in a given app, but only one screen is active at a time.

Textual requires that there be at least one screen object and will create one implicitly in the App class. If you don't change the screen, any widgets you mount or compose will be added to this default screen.

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nM1Ya0/jOFx1MDAxNP3Or6i6X3YlXGKOY8fxSKtcdTAwMTXPpSywo1x1MDAwMVxyj9VcYrmJaT3Na1x1MDAxMpfHIP77XqdMXHUwMDFlbVxiZVx1MDAxN0ZEUZv4OtfX1+fc4+R+pdfr67tU9j/0+vLWXHUwMDE3oVxuMnHTXzXt1zLLVVx1MDAxMoNcdFx1MDAxN/d5Ms38oudY6zT/sL5cdTAwMWWJbFwidVx1MDAxYVxuX1rXKp+KMNfTQCWWn0TrSsso/8P8XHUwMDFliUj+niZRoDOrXHUwMDFhZE1cdTAwMDZKJ9lsLFx1MDAxOcpIxjpcdTAwMDfv/8B9r3df/NaiXHUwMDBilIiSOCi6XHUwMDE3hlp4njvfepTERaiUIVx1MDAwN3PqkbKDyrdhMC1cdTAwMDOwXkHAsrKYpn56sbN1pD45eiNcdTAwMWKyfNP5Nvy6d1WNeqXC8FjfhbM8XGJ/PM1kZc11lkzkqVxu9Fx1MDAxOOz2XFx7+VxcnkBcbqqnsmQ6XHUwMDFhxzLPXHUwMDFizySp8JW+M21cYpWtXCJcdTAwMWVcdTAwMTU+qpZbk1x1MDAwMeJamHmO5zpcdTAwMGV1XHUwMDEwqc23cECY5VLsXHUwMDEwh9G5mLaSXHUwMDEw1lx1MDAwMGL6XHUwMDA1XHUwMDE1R1x1MDAxNdVQ+JNcdTAwMTGEXHUwMDE2XHUwMDA3VVx1MDAxZlx1MDAwZvvcrs335sdMa1x1MDAwM46lXHUwMDFhjbVpxNjyXHUwMDEwcT1GZ75r+ZBF/m3P5pRcdTAwMTKMcWkxI6aDoFx1MDAwMMKX+fyNRZY+5qmfm5tatCbQnXlcdTAwMTTVkVRbY+dcIkv5vlx1MDAxYVxmvk7GfyX88CxcdTAwMWRcdTAwMGZcdTAwMGVLX1xy2Gl5q/ul4WG1y+2Ze1x1MDAxMm1cdTAwMGUv7evp9v6BPls7+8jRfrtbkWXJzfN+XHUwMDFiUawuO5HK7eNVlchpXHUwMDFhiFx1MDAxOfZt10XE5sjjXHUwMDBl4aU9VPFcdTAwMDSM8TRcZqu2xJ9UdFmpxbtA0kacdYba5CmG2thQXHUwMDE0XHUwMDEw4i1N0e7le69cdTAwMTSldidFObeAXG6GLP+HoTpcdTAwMTNxnopcZljQwlLWxlK+wErmeraDXFxcdTAwMWK9Piu7kMihOr1cdTAwMDSJ1YInsT5W31x1MDAwYjS5XHUwMDE2hWKEsIsw41x1MDAxY1HW6LUrXCJcdTAwMTXeNdawgCxEvnMrojSUXHUwMDFiafrrb/VcdTAwMTTnXHUwMDEyXCIpXFyTxjNcdTAwMWKhXHUwMDFhXHUwMDE5aPd9mJvMXHUwMDFhqNdcbkSu7Fx1MDAxMKkgXGJrXGL0IVx1MDAxMFx1MDAwMT6zwTKCk2RqpGJcdTAwMTGetMXZScZM+nqGxVx1MDAxNkZS+qRmYlx1MDAwNCDkUJXdpVx1MDAxOXn+PdGXXyfDk+PRwblzQsefkvPLd89IXHUwMDE3W8hlhHheXHUwMDFiI1x1MDAxZNuxXHUwMDEwI9h+U0pSukhJj0GlmFx1MDAxM+tHalx1MDAwMqRcdTAwMTHFXHUwMDFlcV+fml3KXHUwMDE27MfnQ0rOXHUwMDBmtlx1MDAwMrw33tldu9zDn9+jYM78nu5/vr45INuHXHUwMDA3XHUwMDE5XHL+vMNTTLbdV/CLT4PB3u7EP/Q2iH1cdTAwMTKFf+/EXHUwMDE3ozdcdTAwMTX49sS/QOCZkVZe7a/eSOBcdPXmW3+UXHUwMDEzwinUYUKX34J3o+3dVlx1MDAxM9ZZTVxisZhdaNzbXHUwMDE1XHUwMDEz0lJMsDNfREBcdTAwMWFhXHUwMDE3wp2fKu8vx2GbvGPUaO2Q82M/kzJ+SspZo/+rSfkzMjgv5WWMnZSbVZJcdTAwMTbOMfxcdTAwMTTlQCZAv+FcXF7Bu0vxO+Wc43BcdTAwMGJe7lx1MDAxMXNaOYdcdTAwMTm1XFzOjYJcdTAwMTNujjdjXHUwMDFlslxid5vkLlx06Fx1MDAxMIsz7FJcdTAwMTcvyLlcdTAwMDebXuDGf9loXHUwMDE3wf1sJuZaZHpTxYGKR2CslFxm2OhPzbhryEKO7VLCoVx1MDAxNlKOXHTyylmb6YnU7D0tXHUwMDAyckBcdTAwMWPYg1x1MDAxYYxWr5+98kNQ19b4sXMpqX1cdTAwMTlcdTAwMDfPXHUwMDA2hThUX8Tg1Vx1MDAwME7KmLdcdTAwMTBcdTAwMTW24LWh2HVcdTAwMTXfKmyHPVx1MDAxNVY7zVx1MDAxN8JcbkWut5IoUlx1MDAxYdL/MVGxnk9zkc9ccsPvsVx1MDAxNMG8XHUwMDE1plW3zVx1MDAxN4LUeGzu3KqrXsWU4qa8/rLa2nttXHUwMDExweaoYbfysFL/NzuQwmdfpOmxXHUwMDA2pJVrXHUwMDAwYFbBY+GuJta/VvJms+Xb0lVxmDRcdTAwMTYpNCVHmundP6w8/Fx1MDAwYlxiYlx1MDAxObwifQ== ExampleApp()Screen()

Creating a screen

You can create a screen by extending the Screen class which you can import from textual.screen. The screen may be styled in the same way as other widgets, with the exception that you can't modify the screen's dimensions (as these will always be the size of your terminal).

Let's look at a simple example of writing a screen class to simulate Window's blue screen of death.

screen01.py
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Static

ERROR_TEXT = """
An error has occurred. To continue:

Press Enter to return to Windows, or

Press CTRL+ALT+DEL to restart your computer. If you do this,
you will lose any unsaved information in all open applications.

Error: 0E : 016F : BFF9B3D4
"""


class BSOD(Screen):
    BINDINGS = [("escape", "app.pop_screen", "Pop screen")]

    def compose(self) -> ComposeResult:
        yield Static(" Windows ", id="title")
        yield Static(ERROR_TEXT)
        yield Static("Press any key to continue [blink]_[/]", id="any-key")


class BSODApp(App):
    CSS_PATH = "screen01.tcss"
    SCREENS = {"bsod": BSOD}
    BINDINGS = [("b", "push_screen('bsod')", "BSOD")]


if __name__ == "__main__":
    app = BSODApp()
    app.run()
screen01.tcss
BSOD {
    align: center middle;
    background: blue;
    color: white;
}

BSOD>Static {
    width: 70;
}

#title {
    content-align-horizontal: center;
    text-style: reverse;
}

#any-key {
    content-align-horizontal: center;
}

BSODApp  Windows  An error has occurred. To continue: Press Enter to return to Windows, or Press CTRL+ALT+DEL to restart your computer. If you do this, you will lose any unsaved information in all open applications. Error: 0E : 016F : BFF9B3D4 Press any key to continue _

If you run this you will see an empty screen. Hit the B key to show a blue screen of death. Hit Esc to return to the default screen.

The BSOD class above defines a screen with a key binding and compose method. These should be familiar as they work in the same way as apps.

The app class has a new SCREENS class variable. Textual uses this class variable to associate a name with screen object (the name is used to reference screens in the screen API). Also in the app is a key binding associated with the action "push_screen('bsod')". The screen class has a similar action "pop_screen" bound to the Esc key. We will cover these actions below.

Named screens

You can associate a screen with a name by defining a SCREENS class variable in your app, which should be a dict that maps names on to Screen objects. The name of the screen may be used interchangeably with screen objects in much of the screen API.

You can also install new named screens dynamically with the install_screen method. The following example installs the BSOD screen in a mount handler rather than from the SCREENS variable.

screen02.py
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Static

ERROR_TEXT = """
An error has occurred. To continue:

Press Enter to return to Windows, or

Press CTRL+ALT+DEL to restart your computer. If you do this,
you will lose any unsaved information in all open applications.

Error: 0E : 016F : BFF9B3D4
"""


class BSOD(Screen):
    BINDINGS = [("escape", "app.pop_screen", "Pop screen")]

    def compose(self) -> ComposeResult:
        yield Static(" Windows ", id="title")
        yield Static(ERROR_TEXT)
        yield Static("Press any key to continue [blink]_[/]", id="any-key")


class BSODApp(App):
    CSS_PATH = "screen02.tcss"
    BINDINGS = [("b", "push_screen('bsod')", "BSOD")]

    def on_mount(self) -> None:
        self.install_screen(BSOD(), name="bsod")


if __name__ == "__main__":
    app = BSODApp()
    app.run()
screen02.tcss
BSOD {
    align: center middle;
    background: blue;
    color: white;
}

BSOD>Static {
    width: 70;
}

#title {
    content-align-horizontal: center;
    text-style: reverse;
}

#any-key {
    content-align-horizontal: center;
}

BSODApp  Windows  An error has occurred. To continue: Press Enter to return to Windows, or Press CTRL+ALT+DEL to restart your computer. If you do this, you will lose any unsaved information in all open applications. Error: 0E : 016F : BFF9B3D4 Press any key to continue _

Although both do the same thing, we recommend SCREENS for screens that exist for the lifetime of your app.

Uninstalling screens

Screens defined in SCREENS or added with install_screen are installed screens. Textual will keep these screens in memory for the lifetime of your app.

If you have installed a screen, but you later want it to be removed and cleaned up, you can call uninstall_screen.

Screen stack

Textual apps keep a stack of screens. You can think of this screen stack as a stack of paper, where only the very top sheet is visible. If you remove the top sheet, the paper underneath becomes visible. Screens work in a similar way.

Note

You can also make parts of the top screen translucent, so that deeper screens show through. See Screen opacity.

The active screen (top of the stack) will render the screen and receive input events. The following API methods on the App class can manipulate this stack, and let you decide which screen the user can interact with.

Push screen

The push_screen method puts a screen on top of the stack and makes that screen active. You can call this method with the name of an installed screen, or a screen object.

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nOVcXG1z2khcdTAwMTL+nl/h8n3Zq1xu2nnr6Zmturpcbk5cdTAwMWN7XHUwMDEzx/Fb/HK35ZJBgNaAMFx1MDAxMrbxVv779chcdTAwMGVcdTAwMTIvXCJgMEtyVGyMRkitmaeffrpnJn+92tjYTPqdYPO3jc3gvuI3w2rXv9t87Y7fXHUwMDA23TiM2tQk0s9x1OtW0jNcdTAwMWJJ0ol/+/XXlt+9XHUwMDBlkk7Tr1x1MDAwNN5tXHUwMDE49/xmnPSqYeRVotavYVx1MDAxMrTif7vfn/xW8K9O1KomXS+7SSmohknUfbxX0FxmWkE7ienq/6HPXHUwMDFiXHUwMDFif6W/c9ZVQ79cdTAwMTW1q+npaUPOPLSjRz9F7dRUY5hgWqFcdTAwMWGcXHUwMDEwxm/pZklQpdZcdTAwMWFcdTAwMTlcdTAwMWNkLe7QJjQv9MPZWalafuj/WTqqmebJXHUwMDE3nt21XHUwMDE2NptHSb+Z2lx1MDAxNEf0KFlbnHSj6+A0rCZccmrlI8eLvtWNevVGO4jjoe9EXHUwMDFkv1x1MDAxMiZ9d4yxwVG/XU+vkVx1MDAxZLmnT4prjzFccmgtN1x1MDAwMCx7WPd9oYzHuFVWgFx1MDAxMlpxgSOGbUVNXHUwMDFhXHUwMDA2MuxcdTAwMWYsfWWmXfmV6zrZ165m53Dw/aua1tlZd09cdTAwMGasLHjKKIkwaGpcdTAwMDRhvZE4I4z2XGZyZvOtcZCOXHUwMDAyV1ZbhsCzXHUwMDE2d9PObjWFw1x1MDAxZqP92PC7naf+2ozdh5zBztZ3o1jK4yk30sFu9fz86Fg97Fx1MDAxZVx1MDAxZX5IToKqvdy6XHUwMDFmXFxrXGJ8frdcdTAwMWLdbVx1MDAwZVq+Pv2VmdbrVP1HTHGtlWVcdTAwMTJcZkeW9XQzbF9TY7vXbGbHosp1XHUwMDA2w/To19dzg19cdFVcdTAwMDR+Qo7Qmis5O/rvoHFwvV+PXHUwMDBmPnROXHK7XHUwMDEwd1dvP/TWXHUwMDFj/YJ5wjKu6FGNNiiH4c9Re1xujVx1MDAwNcGNsoh8MfjX/CvGYIng18BcdTAwMDRYo/lqwVx1MDAxZuvz8Kh8X98qb5v7crdzqe3t4VLAb1xmXHUwMDFhLkGwZYE/XHTuk0nIXHUwMDA3XHKFyDdGXHUwMDEx+Fx0XHUwMDBmMyNfto9iXHUwMDFktMzno3q3vHuO8mRfrDnvXHUwMDFiXHSe0VJoXHUwMDAzipxcdTAwMWPsXHUwMDEw8ClcdTAwMTB4TCgujaBcdTAwMWZccrBcdTAwMTDu0Vx1MDAwMquJcdxzZsZcdTAwMDGPfFxm5lx1MDAxNH6YlIrJn4fjhbZKSJhcdTAwMDPmXHUwMDE5mqJ2clx1MDAxND5cdTAwMDQpOVxmXHUwMDFk3fZbYbM/XHUwMDA0iVx1MDAxNP9k4FGlXHUwMDFiXHUwMDA07Vxy/t/2L42wWlxy2v/MXHUwMDBmWVx1MDAxY9D93Vx1MDAwNfXwN980w7rzls1mUFx1MDAxYnajJCQpNmhOolxcXHUwMDFmV8hcdTAwMTKfLtfdrY4+UdRccuth229cdTAwMWVcdTAwMTdb9Sxv1kZcdTAwMTR7M1xigUTgXHUwMDE5/L/nzVx1MDAxNyfbd35n//D+Zlx1MDAwN45bV+clXHUwMDE5Xa+5Nyu0XHUwMDFlSoZWUawg+squ4r5P4cVcdTAwMTDgkFx0ozSSWHpcdTAwMTFvXHUwMDE2MiPMgTfnjj15s2aS5FxymiyU/uAxy1x0Nupblj3pipxZbPxCiVN41VxmJjuzgKFvrsiZ81ZNdebHbp7gzVxc5eLCqDtTTFKkj0Xm8N9z5+kjP4c7i1Fsvpg7g9RcdTAwMWVcdTAwMThtKTZLhZSaXHKrUqU8y8iVXHUwMDA1Q2lAczli2HL8XHUwMDE50JOcXiR/XHUwMDE13Vx1MDAwYsxcdTAwMDT3RuY5cVxmkqhF0082bt9it6A2inXyXHUwMDE5+Vlq51x1MDAxNHf/nkPOk0Hl7PC7STlsV8N2nVx1MDAxYTMm+VZm2J0hRqQuXFzpOSuZp4FzSYNI6Vx1MDAwNVx09ixRdV3hd5zN0mOGRFx1MDAwZadMg34o6Xo64+vAqqBd/b5N0/OvIZtcdTAwMTgpXFxOXHRccpL2s1xcXHQ7Zlx1MDAxNNmkZZrzcCYkmT5mU9OPk62o1VxuXHUwMDEz6vrPUdhORrs47cs3zs1cdTAwMWKBP8ZcdTAwMWb0TPm2UT7ouCtcdTAwMGXTevbXRuYw6YfB33+8nnh2MZbda1xmxdnlXuXf52Yyul0hkZGsZsRcdTAwMDSQucz3iGy6XHUwMDFlXUtcIjPUtYpcdTAwMGLrKkdEMyp72DTLkOhRzoeugEN5iFUjdi2HxzR6hmv6p4GowfKMTFx1MDAwNzRmXGJcdTAwMDCaXHUwMDEyaVdH4qBzicZcdTAwMTONSUtcdTAwMDJcdTAwMTJgxSSGXCLPqMsnselp61x1MDAxMGFI5ViCXHUwMDEzYl1HWZM76YnEhCdcdTAwMTEoWilwWog/l8Sml1BzNpXIKGlpXFy4pZtcdTAwMDGiVuNGecagdIqBMkgk++GHZrFSIZTT1jFcdTAwMTTPSWNTXG6F0rDRo1x1MDAwM1wi05K7bFx1MDAxNmZPsPzfXHUwMDBm3vfffLx4+yG86PcvL9tcdTAwMGbNXHUwMDAztt5cdFx1MDAxNiFcdTAwMWY8XHUwMDA1oDSjIE3cnYmhtE5cdTAwMGXCQ8s1UlJPRJZcdTAwMTNsa1ImJ7BokCBeoExeXFzLI6fjdq5cIsdz8Vx0qjBjUOT3zpjZq3kx6NPLclx1MDAxZuO9m/2y6uzU7vq4t/bwtJ6SXHUwMDAyXFzh0lx1MDAwMojhQMuN8Fx1MDAxOKN2XHUwMDEyQYyyXG47qlx1MDAwMP7eOjblXGJcdTAwMDBMw1x1MDAwYpSxp1TgrFwiPWJXXHUwMDAwTi1kXHUwMDExOEFKrTnD2UXgVrVcdTAwMWQlW5c7+zbeerhcdTAwMGXOeVJcdTAwMGab61x1MDAwZU4hPFLXRlJ4XHUwMDAyru1wdaqktYdcdTAwMTTTSGExJPDgYjLQiIrlwVx1MDAxMqkzzcCZYSueYTRBsNP7+PDQvLrQlzWZhLJ/wXJCaCz/XHUwMDE4tHx9Pe26+/H+obi77iad29Ln2t71l/v34tNcdTAwMTKuW8Wbm+B3/Vx0opCfRNt7N3uldydLuO5N+fN+VZ2etu7Ptt9WoFx1MDAwYu/fXHUwMDA0/WVV4Y1GsHJZXHUwMDFjUFSelpJcdTAwMTdcdTAwMTFcdTAwMDBSVkHaWsqZXHTg0n93/mf/rnZcdTAwMTi/3T/dub1Oro72dtY7XHUwMDBipDOMR1x1MDAxOZ5kLolcdTAwMTJsZJa1RKmFJ4hcYkmju0lYu2BcIkhZ01UwQTxBbrZ74PpqzOGVm/cmmbdSqYRcYozPXHUwMDE1jbJcdTAwMTHPSsiUT3NDbKtJoXKj7dA5g4JyhrVvXHUwMDA1Zb/T8Tq9uHFcdTAwMTmnNdxfXHUwMDFl3+TksnKupL+KsnKhbVNdsbAkI6coRZe1XHUwMDAym10oTqe8Zbhi1Y9cdTAwMWLBsoOx9ijUSlxutIYh48PB2IBcdTAwMDecXHUwMDE5amTkhsYsNvFb5IncXHUwMDEzzsW04kZxd5dcdI7JuSRSsKBpRCwl72JsJom7iVx1MDAwMiZI187vqc8vy5BuJLvnWaCQs2Omssx0ibeRL8tYo5hQSlCqRfwhclXNp1xuXGJ4Wlxu5UaUXHUwMDEz/VEnPp1QUJVcdTAwMTl+ilx1MDAxZqg0UoyotHVcZkvZ9V7l3+emXHUwMDEzZVxuQztHkETtYlx1MDAwZXE/XeusKaFcdTAwMDBcdTAwMDePUkpcdEaCXCKVn027pISiSFkjXG5cdTAwMDDNgVx1MDAxOGexuapcIkJcdTAwMTFcdTAwMWVQnm+UIHfgUptcdFx1MDAxYZ9z7qFby6WsK7vz3ErHgdJHXHUwMDFhM3jWcqpF+IS6jWfG/I18UlwiQrGCuVVBjjGktpg765FQKHhcYkB3krbaMGV/UkIpRJR7jWNpWXxC2UAhnyhcdTAwMDZMXG4+R6F1eq63pnxcIjR6qJTRmlx1MDAxMiPQw3RcIoT1pFx1MDAxNVppXHUwMDFhXHUwMDE4o5larJJVLFAsY5xuT9lcbpOcTZj6pifxSEXRXHUwMDE1LKUsbv52lE9QXHUwMDExMii1y1x1MDAxYVZCJ0ipy0vOXHUwMDFhzSFPSJxJKy2iXHUwMDA2olxuXHUwMDFh1DE6sVx1MDAxZadcdTAwMGUkkyl40Kjit6nXn41OXG5cdTAwMDGVNo5BaU46mbbEu3ihK7FcdTAwMWKSaJqjMv7pTInTqFxcOjg9tlx1MDAwZqdYPWbnX2prXnwkXHTmKTBGK0U9j7lcdTAwMTn5lE8096hcdTAwMDeMJFVohFx1MDAxNHKx0sNLbHBAXHUwMDEy+EOZ2ErKj0dcdTAwMTf7b5p7R8FJr1x1MDAwM+XD6qfT3avP0bLKbujkRcbcL1h6l4XhVEmwXHUwMDFhQc++kKxxU2p3XHUwMDBmto7fn15Hl5dxq9xTbz+sO/qBMlx1MDAxZlx1MDAwNsg5PaxQo/tcdTAwMWIs86RbdUSpXHUwMDEx0zQq6zUvxLlgXHUwMDE0RC0+I4IuMDGElph4XHUwMDFlQf5cXHSiLlxctmyF0qTF5+Dmz7+fd9iX3bPem/P6Wb9S7d10+s8qRq1cdTAwMTSdgjRcdTAwMDJ1ttRcdTAwMWElz3XH41x1MDAwNZRcdTAwMDdcXKBcdTAwMTKkXHUwMDExXHUwMDAw0C6WOy59Yki4WXVjV7wrYYF5oe9cdTAwMTKzJI3CzNKIuWg+RPDiJMcyRVx1MDAwMlxi7ezbzirJznZ8dVx1MDAxYpr7sniItpvxzVFcdTAwMTfWPckxaDw3XHUwMDExQsLEas1gWJWUXHUwMDA0+Vx1MDAwNaBUaaqthF5s31lcdTAwMTHy81x1MDAxYlCmrNjnXGYkokSxYqSrOHp7v7d3wpPjg6D8caeCeLK/XHUwMDE0pFPmzjVH8ayqy1wiS/blWi7Zl1x1MDAwYi/ZXHUwMDA3LC6DOpaXgs1cdTAwMTHKplx1MDAwZvx6TnFazTyBXHUwMDAyJMFcbpRcdTAwMWTZRq09bi0pXHUwMDE5Zlx1MDAwNXk7vkwgk5LuopVA6TJcdTAwMWGYtFx1MDAxZMcqz1xiSomsUW5cdTAwMTNcdTAwMGVcdTAwMWL3dVx1MDAwMaBcZua3XHUwMDFmr2Kp69xxJ2fHTEWL6UFiI1+0oFx1MDAwNF1cdTAwMGLLSSy7moTiuZNcdTAwMDZLXVx1MDAwNWjDnO3I3L7EpzN+tqJFqVx1MDAxMFHuNYal7HKv8u9zq1x1MDAwM1moio3klKHjXHUwMDFje/mOXHUwMDBm3vdKeHi6/eXjx/pd/fL0T41FS01cdTAwMWJ+pdHrXHUwMDA266CL0Vx1MDAxNYRAWMNcdTAwMTQoPZK1XHUwMDAxqWYrgCSElJbU68uIg9zE1jRtXHUwMDAwlMhz58Gr1Vx1MDAwNrUusWjnXGaO3zW2XHUwMDBmv1x1MDAxY4ja4bugM5s2eD3tui9a9rDWTZCtTHM8bqldXHUwMDA3nfFkyfO0XHUwMDA1isL/ocWme+D4XHUwMDFjM6zTcTNcdTAwMTdcdTAwMWasUFxcaItcdTAwMWVBR9Cjulx1MDAxZPt2ZFx1MDAxYo1cdTAwMTJcdTAwMWVoXHUwMDA13M2aUFx1MDAxZbtYXHUwMDExpzBZXHUwMDAwj9PFXHLnkrJxJifJXHUwMDBiro1cdTAwMDeIRksrXHUwMDA1XHUwMDEy6Mc20qCbu2GYXHUwMDFisZXMiTzb8WaUXHUwMDE308PMxvCuXHUwMDE1t0NDS+pAt0JcdTAwMDAmLNrgdJLmMt1qwMBcdTAwMDD8rFx1MDAwMqNcdTAwMThT7lVcdTAwMWGH05xcdTAwMTKjkFM0K+RcdTAwMTTQbjGylLNrjOkxY105XHUwMDA1085329qY0E5VjXCKJVx1MDAwNWKYNcYoXHUwMDE0L7Qm2yjPpYaoLaOsg7p9nFLIXHUwMDBlt1KNJCjRXHUwMDFiSW4xXoejhEpxQlHWef9/nFwiaFx1MDAxOK2rXHUwMDE4XHUwMDE56lx1MDAwYs3HOUW6rXCIlIQq1EaQfpzOKUVWTZ9cdTAwMDJcdTAwMWOxijFlJLOKI1GZlVx1MDAxM5jOc5UwTckxxVx1MDAwNUvZxI+9Qa9cdTAwMTDP7lVcdTAwMWGHclx1MDAxMZ29erqDW/x6lFx1MDAxMO5cdTAwMDZcdTAwMDNC0Fx1MDAwZatPSjB7zM3bMLgrT5qRSV9OeKVcdTAwMWTquChwXHUwMDBm+9fXV1//XHUwMDA3mNhRliJ9 Screen 1(hidden)Screen 2 (visible)app.push_screen(screen3)Screen 3 (visible)hidden

Action

You can also push screens with the "app.push_screen" action, which requires the name of an installed screen.

Pop screen

The pop_screen method removes the top-most screen from the stack, and makes the new top screen active.

Note

The screen stack must always have at least one screen. If you attempt to remove the last screen, Textual will raise a ScreenStackError exception.

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nN1cXOtT20hcdTAwMTL/nr+C4r7sVcWz0z3vrbq6XHUwMDAyXHUwMDEyXHUwMDEyQnhsyObB3VZK2MLW4ddaMsZs5X+/XHUwMDFlhSDJQorBxnHiXHUwMDBmXHUwMDE4a+RRa+bX3b9+yH8/2djYTKbDcPO3jc3wqlx1MDAxOXSj1iiYbD71xy/DUVx1MDAxY1xy+jSE6ed4MFx1MDAxZTXTMztJMox/+/XXXjC6XGKTYTdohuwyisdBN07GrWjAmoPer1FcdTAwMTL24n/7v4dBL/zXcNBrJSOWXaRcdTAwMTG2omQw+nKtsFx1MDAxYvbCflx1MDAxMtPs/6HPXHUwMDFiXHUwMDFif6d/c9K1oqA36LfS09OBnHhazFx1MDAxZT1cdTAwMWP0U1FBXHUwMDBiLZW28vaEKH5GXHUwMDE3S8JcdTAwMTaNnpPAYTbiXHUwMDBmbcrRNNFcdTAwMDae897Fx119cmhcdTAwMGZOxyfZVc+jbvckmXZTmeJcdTAwMDHdSjZcdTAwMTYno8FF+D5qJVx1MDAxZH/pmeNV31x1MDAxYVxyxu1OP4zjwndcdTAwMDbDoFx1MDAxOSVTOqb47cGg306nyI5cXNGnXHUwMDA2cs6M0Vx1MDAxNqTiXHUwMDEy6G7V7fiXXHRcdTAwMDQz1lx1MDAxOFx1MDAwNUJcdTAwMWEhXHUwMDA1qFx1MDAxOcl2XHUwMDA2XdpcdTAwMDeS7Fx1MDAxZjx9ZbKdXHUwMDA1zYs2XHTYb2XngFxugrPz7JzJzf1Kp5i0Uphs+k5cdTAwMTi1O4nfIauZNcBdfjRcdTAwMGXTTXCgpJNaZlvkrzjca6Vg+HN2XHUwMDE1O8FoeLNam7H/kJPWXHUwMDBi+nxcdTAwMTZJeTTl9lm8grPdXHUwMDEwYKe1v/3X85NcdTAwMDP5+2CrfztXXHUwMDAxesFoNJhs3o58vvkvXHUwMDEzbTxsXHUwMDA1X1x1MDAxMFx1MDAwNVpLa43TXHUwMDEyTVx1MDAwNspu1L+gwf64282OXHKaXHUwMDE3XHUwMDE5XGLTo5+f3lx1MDAxYvp0mSroo+OO0KD03NBcdTAwMGbHU3ux39vnfHz+ctLeiyb6hfue0Fx1MDAwN/5N7IPTTFx1MDAxOSNRc1x1MDAwZVx1MDAwMoyyXHUwMDA17EuBXGalQYKeddo4vlx1MDAxOPbPgzPO1Vx1MDAxMrGPQipwlq9cdTAwMTb7vd45n2zx5NlhNFxmwz9eXHUwMDFlbb86iJeEfVx1MDAwYlxccG6Whf0kvEruXHUwMDAyvkVdXHUwMDA1fFx1MDAxMNZx5NLh3Mh/d941l1fDy5fT3taHwfjj8PiF2F1v5CMqprRBXHUwMDA0dEZ6XHUwMDBiWlx1MDAwML7lwMhcdTAwMDRJclxi1iHkrMBDcG+c4udYxj1wW1x1MDAwNryBWZhrgdL7pp/IxDtcdTAwMGJK2PvAPEPToJ+cRNepjbaFo7tBL+pOXHUwMDBikEjxT1x1MDAwMp40R2HY34D/9n/pRK1W2P9nfsfikK7vJ9TFb251o7bXls1ueF5UoyRcIlx1MDAxZXY7nFxmcmvcJElcdTAwMDKabrTXmr2jwShqR/2g+7Zaqlpt/rLMd6gzUVx1MDAxM5w9nNNnIHojxPz6XFy/8/fQZ5zF5uPps3HMSFx1MDAwMGm4pXdbVGfjJFx1MDAwM81cdTAwMWRcdTAwMWGU6EjjXHUwMDFmRZ1cdTAwMWQyrogwS2MsR9TuXHUwMDBl5XZMIzk6KVFcdTAwMDHXOlx1MDAwM/BXlyaFv4VcdTAwMDeoeipkjao/ijLGSTBKtqN+K+q3aTCzXCJfQ5K9OVx1MDAxY0Sqvs2xl5IzhVxcS8FpI5UgL1x1MDAwNLmT2sHQLyFcdTAwMDMgTqLJZKNVTtibXHUwMDEzPt9cblx1MDAxNfZb31x1MDAxNqk+UMmJ1OBcZml5nPV7pjhqilx1MDAxM0pCSaZcdTAwMDVIXHUwMDA3nITiTlmnSlJ1gzjZXHUwMDE59HpRQmt/PIj6yexcdTAwMWGni7nldbxcdTAwMTNcdTAwMDYl40F3lVx1MDAxZps1XHUwMDA2Qz9j0aZn/21k2pJ+uP3/z6d3nt2ohHI6WkJxNt+T/PtcdTAwMDNcdTAwMTi5sJWGXGYtqVx1MDAwNlx1MDAxMZdM8b/JyM9G2NpcdTAwMGKuw/1nW1x1MDAwN89f9LWNIVhvXlwinGOOTFx1MDAxNVxiNN5y68xcdTAwMTJ8XHRGLeNcdTAwMDLAOeK+ZCaEmJHsIcGo1suj5ESVkKJcYs5cdTAwMWaBrNRYMECFuIqIkYLtSkeLZLhcdTAwMDVwNb+jPdzpXHUwMDFjXGZfXHUwMDFjvdtcdTAwMWRcdTAwMWNfj9xhcjQ+XHUwMDFmivVcdTAwMDao5IJcdJ9cdFGWdFWqYrJEXG7NuOPGXHUwMDEyQqVcdTAwMDZcXFxmnsuOXHUwMDE3SVx1MDAxZaE4mTS7fHDWMenXZ8rufYyvOsPx+MNWdPomuVx1MDAxOMllXHUwMDA1jIQ3yHG7x4O+XHUwMDE1qlxu+k6AUkS65jfN76eHb9o9t3+0r/76NH32abtzKI+WivxWXHUwMDEwd8IlQ98xolx1MDAxZYJcdTAwMTONXHUwMDA0YpGuXHUwMDAwfaGQXHUwMDExKZFcdTAwMDY5cDBcXC9GMi02XHUwMDFkhGqZ6CdcdTAwMTNpSTS+4nSJXHLDl+PX19fds1P96VxcJJGYnvL50P+0bt5Y7U+v4+3jyejgdO/Vofj0x9SeLWHe8+H7ydvG5Ni+v+7Fp1x1MDAxN83wI3ZcdTAwMGaWMC9cdTAwMWab//VO3mLyprl9XHUwMDE1NT/uvlx1MDAxMc1oWfE0J+S5pTnAqrSRrktcdTAwMWKRWjiKvOzcNuAsnuKOONpcdTAwMDXV3u6Mmq33h+r6dL3DTGlcdTAwMWSzSkvNOZleNZMuXHUwMDA1QaNOcrKERJ3JXHUwMDE3zlxudj9cdTAwMTNA6npcdTAwMTbewc1ErqZxq/myrO9CWqIrhj+Ct6tBolx1MDAwMan1fZCYbXiW2Vx1MDAxMVx1MDAxNOWlgYfnwDZcdTAwMTdJXHUwMDE38jzZVb7meYLhkFxyXHUwMDA3w09xmln55e4sj9CF7z12lqckU63qVeZ4dHVkROxXXHUwMDEz71Zyft2rN57L0L1H8L8gmERUXHUwMDE0ZiOZOzFTq7CGIVx1MDAxN9pcYqPJXHUwMDE3w2Ip2yrd40RvXHUwMDAxkVgwXHUwMDAx06E1QpZ1XHUwMDExuPGCmpRvXHUwMDAyXHUwMDE5XHUwMDA0V9ZNQjUpTC7JvopEXHUwMDBmcFqcx0z01NO6jUJWxVEsXHUwMDBmjmyoRE2rmUsx3CRVXHUwMDE0U1x1MDAxNFx1MDAwMyuOdFx1MDAwMtpvZnqKt/EjZVtqQJWOl/GUTfkk/35vo2JyRYVZo0JwXHUwMDAx8iD3yFx1MDAxYtczp/U0Ko5LZi1ZXGZDsaySZtaoXGJmXHUwMDA0V4pWnlxmXHUwMDBizEZcdTAwMWLLMipoOWghfSmfLpIrRuVsimWSglx1MDAwZetcdTAwMWNcdTAwMTiCXG6WykSgjZXOc5PV2lx1MDAxNKCYXCJbte9oU4DRXHUwMDBl0PIopykqJlx1MDAxZVJOXHUwMDFlXHUwMDAzZ8QmrNTkQoThXFyan9SmVEPKv1x1MDAxYWU0LcuiXHUwMDE08l+zXHUwMDE5XFyurZS+nDm3SalPnaynSdHKMCO55lx1MDAxMiVIk+tk8d/XXHUwMDAyXHUwMDE4+X3iMd6x4YItXHUwMDE1VSaFdMFcdTAwMTkjyWhJWnXSiez+b02KU1xmpXbKcsFccqrcrtxYXHUwMDE0dPRdhfpcdTAwMDFcdTAwMDHEQiSFK5fJ8nCDMqt8P4FaN6r3NVx1MDAxZC5t6T3Vuq5XSldTXHUwMDA1Tv5cdTAwMTM06vmpgn6m8FX0cnLZfvfhQrevT49+j79rn+C31ZrIMzBcbj6QTCatr5lpXHUwMDE5UUAkTSluXHUwMDFkUSaXL8ivR2VGeTOvVptcZlhdJ59cdTAwMTbVXlx1MDAwN4Rwvk1s/ui4cbU/tn+1wsvOycdLXHUwMDExTl87PNhZe3Rq5uNcdTAwMDdB2DMobLFwSG6XkS9cIuRcbq2EQbdcdTAwMTA6l16YcUIgUTfzgHB4kdR0I57s7CB2I9lcdTAwMTZcdTAwMWZa4z+O48nFslKyVlpuga9cdTAwMDD7NuegS1xy3IAkjJyfcMX93dFe7+L5azHF8L1otTvDg9Z6Q79cdTAwMDHWMrCKmKXTzmlcdTAwMGVcdTAwMDXoXHUwMDBiKYlcZiuL3KLv6l1cYvlfyjLLLElcdTAwMTJHXHUwMDA0XHUwMDEy7CcpypxtPT9/XHUwMDFmbPHD9rv9t1x1MDAwN83rQZDE46X1xqK0uDSNqlxmYWpcbp2gfHVbajN/W3h92Wd9I1x1MDAxOFxupYVcdTAwMDRSXHUwMDFiXHUwMDAxekafiOiQWfHxi6b9WKzOWVx1MDAxZMBcdTAwMThFXHUwMDExlKPolYiLJZ9VVizincxnZZRcIq/mfLmjpF5cdTAwMTTqy2Jss5pcdTAwMTCG1P1BNZBl50Q448AlxXBKXHUwMDE5jeA4N3d2r/neNk3RKsWlZJBuTvjZklwijWpQfVx1MDAxOS7hKZvxSf79vnVTqWD26C071cjR3ec5k6vXoVx1MDAxY530XHUwMDFiz/u6od1+6/CV6thcboPSXHSanfEoXFxcdTAwMDNcdTAwMWZN+GJcdTAwMDZ89yTB0T/jUEy0XHUwMDFhJ1x1MDAxOHF0X1x1MDAwZVx1MDAxMMpavlD1Jlx1MDAxOVx1MDAwNf14XHUwMDE4jEhd7jAsucxpTdO9XHUwMDE0qJw1YsWM9DGfLfHZXHUwMDAy41bedI9r2XSPizfdc1fdXHJBtsOSf7zHk5P1O38vtV5dP0SDXHUwMDAySlx1MDAwNspYIynYXHUwMDE3zuqZxnuLjLyhptPQmVxccWXpWlxyilx0XHUwMDAwKYiekW1x7q5cdTAwMTJcblx1MDAwMpM+XHLCLYVMoPJcdTAwMWRaN3TBu1x1MDAwN4fwkCzJXCJ0gTyzclx1MDAwZtHLOelCvcvYKDa7k+8z5CONTOvo5bIscEaLJITl3JdR9Ndi5D1cdTAwMWLw61x1MDAxZpcsUFx1MDAxOFx1MDAxMCCNpZ3zXHUwMDE5XHUwMDAyJY0uyWRcdTAwMTjSgCR648jGXHUwMDE5xJJMP1x1MDAxMk+pXHUwMDA2s381yjheXHUwMDE2TVx1MDAxMZWJXHUwMDA0XHUwMDA0n2+mUHV+nlwiPlxcyNa1fHn54tnhm9Z04sK+qirdrFx1MDAwZk9cdTAwMDHBmSFoI7FD32ePRYOGqH1cdTAwMDXRKCAuY6VSXHUwMDBipVx1MDAxM75BVO5o8ypcdTAwMTNcdTAwMTWyXHUwMDFjUlx0IdWPw1Se1s37mFx1MDAxOVx1MDAwNLK0Wq+eXHUwMDAxXHTiXHUwMDFhl1FcdTAwMWOddcN1okBcdTAwMDWxXHUwMDFlxoFcdTAwMTRWXHUwMDA2NuAsoZK4/vxN4fVbv65cdTAwMTTIP/IglFTeLKBRYqYtXHUwMDFjkFx0Ylx1MDAxNVx1MDAxNoUherTYXHUwMDAzO7X2XHUwMDAyXHUwMDFkOUokj01Ow3IpsyvdWlx1MDAwZu3du/TJXHUwMDAwXHS0MVjOl/jSXHUwMDAxRaSr7iF5sFrOSYDqfVGRXHUwMDAwgdCeXHUwMDA3Ulx1MDAxNFxuXHUwMDE2yeHlclx1MDAwNF9cdTAwMTmQZNJvJaBcdTAwMTJcdTAwMWOMeegziPW59qJUXFxcbmPJISljtNEu94NcdTAwMWa3YllmNHFcdTAwMDPaWue4MkKWpPqRSFAlnP2rXHUwMDA05CUxIFGdqCFhUEtcdTAwMGbXue3ZyfXlKzWNj6ZHL+LB5OPWZft479O6MyCK1ohcdTAwMDGh4MSCjK9ZXHUwMDE0XHUwMDEzNUJcdTAwMThGXHUwMDExgnRElFxibvlfUPlOmVx1MDAxYSBcdTAwMWHmIFx1MDAxN1x1MDAxNfzotUPHXHUwMDA1t8LlbmiFmZo15Cm4OE8xrlqvwVx1MDAxN1xyrTXzN6/Ub/2a8lx1MDAxNFxuKsH/mFx1MDAwZjk3w41cdTAwMTC5eCFtXHUwMDEwXHUwMDAwzdAnckxcdTAwMWHai8VcdTAwMWXdrNVr7dtotPDP9/tcdTAwMDdcdTAwMDfhXHUwMDBlLdeGOeu7XHUwMDE2NddWlVx1MDAxYdP8U2zKoV3l7yQspJVz0pR6h7FRKOs4RS9OXHUwMDFiXHUwMDA1WphcXGfyRpZcdTAwMTORXGLCao1cdTAwMWH8XHUwMDEzYuWfJJiLpNT3wszIRFxcSCvyyEJq8lx1MDAxZqIkXHUwMDEzkmfx7Wv+4XY0d7X0/0hcdTAwMTSlXHUwMDEyyOlgXHUwMDExwlVcdTAwMDTlyc3s/jGhk4TwdrtcdTAwMTVcdTAwMDTpqHVjyrNb3LyMwsn2XT056cvbx3QxvVx1MDAxNVxu/Y3+/fnJ5/9cdTAwMDPV4pXXIn0= Screen 1(hidden)app.pop_screen()Screen 2(hidden)Screen 3(visible)Screen 2(visible)

When you pop a screen it will be removed and deleted unless it has been installed or there is another copy of the screen on the stack.

Action

You can also pop screens with the "app.pop_screen" action.

Switch screen

The switch_screen method replaces the top of the stack with a new screen.

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nN1cXG1T20hcdTAwMTL+nl9BcV/2qsLsTPe8btXVXHUwMDE1XHUwMDAxNlx1MDAwMVx1MDAxMpNccuH17opcdTAwMTK2bGsxsrFlXGZs5b9fjyCWLL9gg3DIKlXY1sjj1szTTz/dM8pfb1ZWVpPbTrj628pqeFNcclpRrVx1MDAxYlxmVt/689dht1x1MDAxN7VjaoL0c6/d71bTK5tJ0un99uuvl0H3XCJMOq2gXHUwMDFhsuuo11x1MDAwZlq9pF+L2qzavvw1SsLL3r/930pwXHUwMDE5/qvTvqwlXZb9yFpYi5J29/63wlZ4XHUwMDE5xkmPev9cdTAwMGZ9Xln5K/2bs65cdTAwMTZcdTAwMDWX7biWXp425MwzUDxbacepqWiElFxiKrsg6m3SjyVhjVrrZHCYtfhTq7fHrZPex6uT643+2ra4qrq7KKlmv1qPWq395LaV2tRr061kbb2k275cYo+iWtKkVlE4P+1b3Xa/0YzDXm/kO+1OUI2SWzqn+PBkXHUwMDEwN9IusjM39GlccpVgxmgrpOJSKG3VsN13IDUyY41RXHUwMDAypUGJQlx1MDAxNSzbaLdoXHUwMDFlyLJ/8PTIbDtcdTAwMGaqXHUwMDE3XHIyMK5l11xiXHUwMDE1XHUwMDA05/XsmsHD/UqnmLRcdTAwMTJN1n0zjFx1MDAxYc3Ez5DVzFx1MDAxYcFdvrVcdTAwMTemk1x1MDAwMCA5OpR22OB/sbNdS8Hwv+IoNoNu52G0Vnv+Q85ab+hWXHUwMDExSXk05eY5TCpR2N89QLt7uub0detcZuKPw75GoFx1MDAxN3S77cHqsOXbw7vMtH6nXHUwMDE23CNKaC2tdU5cIupsMltRfEGNcb/Vys61q1x1MDAxN1x1MDAxOVxi07Pf3i5cZn2Jalx1MDAxYfSFXHUwMDExllx1MDAxYm5cdTAwMTTOjf0/wi+wKW/OKp9cdTAwMGU+XHUwMDFjXHLuTreCne0vP1x1MDAxMvuCP1x1MDAwZX5pmDJGguZcXKAwelx1MDAwNPvogKHlXHUwMDFjXGJfTlx1MDAxYsefh/16cM65Klx1MDAxMftkmHBOw5LB/2Vf7Z00vsRna/31w62d/atccn5cdTAwMTmUXHUwMDAyfsdRcO2kKVx1MDAwYvxJeJNMQr6yelx1MDAxYfKNdMid4PNcdTAwMDM/6H95t3n2Z3vzc3x4dHP8YaNcIlx1MDAwZVx1MDAwZl478J1hloNBXHUwMDAx3FhrR4FvhGJIzi+ERUW+XHUwMDAxz8K9cYrXYVx1MDAxY/eC23HAXHUwMDFiUYS5UVxuLU1cdCxcdTAwMTflL0fxXHUwMDFl5dKAxlx1MDAwNVCeoalcdTAwMWQn+9FdmHLDyNnfg8uodTtcdTAwMDKJXHUwMDE0/mTgfrVcdTAwMWKG8Yr4b/xLM6rVwvif+Vx1MDAxOeuF9Pu+Qz36zfVW1PDOstpcbuujXpREpMOGzUk7N8ZVsiSg7rrbteJcdTAwMWS1u1EjioPW1+lWPc2ZXHUwMDAxi2eHYVxmOEVUoXF+bz4wx1f609ZBv9M53ZKbXHUwMDFmq+8/71x1MDAxZL52b7aOkXJDq1BobnKKNlxyYyiYoJNaodNKKlMwrFx1MDAxY29cdTAwMDbMOGTozblzXHUwMDBm3uxcZqCRSmZ38DeIWUpQzFi2N8PKL5Q2ReetcLI3g1x1MDAxYfnmkrw5b9VMb75cdTAwMWbmXHTuLCjgTPVnXHJcdTAwMTSXtMuJocf8efbML+DPxSD4gv5cZsYxdEpLZVx1MDAwNXJNideoQ2vrm41cdTAwMTDg6FJcdTAwMTC6YFo5XHUwMDFlrYk2hFBcdTAwMTKApsQ6nvHG0L9cdTAwMWRnXHUwMDE20GpDjsCdmaRRSVxccKKdJ0Tv1MxcdTAwMTn+PsMjlbJOLKJcInN2XHUwMDA03eRdXHUwMDE016K4QY1cdTAwMTmVfK8ybM9cdTAwMTElUlx1MDAxZq72vZWcXHTllPSJsybcWpVcdEs/XHUwMDE2QcdcdTAwMWLNQFhJk01aXmr/7+GKb0Ozwrj2uFGzM7CcUWucgeHcijTrp+R/kk2KQqexUjjpaO6tXHUwMDE5s6lcdTAwMTX0ko325WWU0Nh/bkdxUlx1MDAxY+N0MNe9ozfDYIxB6J7ybUVG6PhcdTAwMWVHiT17t5K5TPph+P5/bydePVx1MDAxNcv+XHUwMDE4Q3HW25v868JUZkFOT7BcdTAwMTUnW3CBPGO2XCJ9pUxmgUnUymjDSX7wQnXJSMtIXHUwMDBmWFx1MDAxNFx1MDAwMlx1MDAxMVxi/C9DZI5ZckJjuOFWTiQy5Vx1MDAxOGiN5H9cdTAwMDLISclZx5iMfIE6cFnDMojs6YnCnEQ2O3lcdTAwMWQhMs0taUhKXHRBccvR5i6651xmw1BrcjJCtsy50YIsNruGOmJcdTAwMTGXxPIkJo1cdTAwMTREU1x1MDAxM0js5+asabD1x9o4Ylx1MDAxN2StXHUwMDE5hUG0tnj2O29cdTAwMDHXXHUwMDE0yEDLjNlcdTAwMWXjrePT885e5Th+31x1MDAxZlxcRWu1TrR+dNF83Vx1MDAxOVx1MDAxNfi6oPWFXHUwMDExMFx1MDAwMimBzO72vihuXHUwMDE5+aZwzlx1MDAxMYNbmUsvn15cdTAwMTTXurzSoCNgSJJemdmlZVmPXHUwMDE1rrPg8nKFa6LGafjUToLBXFx6+Vx1MDAxODrXb+tcdTAwMDfBzqf1+LyyTTnCIN6CratS0VlcdTAwMGJ6zbBcXHgqYL5cXG2c4Vx1MDAwZYBcdTAwMTdcdTAwMTN+R0GXlI5cdTAwMDalwaJ2ZdStVYmVayFcdTAwMDWCUy+CzyFcdTAwMGJOqFx1MDAwMtTtYSVuXHUwMDFjbFx1MDAxY21tXZ9WKlx1MDAxZiuN3km/nCqAplx1MDAxMETiQC1cdTAwMDH9gtT4dFnprFEqP+WP0vOHs697h5+PzuqdXHUwMDAzPKq15Hn7Knnl9KzSlFx0lNdcdTAwMWFcblRu+S/twCGzaDSBTNNYQK75Kfi3UHVcIixz3caBXCI5vOxFS1x1MDAxYoZcdTAwMWb6XHUwMDFm7+5a56f6rI5JhLenfD70v53V77vbzul54/Bi1+3dbVx1MDAwZrqbUbR7Wi+h3+NrXHUwMDFi697B7mDQaZze1bu1qLr+R1x0/fbE+sGOXHUwMDFl3Fx1MDAwZWrrXCLYUdHazWkvLIdcdTAwMDW8KJDKubJYYFrJXHUwMDFi3dRcdTAwMDDo9Tl3Ml9Ee4xcdTAwMDFcdTAwMGX1p+jaXHUwMDFkh1eV3ubmn1j5uvm+rZ7CXHUwMDAwy0ssXHUwMDAxNVOCXCJcdTAwMWNcdTAwMTjKLa1Uo1x1MDAwYliCI6OMUyNpOGVcdTAwMTQ+s+ZccmDPw1x08kzpXHSppJxcdTAwMTDuaEpcdTAwMTRlTi9Q9J4lx5RxsFxiXHUwMDE0s1x1MDAxOc/K0mhcdTAwMTjZXHKgvVxmttqNXFwzLFJn+vd7kTrodFhvQJlb86yXVoZ/uX/BycXq3ELBMorVM6yb6Y/Ti9ZcdTAwMWPMNI90vvRHOkzM7ZCzma9cZod8XHRNqli6sMNcdH7kl06MeCRKzVxmSLBcXFhDI1JcdTAwMTRcdTAwMGLlOCQw37lUoFxyWIE4qdRjXHUwMDA1Q4da+1VlXHUwMDE0+dTtwVvRSikt8ieE5+dUeoBTQvlcdTAwMTRvnbPSM1vnreTrKs761X+Lylx1MDAxOcmlyVx1MDAxNVx1MDAxZVx1MDAxZVxuK4o5wf1FSFOluJFcdTAwMGZcdTAwMTdMKfWM3sVPVIKZjid/XHUwMDE0kZT19ib/+oRcdTAwMTWwnHuMhXdcdTAwMGUkLFx1MDAwNZ9f4M/WO6+TTYw0zO+70ki3i4iFXHUwMDA1MFx0TKVcdTAwMDOhTH7TVplUwpklia5IO5BzgsZcdFwi3zpcdTAwMDagUFJYXHUwMDE0xlx1MDAxYZlcdTAwMWKfXHUwMDA3Klx1MDAxMZo0XGKiWm7R2C836SetR5dNJWuCUTwgMVwiOWViXG5cdTAwMTByXHUwMDE33VOJZKTPNEpBI619reJvSiVrU1x1MDAwMeWPMSiVxiXcTS9cdTAwMTZcdTAwMTC1SePdaG4umZ3rvU4ukY5cdTAwMDaXK2JVXHUwMDFmzLRcdTAwMWRcdTAwMTUmNOSMa1x1MDAwZVx1MDAwNECjKVuXXHUwMDA1w8piXHUwMDEzpL7RXGItXHUwMDE1IV2bSUVcdTAwMDPHmfa0RmlcdTAwMDVcdTAwMDClNFx1MDAxM6SJcSgo21nyajrkPffHSlx1MDAxM6JcdTAwMTMgceaXRSjqar9vfTKlOO5cZo20X71cdTAwMWVfuv57UMpcZlD5Y1xmTlx1MDAwYnLKrJ3jZvqWO0cj75TUXHUwMDE57TxKKr/38fxz/Wj75FCfSLu373ai3dddgbSWM+VcdTAwMDR4tuZo5Gj5gSQzI6bn1ilcdTAwMDSH8nn7Z8tfXHUwMDFlks44Q1x1MDAxZbLkesSyloesnFx1MDAxZfJoSEg1gpj/mZ6dXHJcdTAwMGW7h4Ov8CFpVFx1MDAwNvWrvT+7W+9fOzolo2nXVlx1MDAwYidcdTAwMDGwXHUwMDEw8biXXCJCXHUwMDFiR6qMW8WfXHUwMDE38souj5NmJlx1MDAwNlEvUSxcdTAwMWJcdTAwMTLgXHUwMDEyq+NcdTAwMWLHXHUwMDFmZaRO63+Yg9v1rb3Eid8/XZRSbS7dpaZusJ7+iJyg8FwiXHSQXHUwMDBiXHUwMDE0t1x1MDAwME+qX9Q+bm13XHUwMDE0XHUwMDFjvW+a5ufaa5eQVvpcctbKbzJBa/Lrb2k6KvzOXHUwMDE3J8ibKFx1MDAxZlXuZfxcdCaloONcdTAwMWKshd/jrVx1MDAxNVx1MDAxN0t+XuLlcO6LY2ilWPrzXHUwMDEy+Cp3WOPzd1hLmPr4k/D79Ei32Pn3Jc6e+Ve5fOS0ZqBImVHex7WDwv5cdK2Ypmb/JIWw+efryvRnbVx1MDAxOGl30ipSS+B20sNQ1jFOXHUwMDEzXHUwMDAyXHUwMDAwXHUwMDBl1Eih68HXwVxuxV1+rW8ppeone+Oc+eDsXHUwMDEwMZJcdTAwMGZcbktJMeU3KFx1MDAxZFx0WpywXHUwMDA3XHUwMDEwaKa11ztaXHUwMDFhTjP+PVxyWnBb4mxcdTAwMTm4MrK5WlC+ZZVcdTAwMTbaot+GkD1dN7Rcblx1MDAxOKWupHGs81dKbse3fP9MmehULPujiOKsszf510VVicht8iqSXHUwMDE4aVx1MDAxMrA0vvPnoFti33aSRudT+6ZDaDpcYmrx2bspJNZcZqrNfjf88TpfkPDgQlxiIDCholxmf1ToXHUwMDFigcwh+ke+QPuc7zk8lnSDuNdcdLrkXHUwMDEwXHUwMDEztEkuxc20ybi058I/s/xcIlx1MDAwYuGztMmL7vuS4DRmkm9pT391w8v2dV7m/nBlktn0NF2SX+YperT0iSwp7Pk9evakL+TRS9zXXCItU9zHdK7AXHUwMDA3hsLzXHUwMDEyfl+LzzMoeZbGuGetos/0aFx1MDAwMaSB/Fx1MDAxNlNOXHUwMDFhSXOZ+48gsno1MFx1MDAwNc5vMjHoXHUwMDFmRCu6u0/wpXJmmdXq5/jjnOpkdqgoKFx1MDAwMUupo6LwbqSQOlx1MDAxYqJMnnCGXHUwMDE0XHUwMDE40Vx1MDAxMI+npamnqZPZu5hHXHUwMDE0XHUwMDEzd1xuaL6Ms1r6xYRxmyhqkDiRaK2jTFx1MDAwMIT6uZ/9mlx1MDAwZWV/rFx1MDAxNVE8TZ68efhcdTAwMDG/eWg/IcxccqeDYFx1MDAxZNVcdTAwMWWIPLvL1esoXHUwMDFjvJu0nzo9PEem4+mZKPT3+te3N9/+XHUwMDBmJe3LnyJ9 Screen 1(hidden)Screen 2 (visible)app.switch_screen(screen3)Screen 3 (visible)Screen 2 removed

Like pop_screen, if the screen being replaced is not installed it will be removed and deleted.

Action

You can also switch screens with the "app.switch_screen" action which accepts the name of the screen to switch to.

Screen opacity

If a screen has a background color with an alpha component, then the background color will be blended with the screen beneath it. For example, if the top-most screen has a background set to rgba(0,0,255,0.5) then anywhere in the screen not occupied with a widget will display the second screen from the top, tinted with 50% blue.

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nNVZ2VLjRlx1MDAxNH3nK1x1MDAxY+dlUlx1MDAwNZrel0mlUixhwizMXHUwMDAyXHUwMDE0Q5KplLDatsay5EhtMDPFv+dKXHUwMDE4tySvgJmFXHUwMDA3Y/dtt666zzn3XFz5y0aj0bRXXHUwMDAz03zWaJpRy4/CIPUvm5v5+IVJszCJIUSKz1kyTFvFzK61g+zZ06d9P+1cdTAwMTk7iPyW8S7CbOhHmVx1MDAxZFx1MDAwNmHitZL+09CafvZ7/nro981vg6RcdTAwMWbY1HNcdTAwMTfZMkFok/TmWiYyfVx1MDAxM9tcZlb/XHUwMDFiPjdcdTAwMWFfitdSdkHo95M4KKZcdTAwMTeBUnqK1kdcdTAwMGaTuEiVIEJcdTAwMTTmXHUwMDEyqcmMMNuDq1lcdTAwMTNAuFxyXHUwMDE5XHUwMDFiXHUwMDE3yYeaXHUwMDA3e6fPj7blaKsjRm/7r+WLbo+G7rLtMIqO7FVUJJUlcC8ultk06ZnTMLBdiOLa+Lxvpcmw041NllW+k1xm/FZor2CMo8mgXHUwMDFmd4ol3MhcYj5RwT2EhMKaXG4tKGVuO/LvXHUwMDBirDzKidKEUU0x4bW8dpNcYo5cdTAwMDHy+lx1MDAxOVx1MDAxNX8us3O/1etAenHg5mByrpRwcy7Hd8s095hiVLrluybsdG1xQMJTXHUwMDEyI12OZqY4XHUwMDAyrFx1MDAwNOFcYiniTii/5OAgKMDwsbxNcTDepnhcdTAwMThFLss88EdcdTAwMWRAZVx1MDAxMJVO99S+XHUwMDFh7n/OaHv44cxcdTAwMGbRXHQjvc7u5G4qiPPTNLlsTlwi1+N3LqPhIPBvcISF1Eoohlx1MDAxNEVu86Mw7tWTjZJWz0GvXHUwMDE4vd6cjXhrRnZcdTAwMTbctVx1MDAxNvPgzpXmikuyMti3tzvmg1x1MDAxY52/39k7XGbOXrB9/+jlm29cdHa1XGbskjOPYlx1MDAwZZCRhGvEZVx1MDAwNetKY48qXHUwMDA0jGBYYYJcdTAwMWaGdak5apNprONcdTAwMTIlJyDnuFx1MDAwZW2CqdSSUcF+cGhrJCnnWsg7QNthKIntUfj5Ro0ro/t+P4yuKkAoMFx1MDAwZlx07viZaWSt1Jj4n/jJwE9t6Edccqgx4XlkfimfWmYgl3zxkurlq2xHYSdnSzMy7SqNbFxiJWhcdTAwMTK2ycBFW5CVXHUwMDBmy6VcdTAwMDdB/e6SNOyEsVx1MDAxZlx1MDAxZK+W4UJm32z/XGZqY4llffiW21hJhDSidHV2L0bEXHUwMDFk2E1q4/dlN0ZL6S2FR6FqM62k4sLxt2C3VFDoXHUwMDE051goweRcdTAwMDMr2Vxcdlx1MDAwYk8rXHUwMDAypUpxRFx1MDAxMSup6YTrjIDKQFx1MDAwZZAoQpxxR/Ax9TlVipOy7VjOfEfpW6CQ8cj1fEFYUI1cdTAwMDT8x+w+lM0swHknjIMw7lRcdTAwMTNcdTAwMWL7tINcdTAwMTWKR0Hy1jDPclx1MDAwYnmMXHUwMDExTjCWXHUwMDFja02k5LQ0reNcdTAwMGbyrKlHJYg2lVx1MDAxNFx1MDAwYqZcdTAwMTGmU3dv4mB5Vov9Wy0rSYhcdTAwMDY0gUeUXHUwMDA03rNZWYGMc5goXHUwMDE54pzK6TOJ/MzuJv1+aGH73yZhbOvbXFzs53ZO+q7xp5RcdTAwMDXuqlx1MDAxY6urwyBfsSr+7l3D0af4MHn/cXPm7K254C6iU7h2622U/89cdTAwMTO2XHUwMDA1Jl2XqmDdpGMmKNdwXGYrK9v5q+10p3+xS+K93f1Pg97OSetcXH/nJl16YFx1MDAwZlx1MDAxNVx1MDAxMYJcdTAwMDFcdTAwMTFwzaRzykDaKEVcdTAwMWPioHO0ltfdpC2f0W6vz6QrTYG1uFR6XHUwMDFl08hkx1x1MDAwN0efgyx5d3T4qtc+XHUwMDE57tn/9vl6jFxmQUJAidGP7dFcdTAwMDWei3aMXHUwMDE1dDtSs9XR3rvAXHUwMDA3Q7/bZfzy9fut5+jw5cuT/lx1MDAxY7R3/VZ3mJrHxrtehnfCpKdcdTAwMTDCQlMmudBYVfDOoJTDXHUwMDE0qMFaXHUwMDAx9Vx1MDAxMWNcdTAwMGZcdTAwMDG8Tf04XHUwMDAzXHUwMDBmXHUwMDA26JpcdTAwMDY90WpcdTAwMWHt025cdTAwMWS6XHUwMDA1XHKtk6Lix1x1MDAwNzlXuX5/Nbd+nFxmtvpJZid+2Fx1MDAxZNGzRto595+gTbRJON9EXHUwMDFl/+XX78G+3zXl+/l5XVLMulxmXHUwMDEwiolAtESkZTKwXHUwMDE4MneSga9n6Fx1MDAxOZdcdTAwMWU4p7yJXHUwMDE0WKmyaS/qXHUwMDFlo56kXGaD+6JKXHUwMDEzVU9sfTJQ6Fx1MDAxMaRcdTAwMDCSxEB+S1x1MDAwZlx1MDAwZZytp55ASjFcZonkRdopwa1GIIrh4PSdXG7hWn19QW7KXHUwMDFm1dcvLjdVXHUwMDA3jYnWmGHwiphLrkt28tZAM3BcdTAwMWOC5s9cdTAwMWQpwlJSdT9bv9jxVZNC4HBcdTAwMTTO5VxcXG4o9nQ6K+VJXHUwMDAxglx1MDAwZolcdTAwMDMuOViCXHUwMDFm2tXPhXZcdTAwMTGsg/qOnr7Q51nahuY+hsR5h1x1MDAwMVxyk1hd20YjLsy74+fxv+/SreNR/81W/Fx1MDAxN/qWhn65slx0rj2Ru3VcZrZcdTAwMWSUy1GyeOqOmCeIgK6RsvyZ7MNcZn17lpsnTHlcdTAwMWFLaNhcdTAwMDDo4M/pXGafXHUwMDAzzPTAYyEmQHkxNFx1MDAxN072bjVccu6UcSRLT15cdTAwMWbqe5Y9XCJfh3jVyTYnsmZcdTAwMWFXYuvuzFn+qIFyUYNT/lx1MDAwN4XHQ0RcdTAwMTM4KyrhMMXS5SSUXZDbvJ/UnGJV0YRpVCxbTzFcdTAwMGauyzFcdTAwMDHTLlx1MDAxONOV5Th4JFx1MDAwNEJKsZZMwuJs2XLz9mYlRZrbdPFcdTAwMDVuXHUwMDBieMpcdTAwMDCBqyuSPbeH5oCEn9jhi1x1MDAxM3N0XHUwMDE2nqG389zWN1Mk7oHOglx1MDAwMHFcIjlUV+S+lytcdTAwMTRcdTAwMTfYg6BWgFx1MDAxYlx1MDAwNWfmXHUwMDA0u1AoXHUwMDAxVVx1MDAxMLxcZlVcdTAwMWO8XHUwMDE3zF2/QuGST3GS5LrvsVx1MDAwNFx0XHUwMDAyXaBcdTAwMDJ3+OhcbpT/kiFcdFbuXu/XXHUwMDFilaxhpTeqNjH5xvxpoihpnCZpXHUwMDE0/DSz8Sn9RvU1XHUwMDFhn0o+NzTbXHUwMDE407TpXHUwMDBmXHUwMDA2R1x1MDAxNnZrYsPgXHUwMDFjwmB8y27V5kVoLndmYyCHwcaYujlHTOGArzeu/1x1MDAwN0PH3J0ifQ== Base screen(partial visible)Top-most screenbackground: rgba(0,0,255,0.5);Hello World!

Note

Although parts of other screens may be made visible with background alpha, only the top-most is active (can respond to mouse and keyboard).

One use of background alpha is to style modal dialogs (see below).

Screens may be used to create modal dialogs, where the main interface is temporarily disabled (but still visible) while the user is entering information.

The following example pushes a screen when you hit the Q key to ask you if you really want to quit. From the quit screen you can click either Quit to exit the app immediately, or Cancel to dismiss the screen and return to the main screen.

ModalApp ModalApp I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past, I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.I must not f Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me.▅▅ And when it has gone past, I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.I must not f Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past, I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.I must not f Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear.  q Quit ^p palette

ModalApp ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ Are you sure you want to quit? ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ QuitCancel ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

modal01.py
from textual.app import App, ComposeResult
from textual.containers import Grid
from textual.screen import Screen
from textual.widgets import Button, Footer, Header, Label

TEXT = """I must not fear.
Fear is the mind-killer.
Fear is the little-death that brings total obliteration.
I will face my fear.
I will permit it to pass over me and through me.
And when it has gone past, I will turn the inner eye to see its path.
Where the fear has gone there will be nothing. Only I will remain."""


class QuitScreen(Screen):
    """Screen with a dialog to quit."""

    def compose(self) -> ComposeResult:
        yield Grid(
            Label("Are you sure you want to quit?", id="question"),
            Button("Quit", variant="error", id="quit"),
            Button("Cancel", variant="primary", id="cancel"),
            id="dialog",
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "quit":
            self.app.exit()
        else:
            self.app.pop_screen()


class ModalApp(App):
    """An app with a modal dialog."""

    CSS_PATH = "modal01.tcss"
    BINDINGS = [("q", "request_quit", "Quit")]

    def compose(self) -> ComposeResult:
        yield Header()
        yield Label(TEXT * 8)
        yield Footer()

    def action_request_quit(self) -> None:
        self.push_screen(QuitScreen())


if __name__ == "__main__":
    app = ModalApp()
    app.run()
modal01.tcss
QuitScreen {
    align: center middle;
}

#dialog {
    grid-size: 2;
    grid-gutter: 1 2;
    grid-rows: 1fr 3;
    padding: 0 1;
    width: 60;
    height: 11;
    border: thick $background 80%;
    background: $surface;
}

#question {
    column-span: 2;
    height: 1fr;
    width: 1fr;
    content-align: center middle;
}

Button {
    width: 100%;
}

Note the request_quit action in the app which pushes a new instance of QuitScreen. This makes the quit screen active. If you click Cancel, the quit screen calls pop_screen to return the default screen. This also removes and deletes the QuitScreen object.

There are two flaws with this modal screen, which we can fix in the same way.

The first flaw is that the app adds a new quit screen every time you press Q, even when the quit screen is still visible. Consequently if you press Q three times, you will have to click Cancel three times to get back to the main screen. This is because bindings defined on App are always checked, and we call push_screen for every press of Q.

The second flaw is that the modal dialog doesn't look modal. There is no indication that the main interface is still there, waiting to become active again.

We can solve both those issues by replacing our use of Screen with ModalScreen. This screen sub-class will prevent key bindings on the app from being processed. It also sets a background with a little alpha to allow the previous screen to show through.

Let's see what happens when we use ModalScreen.

ModalApp ModalApp I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past, I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.I must not f Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me.▅▅ And when it has gone past, I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.I must not f Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past, I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.I must not f Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear.  q Quit ^p palette

ModalApp ModalApp I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when i▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ Where the st not f Fear is th Fear is thAre you sure you want to quit? I will fac I will per▅▅ And when i Where the ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔st not f Fear is thQuitCancel Fear is th▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ I will fac▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ I will permit it to pass over me and through me. And when it has gone past, I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.I must not f Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear.  q Quit ^p palette

modal02.py
from textual.app import App, ComposeResult
from textual.containers import Grid
from textual.screen import ModalScreen
from textual.widgets import Button, Footer, Header, Label

TEXT = """I must not fear.
Fear is the mind-killer.
Fear is the little-death that brings total obliteration.
I will face my fear.
I will permit it to pass over me and through me.
And when it has gone past, I will turn the inner eye to see its path.
Where the fear has gone there will be nothing. Only I will remain."""


class QuitScreen(ModalScreen):
    """Screen with a dialog to quit."""

    def compose(self) -> ComposeResult:
        yield Grid(
            Label("Are you sure you want to quit?", id="question"),
            Button("Quit", variant="error", id="quit"),
            Button("Cancel", variant="primary", id="cancel"),
            id="dialog",
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "quit":
            self.app.exit()
        else:
            self.app.pop_screen()


class ModalApp(App):
    """An app with a modal dialog."""

    CSS_PATH = "modal01.tcss"
    BINDINGS = [("q", "request_quit", "Quit")]

    def compose(self) -> ComposeResult:
        yield Header()
        yield Label(TEXT * 8)
        yield Footer()

    def action_request_quit(self) -> None:
        """Action to display the quit dialog."""
        self.push_screen(QuitScreen())


if __name__ == "__main__":
    app = ModalApp()
    app.run()
modal01.tcss
QuitScreen {
    align: center middle;
}

#dialog {
    grid-size: 2;
    grid-gutter: 1 2;
    grid-rows: 1fr 3;
    padding: 0 1;
    width: 60;
    height: 11;
    border: thick $background 80%;
    background: $surface;
}

#question {
    column-span: 2;
    height: 1fr;
    width: 1fr;
    content-align: center middle;
}

Button {
    width: 100%;
}

Now when we press Q, the dialog is displayed over the main screen. The main screen is darkened to indicate to the user that it is not active, and only the dialog will respond to input.

Returning data from screens

It is a common requirement for screens to be able to return data. For instance, you may want a screen to show a dialog and have the result of that dialog processed after the screen has been popped.

To return data from a screen, call dismiss() on the screen with the data you wish to return. This will pop the screen and invoke a callback set when the screen was pushed (with push_screen).

Let's modify the previous example to use dismiss rather than an explicit pop_screen.

modal03.py
from textual.app import App, ComposeResult
from textual.containers import Grid
from textual.screen import ModalScreen
from textual.widgets import Button, Footer, Header, Label

TEXT = """I must not fear.
Fear is the mind-killer.
Fear is the little-death that brings total obliteration.
I will face my fear.
I will permit it to pass over me and through me.
And when it has gone past, I will turn the inner eye to see its path.
Where the fear has gone there will be nothing. Only I will remain."""


class QuitScreen(ModalScreen[bool]):  # (1)!
    """Screen with a dialog to quit."""

    def compose(self) -> ComposeResult:
        yield Grid(
            Label("Are you sure you want to quit?", id="question"),
            Button("Quit", variant="error", id="quit"),
            Button("Cancel", variant="primary", id="cancel"),
            id="dialog",
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "quit":
            self.dismiss(True)
        else:
            self.dismiss(False)


class ModalApp(App):
    """An app with a modal dialog."""

    CSS_PATH = "modal01.tcss"
    BINDINGS = [("q", "request_quit", "Quit")]

    def compose(self) -> ComposeResult:
        yield Header()
        yield Label(TEXT * 8)
        yield Footer()

    def action_request_quit(self) -> None:
        """Action to display the quit dialog."""

        def check_quit(quit: bool | None) -> None:
            """Called when QuitScreen is dismissed."""
            if quit:
                self.exit()

        self.push_screen(QuitScreen(), check_quit)


if __name__ == "__main__":
    app = ModalApp()
    app.run()
  1. See below for an explanation of the [bool]
modal01.tcss
QuitScreen {
    align: center middle;
}

#dialog {
    grid-size: 2;
    grid-gutter: 1 2;
    grid-rows: 1fr 3;
    padding: 0 1;
    width: 60;
    height: 11;
    border: thick $background 80%;
    background: $surface;
}

#question {
    column-span: 2;
    height: 1fr;
    width: 1fr;
    content-align: center middle;
}

Button {
    width: 100%;
}

In the on_button_pressed message handler we call dismiss with a boolean that indicates if the user has chosen to quit the app. This boolean is passed to the check_quit function we provided when QuitScreen was pushed.

Although this example behaves the same as the previous code, it is more flexible because it has removed responsibility for exiting from the modal screen to the caller. This makes it easier for the app to perform any cleanup actions prior to exiting, for example.

Returning data in this way can help keep your code manageable by making it easy to re-use your Screen classes in other contexts.

Typing screen results

You may have noticed in the previous example that we changed the base class to ModalScreen[bool]. The addition of [bool] adds typing information that tells the type checker to expect a boolean in the call to dismiss, and that any callback set in push_screen should also expect the same type. As always, typing is optional in Textual, but this may help you catch bugs.

Waiting for screens

It is also possible to wait on a screen to be dismissed, which can feel like a more natural way of expressing logic than a callback. The push_screen_wait() method will push a screen and wait for its result (the value from Screen.dismiss()).

This can only be done from a worker, so that waiting for the screen doesn't prevent your app from updating.

Let's look at an example that uses push_screen_wait to ask a question and waits for the user to reply by clicking a button.

questions01.py
from textual import on, work
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Button, Label


class QuestionScreen(Screen[bool]):
    """Screen with a parameter."""

    def __init__(self, question: str) -> None:
        self.question = question
        super().__init__()

    def compose(self) -> ComposeResult:
        yield Label(self.question)
        yield Button("Yes", id="yes", variant="success")
        yield Button("No", id="no")

    @on(Button.Pressed, "#yes")
    def handle_yes(self) -> None:
        self.dismiss(True)  # (1)!

    @on(Button.Pressed, "#no")
    def handle_no(self) -> None:
        self.dismiss(False)  # (2)!


class QuestionsApp(App):
    """Demonstrates wait_for_dismiss"""

    CSS_PATH = "questions01.tcss"

    @work  # (3)!
    async def on_mount(self) -> None:
        if await self.push_screen_wait(  # (4)!
            QuestionScreen("Do you like Textual?"),
        ):
            self.notify("Good answer!")
        else:
            self.notify(":-(", severity="error")


if __name__ == "__main__":
    app = QuestionsApp()
    app.run()
  1. Dismiss with True when pressing the Yes button.
  2. Dismiss with False when pressing the No button.
  3. The work decorator will make this method run in a worker (background task).
  4. Will return a result when the user clicks one of the buttons.
questions01.tcss
QuestionScreen {
    layout: grid;
    grid-size: 2 2;
    align: center bottom;
}

QuestionScreen > Label {
    margin: 1;
    text-align: center;
    column-span: 2;
    width: 1fr;
}

QuestionScreen Button {
    margin: 2;
    width: 1fr;
}

QuestionsApp                              Do you like Textual?                              ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ YesNo ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

The mount handler on the app is decorated with @work, which makes the code run in a worker (background task). In the mount handler we push the screen with the push_screen_wait. When the user presses one of the buttons, the screen calls dismiss() with either True or False. This value is then returned from the push_screen_wait method in the mount handler.

Modes

Some apps may benefit from having multiple screen stacks, rather than just one. Consider an app with a dashboard screen, a settings screen, and a help screen. These are independent in the sense that we don't want to prevent the user from switching between them, even if there are one or more modal screens on the screen stack. But we may still want each individual screen to have a navigation stack where we can push and pop screens.

In Textual we can manage this with modes. A mode is simply a named screen stack, which we can switch between as required. When we switch modes, the topmost screen in the new mode becomes the active visible screen.

The following diagram illustrates such an app with modes. On startup the app switches to the "dashboard" mode which makes the top of the stack visible.

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO2aW0/bSFx1MDAxNMff+ylQ9mVXKu7cL5VWK6ClXHUwMDA1UmhJuZRtVTn2JPHGsY3tJEDFd99jw8ZcdTAwMTdcYiSkXHUwMDA0Km1cdTAwMWWCPWfsOTPz+885M+HHi5WVRnpcdTAwMWWZxuuVhjlzbN9zY3vceJmVj0yceGFcdTAwMDAmkt8n4TB28pq9NI2S169eXHLsuG/SyLdcdTAwMWRjjbxkaPtJOnS90HLCwSsvNYPkr+x711x1MDAxZZg/o3DgprFVNLJqXFwvXHLjq7aMb1x1MDAwNiZIXHUwMDEzePvfcL+y8iP/LnnnevYgXGbcvHpuKLmnab10N1xmclcx5YpcdTAwMTOJNZrU8JI30FpqXFwwd8BjU1iyosaRjYZ7zeh78+JTW9uO2t48+/ChaLbj+X4rPfdzp5JcdTAwMTD6UtiSNFx1MDAwZfvmyHPTXtZ2rXzaU3E47PZcdTAwMDKTJJVnwsh2vPRcdTAwMWPKeOG7XHUwMDFkdPNXXHUwMDE0JWdwtyotjVx00pxcdTAwMTFOlGKMTMxXz1OLXHUwMDEwwlx1MDAwNYyT0lxuXHUwMDA2pObYRujDPIBjv6H8U7jWtp1+XHUwMDE3/Fx1MDAwYtyiXHUwMDBl5rbd7lx1MDAxNHXG190lQlpMM1x1MDAwMe1ThbVWk1x1MDAxYT3jdXtp1jvOLCWxkFxiXHUwMDBiTqUo/DD5dGioXHUwMDAwb2BsYshcdTAwMWGPttyci2/lXHUwMDExXHUwMDBi3OtcdTAwMTFcdTAwMGKGvl/4m1x1MDAxOd6WWCqeXHUwMDE5Rq59NelYwDhQxbGkolx1MDAxOCnfXHUwMDBi+vXX+aHTLzjJSy9fzo0n43wqnoogISijbGY8t9+hQ9U8+dTc2Wz7zY3Drffn0f5T4onRvXxyXHUwMDBi5lRrwpiQmFAlK3zChFtcdTAwMDIhSiSlSmjJXHUwMDE2wrNjt1x1MDAxMeKPgyehjGOt0Fx1MDAxMvBkQiMk+Vx1MDAxMvBUpaGo4amFXCLgzVx1MDAxY3SK3ogo01x1MDAxMu6J8665ftzdx1x1MDAwM3fzmdOJLYyBTC44V1xmRlx1MDAxZtEqnlhCXHUwMDA1ilx1MDAxOPArXHUwMDA1XHUwMDExdLHlU1x1MDAxMUdj8yh8YkJcdTAwMTiGJYUsXHUwMDAxUIZcdTAwMTFF0NxcdTAwMTJcdTAwMDClkkxcdTAwMDM0XHUwMDBiaowjiWdcdTAwMDd0dEC3u1EoRu/eXHUwMDFlOKnauaCO/5SA0vv4lDhcdTAwMDOUQ1dcdTAwMDVDWlx1MDAxMF6hkyNkMWCTaSGZYrTu1nxwtlxya7vtXz22g4gxXHUwMDEzVC6DzVIwuFx1MDAxMduZZJrCnM1cZufnL/2NcNPZ+4L39b67ZtZcdTAwMDej95+fNZxUUEtcbqmk1Fx1MDAwMsM3rcGpLSWIJCBhzLhaiM2OK1xyZv+zOTObnN3BJkJKc0g8Z2bT/b56vLcrm+GgXHUwMDFk+73xXHUwMDA2e+tvnjw3Ni3IXCJplkUykucuvFx1MDAwNiu3lIZcdTAwMDSTMYlcdTAwMTFcdTAwMTdcdTAwMTVWmURgXHUwMDE08Fx1MDAxNIxccqRcdTAwMDJ0IViZI0znl89CfzasqTlLbyNcdTAwMTWXwlaNVMVhaYG1Q85cZqp/SFtvdk9OTtaJcS76slx1MDAxZnL1aVxuqD3b6VxyY/P0SShcdTAwMTOWXHUwMDEwkFxcMlx1MDAwNmkoIZKzXG6cXHUwMDEySVx1MDAwYlZRyHUgxSOYL5aCTovymCuLaq6xlkAmYvImnOXk91xuR4Ipw1x1MDAxY5V20o+II+TmisyTc1x1MDAxNtNcdTAwMWVcdTAwMDZpy7vIk0ZVKd20XHUwMDA3nn9embmcU1x1MDAxOKmvXHLXTnrt0I7dr41Gxbzme90gx810qkynnmP7XHUwMDEzc1x1MDAxYUaF1YHmbC8w8ZZbdzuMva5cdTAwMTfY/ue7m4ZcdTAwMWWb95OVwiolg207MZk1z4pcdTAwMWakQqBrarzAXGa2o4ry2eOFQVx0+uLt75zzXvfirFx1MDAxZq7udFx1MDAwZbaeVobsPlx1MDAxNSpMsr0ggTVcdTAwMDfyadiD10SoLYIo5lx1MDAxYWFcYjNqsXOKaVwiXHUwMDE0ylx1MDAxMopqXGJRXGJyJlXKSZ6NXGKh92iek4lFRdgzfrR8/dVbfVTpTVx1MDAwZoDZgVx1MDAxOFwipdbuXHUwMDEz3sH2If9cdTAwMThcdTAwMWSGp7J1vrHzXHUwMDBmYmunb5vPXFx4jHKLYlx1MDAwNblcdTAwMWJHnGOlqvuIXFx5XHUwMDA0Q1qnRCa/R1xuf0RZsIvWRCDCqS6r6dlIXHUwMDBmKTlXOrao9Fx1MDAxMpOmXtBNli+/21p+TFx0lnO0mzt5yLnpPLsl0Vx1MDAxMuHx+MBcdTAwMWZ8XGLGXFydXlx1MDAxY1x1MDAwZtvjvYeJkNTKXHUwMDFmL1x0JcRCXHUwMDAy4p5cdTAwMDagQVx1MDAwNJRU41x1MDAxZtHEXHUwMDAyocLOKtNcdTAwMDfEpukqVLLD21x1MDAwZlShVlkrXHUwMDEwZJDimpaOwO9cdTAwMTChUOAz4z9vS3RtKPgpze1ol1x1MDAxY21/POqf4vFWa20zSnbtuNjqVWCz4zhcdTAwMWM3JpbL66s7XHUwMDE0rjQofJ5fpVx1MDAxNlP4mpN6I7Py+8hLvLZv/liuyqe3/jOUfjX4t0mdsHrpROqwXHUwMDA3k0rL2bebd9PwJEqX91x0PYt0QnPKXHUwMDE51Vx1MDAxNFx1MDAxM1ngklx1MDAxZqxAMOZIXHSuXGLnTEl2x1HIXHUwMDAyQkdcdTAwMTZWXHUwMDFjgStMZb9OXHUwMDEzSW45XGZhzJKIXHUwMDExgYXQWGB9U/lCQFx1MDAwZlx1MDAxOCqWqvulX2j6P1bIdcnlg6LywzWbpHacrnuBXHUwMDBika7q2PU/RGzNXHUwMDEwTXKVO8PMy1VkIcmFJJpjXGJZTFx1MDAxNucm2cjYUbbJgSqUXG5CKMKSiJtdN4FbuFTthZ2kXHUwMDFi4WDgpdD/j6FcdTAwMTek9Vx1MDAxYXmH1jLh9Yx9Q//w5rKtrtAoe2N1+S2uVlxuhvObyfW3l7fWXr2Dr+xzg6zihS/Kf7M1O2+iYUdRK4WZn0xcdTAwMTSg5rnXS27Rz8bIM+P121x1MDAwZbDzT1x1MDAxNlxy8rHOllx1MDAwNpPjePni8l9cdTAwMDSHyVx1MDAxMCJ9 "dashboard""help""settings"Active (visible)

If we later change the mode to "settings", the top of that mode's screen stack becomes visible.

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nN2a2VLbSFx1MDAxNIbv81x1MDAxNJTnNii9L6mammJfMmxcdTAwMDFcdTAwMDJkkkrJUttWLEtCkrFJinef01xuYy1gNrNlfGHsPrL6tPr7z1wi8fPN3FxcKz9PTOv9XFzLjD03XGb81Fx1MDAxZLXe2vEzk2ZBXHUwMDFjgYlcdTAwMTTfs3iYesWRvTxPsvfv3lxy3LRv8iR0PeOcXHUwMDA12dBccrN86Fx1MDAwN7HjxYN3QW5cdTAwMDbZX/Z921x1MDAxZJg/k3jg56lTTjJv/CCP019zmdBcZkyUZ3D2f+D73NzP4r3inVx1MDAxZriDOPKLw1x1MDAwYkPpXHUwMDFlJbw5ulx1MDAxZEeFq5xcdIlcdTAwMTlcIlx1MDAxM3uQLcNcXLnxwdhcdTAwMDF/TWmxQ62j3Z01s7iXu9m8TpLTlW/JsEPLSTtBXHUwMDE47ufnYeFSXHUwMDE2w0pKW5ancd9cdTAwMWNcdTAwMDV+3lx1MDAwMytujE/7VVx1MDAxYVx1MDAwZru9yGRZ7Tdx4npBfm5cdTAwMTeHJoNu1C1OUY6M7Y80dbDWmGtcIpggQtOJ2f6eKe4wXCK1XHUwMDA2XHUwMDEzZVxcNdxaikPYXHUwMDAzcOtcdTAwMGZUvErH2q7X74J3kV9cdTAwMWWDueu2O+Uxo8vFXHUwMDEyIVx1MDAxZKaZUIxRXHUwMDA1zpSz9EzQ7eXWTc5cdTAwMWMlsZBcYlx1MDAwYk6lKP0wxWbAXHUwMDAy7Fx1MDAxOVx1MDAxOJtcdTAwMTjs5MmGXzDxtXq9XCL/8npFwzAs/bWGlSZHVZYq27y/sX7a3/Hzle1w72Rk0sWTxW97k3XVwHPTNFx1MDAxZbUmlovLT6VHw8R3f1x1MDAwMYXh4iuGXHUwMDA1oVKWSIZB1G86XHUwMDFixl6/ZLBcdTAwMTi9eHtv8JlcdTAwMTLTwFx1MDAwNyooJVx1MDAwMle24jb0+3LpQ1x1MDAxYeJvx6j9PTzoxt3R9+HWK0dcdTAwMWbYXHUwMDE2XHUwMDAwNiGMXHUwMDEy1Fx1MDAwMJ9cdEchLlx1MDAwNVx1MDAxMoLCzrCZyO+4bYT405BPQJewUejRyH9ccmxqoqexSTVBmHBy96jMR8nR1tjfpOM12U+Xu0ujT/HglaOpXHUwMDFkiLlSaURcdTAwMTmSVNbYpGClWDFFuCRIK8ZnglNcdTAwMTFPY/MkcGKQXHUwMDE2xoqQ/1x1MDAxN520kiWbJYOWmFx1MDAxM87Znek88Vx1MDAwZdaOt7zOp7PNY7W1tDFYWF7cfkk62W10akxcdTAwMWNCXHUwMDA0x5hSpKSqwVx0VDpcdTAwMWHUXHTVhKRcdTAwMTji60xstlxya/vt36RkuFx1MDAxMU0hlHpcdTAwMTY01TQ0MVKwYCwqaf82NlPlkf7ybrhcdTAwMWLujE829lx1MDAwZvn6SftF61mMboOTXHUwMDBiu+1cdTAwMWFTXHUwMDBiqNSkXHUwMDFlOpkmXHUwMDBlXHUwMDEyXG5ziVx1MDAxONdcdTAwMTQ1/bpnWvelwez3p1x1MDAxM1xuXHUwMDFjhckz0Mn59F6LXG6GIKKgO8OZeWp1NzvrbVxmP6ydjr6vXHUwMDA0KplfeXVwOohcdTAwMTIoppVgRFx1MDAwYlx1MDAwNVA2aJVcdTAwMGXCSENcdTAwMDcmXHUwMDEwpFx1MDAwZVanlUNzhlxixlx1MDAwMlxuXHUwMDFlTORsRSjzhOn8XHUwMDBmitDHpTU34/w6VGGSqYGUI4FcdTAwMTnn8u7dUZet7+xcdTAwMWRcdTAwMWStRHtYXHUwMDFk8uHpeEzaXHUwMDBiU1jtuV5vmJpcdTAwMTdP84QpXHUwMDA3NpxcdMKVRErX2Vx1MDAxNLZcYoXuiCPIKZg+UZ7HXFw5VHONtVx1MDAwNDBcdTAwMTGTV9mkvEkjwZTZPVwiz0GjJlCG03vQWG56XHUwMDFj5fvBXHUwMDBme+GJqo2uuoMgPK/tW4EpXFypLy3fzXrt2E39L61WzbxcdTAwMTBcdTAwMDZdS24rNJ060nngueHEnMdJafVgOjeITLrhN92O06BcdTAwMWJEbnhw89SwYrM+XHRcdTAwMTRO5W5a282MtdpcdTAwMDXyXHUwMDA3ibBcdTAwMWHymlwiXHUwMDE0UHpcIoird+9cdTAwMDPnPZxcdTAwMWZcdTAwMWZ8Xo/ib5tcdTAwMWZHW6uHy8qLXrlcYjFEXFyHKi6FgFpcdTAwMDZcdTAwMTFdr2ckkkWG4IgwTrSa7Vx1MDAwNt00XHUwMDE1XG7lXGJFNWPgXHUwMDAwZqpSlLxcdTAwMWFcdTAwMTVCI4Lv0/rNqsKeXHST51x1MDAxN2Bz1ifVnsDN0Yn2JKOSQ69992Ltx+r4cIVcdTAwMDdq6cTdPmDR2s5cdTAwMGZ/8PFltXd7LyEocygniDJbc6Ayxf2SXHUwMDFlNLpSI6lcdTAwMTDngqjZWompXHSQKEdcdTAwMGLoY1x1MDAwNII0o6tyejXaI5yx+9Rjs2ovM3lcdTAwMWVE3ez59XfdzE+pQYxlc7TMf1RqXHUwMDAxjcHd85//cXfps4/mN8+S4OPSh8V2XHUwMDEwz88/TIOkMf6EXHUwMDFhZNpB0MkjibWwbVJNhERxh2PMidJQqaLqfbcrKlSyw9tcdTAwMGZToYI4QDn0b1xiQ9yrdlx1MDAxZjeIUEDJXGZtwuP1RJeGa1x1MDAxZknhwd/n/vKmiFx1MDAwZldcdTAwMGU6cZxiubpQ3pmowXb/R1Ka2j/PpvBcdTAwMDUvXHUwMDBmzszzars552Oo+teFvk7WeuqTN4K0JETf48HbzTv/XCKqlreK2lxuXG60LyhcdTAwMTSuiuD6LWRcbjlPSlvTaY5cdTAwMTWYb3i+MYOoqVx1MDAwMz08gTDKtOQgVVROM1G11I5iXHUwMDFjXHUwMDBlXHUwMDAxzdubhldFblx1MDAxZlx1MDAwZlx1MDAxMlapXHJuV3kp3/9QIZcjXHUwMDE3XHUwMDBmLH5cdTAwMWYqzyx303wxiHxIanXHLv+lYuNcdTAwMGWJo1x1MDAxMLQ3tF5cIlx1MDAwNyvrk6BcblOoQVx1MDAxMKGVo7puYmOpXHUwMDAztVxmWFx1MDAxMId2XHUwMDFlXHUwMDEyXHUwMDE4u7J2XHUwMDEz+aVP9WW4Wb5cdTAwMTRcdTAwMGZcdTAwMDZBXHUwMDBlXHUwMDE3YDdcdTAwMGWivHlEsaJcdTAwMDUrvJ5xr6hcdTAwMWXOXFy1NVx1MDAxNZrYM9ZDbflprmS4+DL5/PXttUdPx8u+roBVnu5N9a+NzsVcdTAwMDQtN0n2c9j4yT5cdTAwMDFpgX9cdTAwMTlcXMtVts5cdTAwMDIzWrzuZnXxsnG/uNI2MJiCxos3XHUwMDE3/1x1MDAwMiHc1HkifQ== "dashboard""help""settings"Active

To add modes to your app, define a MODES class variable in your App class which should be a dict that maps the name of the mode on to either a screen object, a callable that returns a screen, or the name of an installed screen. However you specify it, the values in MODES set the base screen for each mode's screen stack.

You can switch between these screens at any time by calling App.switch_mode. When you switch to a new mode, the topmost screen in the new stack becomes visible. Any calls to App.push_screen or App.pop_screen will affect only the active mode.

Let's look at an example with modes:

from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Footer, Placeholder


class DashboardScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Placeholder("Dashboard Screen")
        yield Footer()


class SettingsScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Placeholder("Settings Screen")
        yield Footer()


class HelpScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Placeholder("Help Screen")
        yield Footer()


class ModesApp(App):
    BINDINGS = [
        ("d", "switch_mode('dashboard')", "Dashboard"),  # (1)!
        ("s", "switch_mode('settings')", "Settings"),
        ("h", "switch_mode('help')", "Help"),
    ]
    MODES = {
        "dashboard": DashboardScreen,  # (2)!
        "settings": SettingsScreen,
        "help": HelpScreen,
    }

    def on_mount(self) -> None:
        self.switch_mode("dashboard")  # (3)!


if __name__ == "__main__":
    app = ModesApp()
    app.run()
  1. switch_mode is a builtin action to switch modes.
  2. Associates DashboardScreen with the name "dashboard".
  3. Switches to the dashboard mode.

ModesApp Dashboard Screen  d Dashboard  s Settings  h Help ^p palette

ModesApp Settings Screen  d Dashboard  s Settings  h Help ^p palette

Here we have defined three screens. One for a dashboard, one for settings, and one for help. We've bound keys to each of these screens, so the user can switch between the screens.

Pressing D, S, or H switches between these modes.

Screen events

Textual will send a ScreenSuspend event to screens that have become inactive due to another screen being pushed, or switching via a mode.

When a screen becomes active, Textual will send a ScreenResume event to the newly active screen.

These events can be useful if you want to disable processing for a screen that is no longer visible, for example.