Adding a loading screen to welcome page while time heavy background functions are performed. #4209
-
Hi! I would like to improve my TUI's user experience by add a loading screen to my welcome page, once the "start" button is clicked, as I have two functions I call from the backend that take a good chunk of time. I have tried multiple methods, such as async backend functions, adding a LoadingIndicator widget to my welcome screen, utilizing the ".loading" variable, etc. However, none of them function correctly, either the loading indicator does not show up at all, or it doesn't hide after my functions have completed processing. I will provide a snippet of my code below, if anyone from the community could help, it would be much appreciated: class WelcomeScreen(Screen):
def compose(self) -> ComposeResult:
yield Grid(
Label("Welcome to the text interface", id="welcome_label"),
Button("Start", id="start_button"),
id = "welcome_screen"
)
def on_button_pressed(self, event: Button.Pressed) -> None:
global error_exit_label
if event.button.id == "start_button":
# I need to add a loading screen here, that pops up after the start button is pressed and while the two functions below are running
self.loading = True
cli_installed, output1, errCode1 = be.check_cli()
identity_added, output2, errCode2 = be.check_account_balance()
self.loading = False
# Loading screen can be removed here
if (cli_installed and identity_added):
self.app.switch_screen(account_page())
elif (not cli_installed):
error_exit_label = f"CLI not found, please double check installation and ensure you are running the TUI through the environment the CLI was installed in.\n\nCommand error output: {output1}"
self.app.switch_screen(error_exit_page())
if (not identity_added):
self.app.push_screen(create_identity_page())
class TUI(App):
CSS_PATH = "style.tcss"
def compose(self) -> ComposeResult:
yield Header()
self.push_screen(WelcomeScreen()) |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Exactly how you go about it will depend on the nature of your code that gets the data, etc; but normally I'd do it using some form of worker in conjunction with returning data from screens: from time import sleep
from textual import work
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Label, LoadingIndicator
class LoadSomeStuff(Screen[str]):
DEFAULT_CSS = """
LoadSomeStuff {
LoadingIndicator {
height: 1fr;
}
}
"""
def compose(self) -> ComposeResult:
yield Label("Just wait a wee while, we'll be done soon.")
yield LoadingIndicator()
@work(thread=True)
def load_stuff(self) -> None:
sleep(5)
self.app.call_from_thread(self.dismiss, "We got the data!")
def on_mount(self) -> None:
self.load_stuff()
class LoadingScreenApp(App[None]):
def compose(self) -> ComposeResult:
yield Label("The result will go in here.")
def stuff_loaded(self, stuff: str) -> None:
self.query_one(Label).update(stuff)
def on_mount(self) -> None:
self.push_screen(LoadSomeStuff(), callback=self.stuff_loaded)
if __name__ == "__main__":
LoadingScreenApp().run() |
Beta Was this translation helpful? Give feedback.
Exactly how you go about it will depend on the nature of your code that gets the data, etc; but normally I'd do it using some form of worker in conjunction with returning data from screens: