Skip to content

Instantly share code, notes, and snippets.

@llimllib
Created December 28, 2021 17:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save llimllib/61e0e5632bac60e8d8c0016ed92da82d to your computer and use it in GitHub Desktop.
Save llimllib/61e0e5632bac60e8d8c0016ed92da82d to your computer and use it in GitHub Desktop.
The simplest menu implementation I could come up with in rich
from rich.console import Console
from rich.style import Style
from rich.table import Table
from getchar import getchar
SELECTED = Style(bgcolor="white")
def print_table(console, table, rows=[], selected=0):
for i, row in enumerate(rows):
if i == selected:
table.add_row(*row, style=SELECTED)
else:
table.add_row(*row)
console.print(table)
def make_table():
table = Table.grid(expand=True)
table.add_column()
table.add_column()
table.add_column()
return table
UP = "\x1b[A"
DOWN = "\x1b[B"
ENTER = "\r"
selected = 0
console = Console()
items = [
("a", "b", "c"),
("bb", "b", "c"),
("ccc", "b", "c"),
("dddd", "b", "c"),
]
console.clear()
print_table(console, make_table(), items, selected)
while True:
ch = getchar(False)
if ch == UP:
selected = max(0, selected - 1)
if ch == DOWN:
selected = min(len(items) - 1, selected + 1)
if ch == ENTER:
print("you selected: ", items[selected])
break
console.clear()
print_table(console, make_table(), items, selected)
# modified from https://github.com/pallets/click/blob/051d57cef4ce59212dc1175ad4550743bf47d840/src/click/_termui_impl.py
import contextlib
import codecs
import os
import sys
import tty
import termios
import typing as t
def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]:
if ch == "\x03":
raise KeyboardInterrupt()
if ch == "\x04": # Unix-like, Ctrl+D
raise EOFError()
return None
def is_ascii_encoding(encoding: str) -> bool:
"""Checks if a given encoding is ascii."""
try:
return codecs.lookup(encoding).name == "ascii"
except LookupError:
return False
def get_best_encoding(stream: t.IO) -> str:
"""Returns the default stream encoding if not found."""
rv = getattr(stream, "encoding", None) or sys.getdefaultencoding()
if is_ascii_encoding(rv):
return "utf-8"
return rv
@contextlib.contextmanager
def raw_terminal() -> t.Iterator[int]:
f: t.Optional[t.TextIO]
fd: int
if not sys.stdin.isatty():
f = open("/dev/tty")
fd = f.fileno()
else:
fd = sys.stdin.fileno()
f = None
try:
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
yield fd
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
sys.stdout.flush()
if f is not None:
f.close()
except termios.error:
pass
# this won't work in windows because I only copied the unix version
def getchar(echo: bool) -> str:
with raw_terminal() as fd:
ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace")
if echo and sys.stdout.isatty():
sys.stdout.write(ch)
_translate_ch_to_exc(ch)
return ch
if __name__ == "__main__":
import json
while True:
ch = getchar(False)
print(json.dumps(ch))
if ch == "\x1b[A":
print("up")
if ch == "\x1b[B":
print("down")
if ch == "\x1b[B":
print("right")
if ch == "\x1b[D":
print("left")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment