I'm trying to get a bit into prompt-toolkit for building CLI apps. Maybe it's a trivial question, but I could not find any solution in the docs or in other posts.
I want to build a full-screen app with prompt-toolkit, that monitors and displays periodically changing information. As a demonstrator, I just wanted to display the current date and time, and have it update every second.
However, I could not yet find any way for a prompt-toolkit app to update itself without user input. I assume, I need to add some callback somewhere, but the documentation is not very clear on that and I have not found a good example yet.
Update 1: After some more trial&error I found that the following code produces the expected result, although I'm still not a 100% sure if this is the best way of doing it.
With app.create_background_task() one can add coroutines that to the event loop. In this example I update the text in the upper part of the window. However, this does not do anything, unless the user manually refreshes the app (I think with app.invalidate()) OR by providing the refresh argument in Application.
I think this solution is sufficient unless too much other stuff is happening so that the refresh-rate cannot be maintained..
import datetime
import asyncio
from prompt_toolkit import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.containers import Window, HSplit
from prompt_toolkit.layout.controls import FormattedTextControl
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit import widgets
static_text = FormattedTextControl(text=f"Time: {datetime.datetime.now().strftime('%H:%M:%S')}")
static_window = Window(content=static_text, height=2)
telemetry_window = Window(content=FormattedTextControl(text="Some fixed text"))
root_container = HSplit([
widgets.Frame(body=static_window),
widgets.Frame(body=telemetry_window)
])
layout = Layout(root_container)
kb = KeyBindings()
@kb.add('c-q')
@kb.add('c-c')
def exit_(event):
event.app.exit()
app = Application(layout=layout, full_screen=True, key_bindings=kb, refresh_interval=0.5)
async def refresh():
while True:
static_text.text = f"Time: {datetime.datetime.now().strftime('%H:%M:%S')}"
await asyncio.sleep(0.1)
app.create_background_task(refresh())
app.run()
Update 2:
It's also possible to update text controls through running threads. Every time the UI is invalidated, it renders itself with the new values set to text. So, a coroutine is not strictly necessary.
Documentation for Application shows:
and it also shows:
It automatically runs
refreshwhen I assign it toon_invalidate=or to
before_render=