Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions python-textual/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Python Textual: Build Beautiful UIs in the Terminal

This folder provides the code examples for the Real Python tutorial [Python Textual: Build Beautiful UIs in the Terminal](https://realpython.com/python-textual/).
25 changes: 25 additions & 0 deletions python-textual/buttons_and_inputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from textual.app import App
from textual.widgets import Button, Input


class ButtonsAndInputsApp(App):
def compose(self):
# Buttons
yield Button("Click me!")
yield Button("Primary!", variant="primary")
yield Button.success("Success!")
yield Button.warning("Warning!")
yield Button.error("Error!")
# Inputs
yield Input(placeholder="Type your text here")
yield Input(placeholder="Password", password=True)
yield Input(
placeholder="Type a number here",
type="number",
tooltip="Digits only please!",
)


if __name__ == "__main__":
app = ButtonsAndInputsApp()
app.run()
49 changes: 49 additions & 0 deletions python-textual/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# from textual import on
from textual.app import App
from textual.widgets import Button, Digits, Footer


class EventsApp(App):
CSS_PATH = "events.tcss"
BINDINGS = [
("q", "quit", "Quit"),
("b", "toggle_border", "Toggle border"),
]

presses_count = 0
double_border = False

def compose(self):
yield Button(
"Click me!",
id="button",
)
digits = Digits("0", id="digits")
digits.border_subtitle = "Button presses"
yield digits
yield Footer()

def action_toggle_border(self):
self.double_border = not self.double_border
digits = self.query_one("#digits")
if self.double_border:
digits.styles.border = ("double", "yellow")
else:
digits.styles.border = ("solid", "white")

def on_button_pressed(self, event):
if event.button.id == "button":
self.presses_count += 1
digits = self.query_one("#digits")
digits.update(f"{self.presses_count}")

# @on(Button.Pressed, "#button")
# def button_pressed(self, event):
# self.presses_count += 1
# digits = self.query_one("#digits")
# digits.update(f"{self.presses_count}")


if __name__ == "__main__":
app = EventsApp()
app.run()
16 changes: 16 additions & 0 deletions python-textual/events.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Button {
background: $secondary;
border: solid $primary;
margin: 2 2;
}

Button:hover {
border: round white;
}

#digits {
color: green;
border: solid white;
padding: 1;
width: 30;
}
23 changes: 23 additions & 0 deletions python-textual/grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Static


class GridLayoutExample(App):
def compose(self):
grid = Grid()
grid.styles.grid_size_rows = rows = 6
grid.styles.grid_size_columns = cols = 4
with grid:
for row in range(rows):
for col in range(cols):
static = Static(f"Static ({row=}, {col=})")
static.styles.border = ("solid", "green")
static.styles.width = "1fr"
static.styles.height = "1fr"
yield static


if __name__ == "__main__":
app = GridLayoutExample()
app.run()
9 changes: 9 additions & 0 deletions python-textual/grid.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Grid {
grid_size: 4 6;
}

Static {
height: 1fr;
width: 1fr;
border: solid green;
}
18 changes: 18 additions & 0 deletions python-textual/grid_with_tcss.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Static


class GridLayoutWithTCSS(App):
CSS_PATH = "grid.tcss"

def compose(self):
with Grid():
for row in range(6):
for col in range(4):
yield Static(f"Static ({row=}, {col=})")


if __name__ == "__main__":
app = GridLayoutWithTCSS()
app.run()
12 changes: 12 additions & 0 deletions python-textual/hello_textual.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from textual.app import App
from textual.widgets import Static


class HelloTextualApp(App):
def compose(self):
yield Static("Hello, Textual!")


if __name__ == "__main__":
app = HelloTextualApp()
app.run()
20 changes: 20 additions & 0 deletions python-textual/horizontal_layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from textual.app import App
from textual.containers import Horizontal
from textual.widgets import Static

NUM_BOXES = 4


class HorizontalLayoutExample(App):
def compose(self):
with Horizontal():
for i in range(NUM_BOXES):
static = Static(f"Static {i+1}")
static.styles.border = ("solid", "green")
static.styles.width = "10%"
yield static


if __name__ == "__main__":
app = HorizontalLayoutExample()
app.run()
20 changes: 20 additions & 0 deletions python-textual/horizontal_scroll.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from textual.app import App
from textual.containers import HorizontalScroll
from textual.widgets import Static

NUM_BOXES = 20


class HorizontalScrollExample(App):
def compose(self):
with HorizontalScroll():
for i in range(NUM_BOXES):
static = Static(f"Static {i+1}")
static.styles.border = ("solid", "green")
static.styles.width = "10%"
yield static


if __name__ == "__main__":
app = HorizontalScrollExample()
app.run()
38 changes: 38 additions & 0 deletions python-textual/layouts.py.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from textual.app import App
from textual.containers import (
Horizontal,
HorizontalScroll,
VerticalScroll,
)
from textual.widgets import Label, Static

NUM_BOXES = 12


class NestedContainersExample(App):
CSS_PATH = "layouts.tcss"

def compose(self):
with Horizontal(id="horizontal"):
yield Static("Left", classes="box")
with HorizontalScroll(id="horizontalscroll"):
for i in range(NUM_BOXES):
yield Static(
f"Center.{i+1}",
classes="box yellowbox",
)
with VerticalScroll(id="verticalscroll"):
for i in range(NUM_BOXES):
yield Static(
f"Right.{i+1}",
classes="box redbox",
)
yield Label(
"I am a docked label.\nI don't move!",
id="docked-label",
)


if __name__ == "__main__":
app = NestedContainersExample()
app.run()
21 changes: 21 additions & 0 deletions python-textual/layouts.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.box {
height: 1fr;
width: 1fr;
background:$panel;
border: solid white;
}

.redbox {
border: heavy red;
height: 5;
}

.yellowbox {
border: heavy yellow;
width: 10;
}

#docked-label {
dock: bottom;
border: solid dodgerblue;
}
32 changes: 32 additions & 0 deletions python-textual/static_and_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from textual.app import App
from textual.widgets import Label, Static


class StaticAndLabelApp(App):
def compose(self):
self.static = Static(
"I am a [bold red]Static[/bold red] widget!",
)
yield self.static
self.label = Label(
"I am a [yellow italic]Label[/yellow italic] widget!",
)
yield self.label

def on_mount(self):
# Styling the static
self.static.styles.background = "blue"
self.static.styles.border = ("solid", "white")
self.static.styles.text_align = "center"
self.static.styles.padding = 1, 1
self.static.styles.margin = 4, 4
# Styling the label
self.label.styles.background = "darkgreen"
self.label.styles.border = ("double", "red")
self.label.styles.padding = 1, 1
self.label.styles.margin = 2, 4


if __name__ == "__main__":
app = StaticAndLabelApp()
app.run()
23 changes: 23 additions & 0 deletions python-textual/static_and_label.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Static {
background: blue;
border: solid white;
padding: 1 1;
margin: 2 2;
text-align: center;
}

#label_id {
color: black;
background: yellow;
border: solid black;
padding: 1 1;
margin: 2 4;
}

.label_class {
color:black;
background: green;
border: dashed purple;
padding: 1 1;
margin: 2 6;
}
24 changes: 24 additions & 0 deletions python-textual/static_and_label_tcss.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from textual.app import App
from textual.widgets import Label, Static


class StaticAndLabelAppWithTCSS(App):
CSS_PATH = "static_and_label.tcss"

def compose(self):
yield Static(
"I am a [bold red]Static[/bold red] widget!",
)
yield Label(
"I am a [yellow italic]Label[/yellow italic] widget with an id!",
id="label_id",
)
yield Label(
"I am a [yellow italic]Label[/yellow italic] widget with a CSS class!",
classes="label_class",
)


if __name__ == "__main__":
app = StaticAndLabelAppWithTCSS()
app.run()
24 changes: 24 additions & 0 deletions python-textual/vertical_layout.py.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from textual.app import App
from textual.containers import Vertical
from textual.widgets import Static

NUM_BOXES = 4


class VerticalLayoutExample(App):
def compose(self):
with Vertical():
for i in range(NUM_BOXES):
static = Static(f"Static {i+1}")
static.styles.border = ("solid", "green")
yield static

# for i in range(1, NUM_BOXES):
# static = Static(f"Static {i}")
# static.styles.border = ("solid", "green")
# yield static


if __name__ == "__main__":
app = VerticalLayoutExample()
app.run()
3 changes: 3 additions & 0 deletions python-textual/vertical_layout.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Static {
border: solid green;
}
Loading