textual
textual copied to clipboard
How to take input. planning to make a CLI based messenger.
To be added by @willmcgugan need a input text feild. havent found it any example. used the default input() in python its just jamming my screen.
Check out the discussion here: https://github.com/willmcgugan/textual/discussions/105
It's pretty trivial to implement on your own. This is roughly how I do it in my app:
import string
from typing import Union
from rich.console import RenderableType
from textual import events
from textual.app import App
from textual.keys import Keys
from textual.reactive import Reactive
from textual.widget import Widget
class InputBox(Widget):
input_text: Union[Reactive[str], str] = Reactive("")
def render(self) -> RenderableType:
return f"[blue]❯[/blue] {self.input_text}"
def set_input_text(self, input_text: str) -> None:
self.input_text = input_text
class MyApp(App):
input_text: Union[Reactive[str], str] = Reactive("")
input_box: InputBox
async def on_key(self, key: events.Key) -> None:
if key.key == Keys.ControlH:
self.input_text = self.input_text[:-1]
elif key.key == Keys.Delete:
self.input_text = ""
elif key.key in string.printable:
self.input_text += key.key
def watch_input_text(self, input_text) -> None:
self.input_box.set_input_text(input_text)
async def on_mount(self, event: events.Mount) -> None:
self.input_box = InputBox()
grid = await self.view.dock_grid(edge="left", name="left")
grid.add_column(name="body")
grid.add_row(size=1, name="input")
grid.add_areas(areaInputBox="body,input")
grid.place(areaInputBox=self.input_box)
Whether this is the best practice or not I'm not 100% sure as Python isn't a strength of mine but it works.
I think the devil will be in the details. I'm not sure how well your approach scales to multiple widgets each taking focus etc.
I know @willmcgugan is working on some magic for implementing forms etc. In the meantime, some people are using https://github.com/sirfuzzalot/textual-inputs though it's been a couple of months since it's had a release so YMMV.
@M-Porter Hey there!
I used your example, but I would like to know how to render a blinking character at the end of the input box I tried using ANSI codes, but they get escaped.
Do I have to wrap the text with a rich
method or smth?
PS: I am on the css branch.
Code
import string
from typing import Union
from textual.app import App
from textual.widget import Widget
from textual.widgets import Placeholder
from textual.keys import Keys
from textual.reactive import Reactive
from rich.console import RenderableType
from textual import events
class InputBox(Widget):
has_focus: Reactive[bool] = Reactive(False)
input_text: Union[Reactive[str], str] = Reactive("")
def render(self) -> RenderableType:
return f"[blue]❯[/blue] {self.input_text}" + ('|' if self.has_focus else '')
def set_input_text(self, input_text: str) -> None:
self.input_text = input_text
async def on_focus(self, event: events.Focus) -> None:
self.has_focus = True
async def on_blur(self, event: events.Blur) -> None:
self.has_focus = False
class BasicApp(App):
"""A basic app demonstrating CSS"""
input_text: Union[Reactive[str], str] = Reactive("")
input_box: InputBox
async def on_key(self, key: events.Key) -> None:
if self.input_box.has_focus:
if key.key == Keys.ControlH:
self.input_text = self.input_text[:-1]
elif key.key == Keys.Delete:
self.input_text = ""
elif key.key in string.printable:
self.input_text += key.key
def watch_input_text(self, input_text) -> None:
self.input_box.set_input_text(input_text)
def on_load(self):
"""Bind keys here."""
def on_mount(self):
self.input_box = InputBox()
"""Build layout here."""
self.mount(
Placeholder(),
self.input_box,
Placeholder()
)
BasicApp.run(log="textual.log")
https://github.com/Textualize/textual/wiki/Sorry-we-closed-your-issue