textual icon indicating copy to clipboard operation
textual copied to clipboard

How to take input. planning to make a CLI based messenger.

Open b33lz3bubTH opened this issue 3 years ago • 4 comments

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.

b33lz3bubTH avatar Nov 04 '21 14:11 b33lz3bubTH

Check out the discussion here: https://github.com/willmcgugan/textual/discussions/105

tusharsadhwani avatar Nov 04 '21 17:11 tusharsadhwani

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.

M-Porter avatar Nov 10 '21 18:11 M-Porter

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.

lllama avatar Nov 10 '21 19:11 lllama

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

ArjixWasTaken avatar Mar 09 '22 20:03 ArjixWasTaken

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]