Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live console widget #125

Closed
hajdbo opened this issue Sep 30, 2021 · 13 comments
Closed

Live console widget #125

hajdbo opened this issue Sep 30, 2021 · 13 comments

Comments

@hajdbo
Copy link

hajdbo commented Sep 30, 2021

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?

@Tinche
Copy link

Tinche commented Oct 19, 2021

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

@presedo93
Copy link

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

@ncwhale
Copy link

ncwhale commented Jan 13, 2022

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

@DanielATucker
Copy link

@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.

@yamatteo
Copy link

yamatteo commented Feb 7, 2022

@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")

@DanielATucker
Copy link

@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

@yamatteo
Copy link

yamatteo commented Feb 8, 2022

@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")

@DanielATucker
Copy link

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

@DanielATucker
Copy link

@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.

@yamatteo
Copy link

@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.

@iapyeh iapyeh mentioned this issue Oct 13, 2022
@willmcgugan
Copy link
Collaborator

@github-actions
Copy link

Did we solve your problem?

Consider buying the Textualize developers a coffee to say thanks.

Textualize

@erezsh
Copy link
Contributor

erezsh commented Nov 23, 2022

Request to re-open this issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants