feat: 新增技能扩展N15一章相关示例源码
This commit is contained in:
9
code/newsletter/N15/01_basic_app.py
Normal file
9
code/newsletter/N15/01_basic_app.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from textual.app import App
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleApp(App):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__== '__main__':
|
||||||
|
app = SimpleApp()
|
||||||
|
app.run()
|
||||||
70
code/newsletter/N15/02_widgets.py
Normal file
70
code/newsletter/N15/02_widgets.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import re
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.containers import Container
|
||||||
|
from textual.reactive import reactive
|
||||||
|
from textual.widgets import Footer, Header, Static
|
||||||
|
|
||||||
|
lorem = """\
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
Sed eget suscipit ligula.
|
||||||
|
Curabitur id justo imperdiet, pretium orci sed, tempus leo.
|
||||||
|
Integer vulputate vitae diam efficitur tempus.
|
||||||
|
Donec vel est ac purus feugiat ultricies sit amet non sapien.
|
||||||
|
Vivamus faucibus viverra bibendum.
|
||||||
|
Integer congue sed mi et imperdiet. Praesent quis dapibus neque.
|
||||||
|
Cras arcu purus, laoreet eu nibh scelerisque, bibendum tincidunt mauris.
|
||||||
|
Cras congue erat leo, non efficitur tellus auctor a.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class LoremWidget(Static):
|
||||||
|
|
||||||
|
is_highlight = reactive(False, layout=True)
|
||||||
|
_RE_LOREM = re.compile("(?P<target>Lorem ipsum)", re.I)
|
||||||
|
|
||||||
|
_cache_lorem = None
|
||||||
|
_raw_lorem = dedent(lorem)
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self.update(self._raw_lorem)
|
||||||
|
|
||||||
|
async def watch_is_highlight(self, old_value, new_value) -> None:
|
||||||
|
if self.is_highlight:
|
||||||
|
content = self.marked_lorem()
|
||||||
|
self.update(content)
|
||||||
|
else:
|
||||||
|
self.update(self._raw_lorem)
|
||||||
|
|
||||||
|
def marked_lorem(self):
|
||||||
|
if self._cache_lorem:
|
||||||
|
return self._cache_lorem
|
||||||
|
|
||||||
|
content = self._RE_LOREM.sub(r"[u red b]\1[/]", lorem)
|
||||||
|
self._cache_lorem = content
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleApp(App):
|
||||||
|
BINDINGS = [
|
||||||
|
("h", "highlight", "Highlight lorem"),
|
||||||
|
("q", "quit", "Quit"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header()
|
||||||
|
yield Container(LoremWidget(id="lorem"))
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def action_highlight(self) -> None:
|
||||||
|
widget = self.query_one("#lorem")
|
||||||
|
widget.is_highlight = not widget.is_highlight
|
||||||
|
|
||||||
|
def action_quit(self) -> None:
|
||||||
|
self.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = SimpleApp()
|
||||||
|
app.run()
|
||||||
74
code/newsletter/N15/03_actions.py
Normal file
74
code/newsletter/N15/03_actions.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import re
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
from textual import events
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.containers import Container
|
||||||
|
from textual.reactive import reactive
|
||||||
|
from textual.widgets import Footer, Header, Static
|
||||||
|
|
||||||
|
lorem = """\
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
Sed eget suscipit ligula.
|
||||||
|
Curabitur id justo imperdiet, pretium orci sed, tempus leo.
|
||||||
|
Integer vulputate vitae diam efficitur tempus.
|
||||||
|
Donec vel est ac purus feugiat ultricies sit amet non sapien.
|
||||||
|
Vivamus faucibus viverra bibendum.
|
||||||
|
Integer congue sed mi et imperdiet. Praesent quis dapibus neque.
|
||||||
|
Cras arcu purus, laoreet eu nibh scelerisque, bibendum tincidunt mauris.
|
||||||
|
Cras congue erat leo, non efficitur tellus auctor a.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class LoremWidget(Static):
|
||||||
|
|
||||||
|
is_highlight = reactive(False, layout=True)
|
||||||
|
_RE_LOREM = re.compile("(?P<target>Lorem ipsum)", re.I)
|
||||||
|
|
||||||
|
_cache_lorem = None
|
||||||
|
_raw_lorem = dedent(lorem)
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self.update(self._raw_lorem)
|
||||||
|
|
||||||
|
async def watch_is_highlight(self, old_value, new_value) -> None:
|
||||||
|
if self.is_highlight:
|
||||||
|
content = self.marked_lorem()
|
||||||
|
self.update(content)
|
||||||
|
else:
|
||||||
|
self.update(self._raw_lorem)
|
||||||
|
|
||||||
|
def marked_lorem(self):
|
||||||
|
if self._cache_lorem:
|
||||||
|
return self._cache_lorem
|
||||||
|
|
||||||
|
content = self._RE_LOREM.sub(r"[u red b]\1[/]", lorem)
|
||||||
|
self._cache_lorem = content
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleApp(App):
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header()
|
||||||
|
yield Container(LoremWidget(id="lorem"))
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def on_key(self, event: events.Key) -> None:
|
||||||
|
if event.key == "h":
|
||||||
|
self.action_highlight()
|
||||||
|
elif event.key == "q":
|
||||||
|
self.action_quit()
|
||||||
|
elif event.key == "d":
|
||||||
|
self.action_toggle_dark()
|
||||||
|
|
||||||
|
def action_highlight(self) -> None:
|
||||||
|
widget = self.query_one("#lorem")
|
||||||
|
widget.is_highlight = not widget.is_highlight
|
||||||
|
|
||||||
|
def action_quit(self) -> None:
|
||||||
|
self.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = SimpleApp()
|
||||||
|
app.run()
|
||||||
62
code/newsletter/N15/04_events.py
Normal file
62
code/newsletter/N15/04_events.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from textual import events
|
||||||
|
from textual.app import App
|
||||||
|
from textual.containers import Container
|
||||||
|
from textual.widgets import Static
|
||||||
|
|
||||||
|
|
||||||
|
class ClickableColorBlock(Static):
|
||||||
|
background_toggled = False
|
||||||
|
raw_background = ""
|
||||||
|
|
||||||
|
def on_mount(self):
|
||||||
|
self.styles.border = ("heavy", "white")
|
||||||
|
self.raw_background = self.styles.background
|
||||||
|
|
||||||
|
def on_click(self, event: events.Click):
|
||||||
|
event.prevent_default()
|
||||||
|
|
||||||
|
self.background_toggled = not self.background_toggled
|
||||||
|
self.styles.background = (
|
||||||
|
str(self.render()) if self.background_toggled else self.raw_background
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log("block background changed!")
|
||||||
|
|
||||||
|
|
||||||
|
class DisplayApp(App):
|
||||||
|
CSS_PATH = "css/events.css"
|
||||||
|
background_toggled = False
|
||||||
|
|
||||||
|
def compose(self):
|
||||||
|
yield Container(
|
||||||
|
ClickableColorBlock("red", classes="block"),
|
||||||
|
ClickableColorBlock("blue", classes="block"),
|
||||||
|
ClickableColorBlock("green", classes="block"),
|
||||||
|
ClickableColorBlock("cyan", classes="block"),
|
||||||
|
id="display-container",
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_click(self):
|
||||||
|
self.background_toggled = not self.background_toggled
|
||||||
|
target = self.query_one("#display-container")
|
||||||
|
blocks = target.query(".block")
|
||||||
|
background = None
|
||||||
|
border = None
|
||||||
|
|
||||||
|
if self.background_toggled:
|
||||||
|
background = "white"
|
||||||
|
border = ("heavy", "black")
|
||||||
|
else:
|
||||||
|
background = "grey"
|
||||||
|
border = ("heavy", "white")
|
||||||
|
|
||||||
|
target.styles.background = background
|
||||||
|
for block in blocks:
|
||||||
|
block.styles.border = border
|
||||||
|
|
||||||
|
self.log("container background changed!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = DisplayApp()
|
||||||
|
app.run()
|
||||||
55
code/newsletter/N15/05_reactivity.py
Normal file
55
code/newsletter/N15/05_reactivity.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
from textual.app import App
|
||||||
|
from textual.containers import Vertical
|
||||||
|
from textual.reactive import reactive
|
||||||
|
from textual.widget import Widget
|
||||||
|
from textual.widgets import Input
|
||||||
|
|
||||||
|
|
||||||
|
class WelcomeWidget(Widget):
|
||||||
|
who = reactive("World!")
|
||||||
|
total = reactive(15)
|
||||||
|
|
||||||
|
def on_mount(self):
|
||||||
|
default_styles = """
|
||||||
|
margin: 1;
|
||||||
|
padding: 1;
|
||||||
|
"""
|
||||||
|
self.set_styles(default_styles)
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
return f":partying_face: Hello, [b red]{self.who}[/]! ({self.total}/15)"
|
||||||
|
|
||||||
|
def watch_who(self, old_value, new_value):
|
||||||
|
self.log(
|
||||||
|
f"who attribute changed: old value: {old_value}, "
|
||||||
|
f"new value: {new_value} "
|
||||||
|
)
|
||||||
|
|
||||||
|
def compute_total(self):
|
||||||
|
return len(self.who)
|
||||||
|
|
||||||
|
|
||||||
|
class InputChangeApp(App):
|
||||||
|
def compose(self):
|
||||||
|
yield Vertical(
|
||||||
|
Input(placeholder="enter your name..."),
|
||||||
|
WelcomeWidget(classes="welcome"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_input_changed(self, event: Input.Changed) -> None:
|
||||||
|
target = self.query_one(".welcome")
|
||||||
|
value = event.value
|
||||||
|
|
||||||
|
if len(value) > 15:
|
||||||
|
event.input.value = value[:15]
|
||||||
|
return
|
||||||
|
|
||||||
|
if not value:
|
||||||
|
target.who = "World!"
|
||||||
|
else:
|
||||||
|
target.who = value
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = InputChangeApp()
|
||||||
|
app.run()
|
||||||
16
code/newsletter/N15/css/events.css
Normal file
16
code/newsletter/N15/css/events.css
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#display-container {
|
||||||
|
layout: grid;
|
||||||
|
grid-size: 2 2;
|
||||||
|
align: center middle;
|
||||||
|
|
||||||
|
background: grey;
|
||||||
|
border: solid white;
|
||||||
|
margin: 1;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
content-align: center middle;
|
||||||
|
width: 100%;
|
||||||
|
margin: 2;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user