textual icon indicating copy to clipboard operation
textual copied to clipboard

Live console widget

Open hajdbo opened this issue 2 years ago • 10 comments

It would be nice to have a widget that behaves like a console. At first, just able to print and auto-scroll, and scroll bar, with auto-refresh. console.print("another brick in the wall")

Maybe reuse Rich's Live View / Console?

hajdbo avatar Sep 30 '21 18:09 hajdbo

I am looking into this too. Could we emulate this with an autoscrolling ScrollView?

Tinche avatar Oct 19 '21 23:10 Tinche

Did you get any further in this topic? I have a pytorch-lightning script that prints to console and I would like to encapsulate that printing inside a Panel (or a live console widget) so that the prints of the module won't be in top of the interface when it starts

presedo93 avatar Dec 25 '21 20:12 presedo93

When I try to using rich.console inside textual, BOOM! everything broken.

ncwhale avatar Jan 13 '22 16:01 ncwhale

@willmcgugan Please implement this feature. I have an application that is heavily dependant on Rich, Rich Handlers, Rich Logging, Rich Console and now Textual (can you tell I like your work lol), and the last piece of the puzzle is getting Rich Console to work as a widget in Textual. I've been driving myself mad for days thinking the bug was in my code until I stumbled upon this thread. Please allow for a creation of a widget in Textual that can display a Rich Console utilizing the ScrollView or something similar.

DanielATucker avatar Feb 05 '22 12:02 DanielATucker

@DanielATucker Can something like this work? I think the relevant parts are: using Console.capture() and ScrollView.animate("y", ...)

import random

from rich.text import Text

from textual import events
from textual.app import App
from textual.widgets import Header, Footer, Placeholder, ScrollView
from rich.console import Console

console = Console()

def random_content():
    what = random.randint(0, 2)
    if what == 0:
        return f"Some random number [blue] {random.random()}",
    elif what == 1:
        try:
            import requests
            x = requests.get("https://baconipsum.com/api/?type=meat-and-filler").json()
        except Exception:
            x = "   ...no, no content from internet."
        return f"Some json from the internet...", x
    elif what == 2:
        return f"Your globals...", globals()


class MyApp(App):
    console_outputs = Text("")

    async def on_load(self, event: events.Load) -> None:
        await self.bind("q", "quit", "Quit")
        await self.bind("p", "console", "Generate random content")

    async def action_console(self):
        pre_y = self.body.y
        with console.capture() as capture:
            for line in random_content():
                console.print(line)
        self.console_outputs.append(Text.from_ansi(capture.get()+"\n"))
        await self.body.update(self.console_outputs)
        self.body.y = pre_y
        self.body.animate("y", self.body.window.virtual_size.height, duration=1, easing="linear")

    async def on_mount(self, event: events.Mount) -> None:
        self.body = ScrollView(gutter=1)

        await self.view.dock(Header(), edge="top")
        await self.view.dock(Footer(), edge="bottom")

        await self.view.dock(self.body, edge="right")


MyApp.run(title="Press p to generate console content")

yamatteo avatar Feb 07 '22 22:02 yamatteo

@yamatteo, That's not exactly what I'm looking for. I have an application that uses console.input() and Rich logging to simulate a bash screen. When I try to add an input to the code you wrote, my terminal just hangs indefinitely with no response. Maybe if we could integrate https://github.com/sirfuzzalot/textual-inputs then have it displayed using your console_outputs.

If not, I understand, but could you look at my code and point me in the right direction of integration? https://github.com/DanielATucker/Brain. The main console logic is in Brain.py under def switchboard

DanielATucker avatar Feb 07 '22 23:02 DanielATucker

@DanielATucker is it a REPL you want?

from rich.text import Text

from textual import events
from textual.app import App
from textual.widgets import Header, ScrollView
from rich.console import Console
from textual_inputs import TextInput

console = Console()


class OutConsole(ScrollView):
    prev = Text("")

    async def eval(self, text_input):
        pre_y = self.y
        with console.capture() as capture:
            try:
                console.print(eval(text_input))
            except Exception:
                console.print_exception(show_locals=True)
        self.prev.append(Text.from_ansi(capture.get() + "\n"))
        await self.update(self.prev)
        self.y = pre_y
        self.animate("y", self.window.virtual_size.height, duration=1, easing="linear")


class InConsole(TextInput):
    def __init__(self, out):
        super(InConsole, self).__init__()
        self.out = out

    async def on_key(self, event: events.Key) -> None:
        if event.key == "enter":
            await self.out.eval(self.value)
            self.value = ""


class GridTest(App):
    async def on_mount(self) -> None:
        output = OutConsole()
        in_put = InConsole(out=output)

        grid = await self.view.dock_grid(edge="left", name="left")
        grid.add_column(fraction=1, name="u")
        grid.add_row(fraction=1, name="top", min_size=3)
        grid.add_row(fraction=20, name="middle")
        grid.add_row(fraction=1, name="bottom", min_size=3)
        grid.add_areas(area1="u,top", area2="u,middle", area3="u,bottom")
        grid.place(area1=Header(), area2=output, area3=in_put, )


GridTest.run(title="Something like REPL")

yamatteo avatar Feb 08 '22 10:02 yamatteo

@yamatteo yes, your fix is exactly what I needed. Thanks for your help!

DanielATucker avatar Feb 11 '22 03:02 DanielATucker

@yamatteo How would add to the console from outside of the OutConsole. For example in the on_mount I am trying to add console.print("Hello") after both the InConsole and Outconsole, but when I run the code it never shows up, anything else I try ends with a series or errors. I have been trying for days to figure out how to print to the console from other parts of my program, eg. error messages and status updates and such.

DanielATucker avatar Feb 20 '22 18:02 DanielATucker

@DanielATucker You can do as follow:

class GridTest(App):
    async def on_mount(self) -> None:
        output = OutConsole()
        in_put = InConsole(out=output)

        # ... same as before ...

        # You can use output.eval to add console output
        await output.eval(repr("Hello World!"))

        # You can change the value of output directly
        await output.update(output.prev[:-2] + "?")

Maybe I should point out that this is not the clean way to do it in textual. I'm sure the developer is cooking some proper widget that will work in harmony with the rest of the framework. This is just a dirty hack.

yamatteo avatar Feb 22 '22 09:02 yamatteo

https://github.com/Textualize/textual/wiki/Sorry-we-closed-your-issue

willmcgugan avatar Oct 25 '22 09:10 willmcgugan

Did we solve your problem?

Consider buying the Textualize developers a coffee to say thanks.

Textualize

github-actions[bot] avatar Oct 25 '22 09:10 github-actions[bot]

Request to re-open this issue!

erezsh avatar Nov 23 '22 01:11 erezsh