textual
textual copied to clipboard
Logging widget
I'm in need of a widget that can display logs, I've attempted to implement this myself multiple times. However, the widget refuses to update with the new content.
Example: class LogPanel(Widget): logs = Reactive([])
def write(self, message: str) -> None:
self.logs.append(message)
self.refresh()
def render(self) -> Panel:
return Panel("\n".join(self.logs), title="Logs")
The main app can call the write method without anything happening. According to the source refreshing happens when idle, this does not seem to be working!
This isn't difficult, rather simple yet It's not working and I am unable to explain why.
In my experience, you have to create the widget instance within the context of the app. There's some global magic going on, and if you try to create it elsewhere, it just breaks silently.
I found an imperfect workaround,
class Logview(Widget):
value = Reactive('this is log view')
def __init__(self,*args):
super().__init__(*args)
## need some placeholder to expand size of this widget
self.rows = [''] * 60
def render(self) -> RenderableType:
value = '\n'.join(reversed(self.rows))
#return ScrollView(Panel(value)) ## this does not work
return Panel(value)
def write(self,s):
self.rows.append(s)
## just hint the widget to call "render"
self.value = s
In the on_mount of the app, calls
await self.dock(ScrollView(Logview()), edge="top", name="body")
My suggestion to implement this kind of widget:
class Logview(Widget):
async def on_mount(self) -> None:
self.body = Panel()
self.view = ScrollView(self.body)
def render(self) -> RenderableType:
return self.view
def write(self,s):
## update Panel()' content
self.body.addLine(s)
## if there is no auto refresh, manual refreshing is fine.
self.body.refresh()
## or it is also fine to call refresh() by asyncio.get_event_loop().run_until_complete()
# asyncio.get_event_loop().run_until_complete(self.body.refresh)
self.view.scroll_to_bottom()
Finally I got a working copy inspired from https://github.com/Textualize/textual/issues/125
class MyApp(App):
console_outputs = Text("")
def write(self,value:str)->None:
self.console_outputs.append(Text.from_ansi(value+"\n"))
async def on_mount(self) -> None:
self.myview = ScrollView()
await self.view.dock(self.myview)
loop = asyncio.get_running_loop()
task = loop.create_task(self.action_console())
loop.call_soon_threadsafe(task)
async def action_console(self):
body = self.myview
while True:
pre_y = body.y
await body.update(self.console_outputs)
body.y = pre_y
body.animate("y", body.window.virtual_size.height, duration=1, easing="linear")
await asyncio.sleep(1)
https://github.com/Textualize/textual/wiki/Sorry-we-closed-your-issue
Did we solve your problem?
Glad we could help!