mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
[App] Add _path suffixes to PathLike constructor args
This commit is contained in:
@@ -87,7 +87,7 @@ class ColorChanger(App):
|
|||||||
self.background = f"on color({event.key})"
|
self.background = f"on color({event.key})"
|
||||||
|
|
||||||
|
|
||||||
ColorChanger.run(log="textual.log")
|
ColorChanger.run(log_path="textual.log")
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll notice that the `on_key` method above contains an additional `event` parameter which wasn't present on the beeper example. If the `event` argument is present, Textual will call the handler with an event object. Every event has an associated handler object, in this case it is a KeyEvent which contains additional information regarding which key was pressed.
|
You'll notice that the `on_key` method above contains an additional `event` parameter which wasn't present on the beeper example. If the `event` argument is present, Textual will call the handler with an event object. Every event has an associated handler object, in this case it is a KeyEvent which contains additional information regarding which key was pressed.
|
||||||
@@ -114,7 +114,7 @@ class SimpleApp(App):
|
|||||||
await self.view.dock(Placeholder(), Placeholder(), edge="top")
|
await self.view.dock(Placeholder(), Placeholder(), edge="top")
|
||||||
|
|
||||||
|
|
||||||
SimpleApp.run(log="textual.log")
|
SimpleApp.run(log_path="textual.log")
|
||||||
```
|
```
|
||||||
|
|
||||||
This app contains a single event handler `on_mount`. The mount event is sent when the app or widget is ready to start processing events, and is typically used for initialization. You may have noticed that `on_mount` is an `async` function. Since Textual is an asynchronous framework we will need this if we need to call most other methods.
|
This app contains a single event handler `on_mount`. The mount event is sent when the app or widget is ready to start processing events, and is typically used for initialization. You may have noticed that `on_mount` is an `async` function. Since Textual is an asynchronous framework we will need this if we need to call most other methods.
|
||||||
@@ -137,7 +137,7 @@ await self.view.dock(Placeholder(), Placeholder(), edge="top")
|
|||||||
|
|
||||||
You will notice that this time we are docking _two_ Placeholder objects onto the `"top"` edge. We haven't set an explicit size this time so Textual will divide the remaining size amongst the two new widgets.
|
You will notice that this time we are docking _two_ Placeholder objects onto the `"top"` edge. We haven't set an explicit size this time so Textual will divide the remaining size amongst the two new widgets.
|
||||||
|
|
||||||
The last line calls the `run` class method in the usual way, but with an argument we haven't seen before: `log="textual.log"` tells Textual to write log information to the given file. You can tail textual.log to see events being processed and other debug information.
|
The last line calls the `run` class method in the usual way, but with an argument we haven't seen before: `log_path="textual.log"` tells Textual to write log information to the given file. You can tail textual.log to see events being processed and other debug information.
|
||||||
|
|
||||||
If you run the above example, you will see something like the following:
|
If you run the above example, you will see something like the following:
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ class HoverApp(App):
|
|||||||
await self.view.dock(*hovers, edge="top")
|
await self.view.dock(*hovers, edge="top")
|
||||||
|
|
||||||
|
|
||||||
HoverApp.run(log="textual.log")
|
HoverApp.run(log_path="textual.log")
|
||||||
```
|
```
|
||||||
|
|
||||||
The `Hover` class is a custom widget which displays a panel containing the classic text "Hello World". The first line in the Hover class may seem a little mysterious at this point:
|
The `Hover` class is a custom widget which displays a panel containing the classic text "Hello World". The first line in the Hover class may seem a little mysterious at this point:
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ class ColorChanger(App):
|
|||||||
self.background = f"on color({event.key})"
|
self.background = f"on color({event.key})"
|
||||||
|
|
||||||
|
|
||||||
ColorChanger.run(log="textual.log")
|
ColorChanger.run(log_path="textual.log")
|
||||||
|
|||||||
@@ -29,4 +29,4 @@ class HoverApp(App):
|
|||||||
await self.screen.dock(*hovers, edge="top")
|
await self.screen.dock(*hovers, edge="top")
|
||||||
|
|
||||||
|
|
||||||
HoverApp.run(log="textual.log")
|
HoverApp.run(log_path="textual.log")
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ class SimpleApp(App):
|
|||||||
await self.screen.dock(Placeholder(), Placeholder(), edge="top")
|
await self.screen.dock(Placeholder(), Placeholder(), edge="top")
|
||||||
|
|
||||||
|
|
||||||
SimpleApp.run(log="textual.log")
|
SimpleApp.run(log_path="textual.log")
|
||||||
|
|||||||
@@ -143,9 +143,9 @@ class BasicApp(App):
|
|||||||
|
|
||||||
sandbox_folder = Path(__file__).parent
|
sandbox_folder = Path(__file__).parent
|
||||||
app = BasicApp(
|
app = BasicApp(
|
||||||
css_file=sandbox_folder / "basic.css",
|
css_path=sandbox_folder / "basic.css",
|
||||||
watch_css=True,
|
watch_css=True,
|
||||||
log=sandbox_folder / "textual.log",
|
log_path=sandbox_folder / "basic.log",
|
||||||
log_verbosity=0,
|
log_verbosity=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -35,4 +35,4 @@ class SmoothApp(App):
|
|||||||
# self.set_timer(10, lambda: self.action("quit"))
|
# self.set_timer(10, lambda: self.action("quit"))
|
||||||
|
|
||||||
|
|
||||||
SmoothApp.run(log="textual.log", log_verbosity=2)
|
SmoothApp.run(log_path="textual.log", log_verbosity=2)
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ class MyApp(App):
|
|||||||
await self.call_later(add_content)
|
await self.call_later(add_content)
|
||||||
|
|
||||||
|
|
||||||
MyApp.run(title="Simple App", log="textual.log")
|
MyApp.run(title="Simple App", log_path="textual.log")
|
||||||
|
|||||||
@@ -55,4 +55,4 @@ class BordersApp(App):
|
|||||||
self.mount(borders=borders_view)
|
self.mount(borders=borders_view)
|
||||||
|
|
||||||
|
|
||||||
BordersApp.run(css_file="borders.css", log="textual.log")
|
BordersApp.run(css_path="borders.css", log_path="textual.log")
|
||||||
|
|||||||
@@ -212,4 +212,4 @@ class CalculatorApp(App):
|
|||||||
await self.screen.dock(Calculator())
|
await self.screen.dock(Calculator())
|
||||||
|
|
||||||
|
|
||||||
CalculatorApp.run(title="Calculator Test", log="textual.log")
|
CalculatorApp.run(title="Calculator Test", log_path="textual.log")
|
||||||
|
|||||||
@@ -67,4 +67,4 @@ class MyApp(App):
|
|||||||
|
|
||||||
|
|
||||||
# Run our app class
|
# Run our app class
|
||||||
MyApp.run(title="Code Viewer", log="textual.log")
|
MyApp.run(title="Code Viewer", log_path="textual.log")
|
||||||
|
|||||||
@@ -42,4 +42,4 @@ class EasingApp(App):
|
|||||||
self.side = not self.side
|
self.side = not self.side
|
||||||
|
|
||||||
|
|
||||||
EasingApp().run(log="textual.log")
|
EasingApp().run(log_path="textual.log")
|
||||||
|
|||||||
@@ -31,4 +31,4 @@ class GridTest(App):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
GridTest.run(title="Grid Test", log="textual.log")
|
GridTest.run(title="Grid Test", log_path="textual.log")
|
||||||
|
|||||||
@@ -19,4 +19,4 @@ class GridTest(App):
|
|||||||
grid.place(*placeholders, center=Placeholder())
|
grid.place(*placeholders, center=Placeholder())
|
||||||
|
|
||||||
|
|
||||||
GridTest.run(title="Grid Test", log="textual.log")
|
GridTest.run(title="Grid Test", log_path="textual.log")
|
||||||
|
|||||||
@@ -48,4 +48,4 @@ class MyApp(App):
|
|||||||
await self.call_later(get_markdown, "richreadme.md")
|
await self.call_later(get_markdown, "richreadme.md")
|
||||||
|
|
||||||
|
|
||||||
MyApp.run(title="Simple App", log="textual.log")
|
MyApp.run(title="Simple App", log_path="textual.log")
|
||||||
|
|||||||
@@ -22,4 +22,4 @@ class AlignApp(App):
|
|||||||
self.log(self.screen.tree)
|
self.log(self.screen.tree)
|
||||||
|
|
||||||
|
|
||||||
AlignApp.run(css_file="align.css", log="textual.log", watch_css=True)
|
AlignApp.run(css_path="align.css", log_path="textual.log", watch_css=True)
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from rich.align import Align
|
|
||||||
from rich.console import RenderableType
|
from rich.console import RenderableType
|
||||||
from rich.syntax import Syntax
|
from rich.syntax import Syntax
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
@@ -141,11 +138,10 @@ class BasicApp(App):
|
|||||||
self.panic(self.tree)
|
self.panic(self.tree)
|
||||||
|
|
||||||
|
|
||||||
sandbox_folder = Path(__file__).parent
|
|
||||||
app = BasicApp(
|
app = BasicApp(
|
||||||
css_file=sandbox_folder / "basic.css",
|
css_path="basic.css",
|
||||||
watch_css=True,
|
watch_css=True,
|
||||||
log=sandbox_folder / "textual.log",
|
log_path="textual.log",
|
||||||
)
|
)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from textual import layout
|
|||||||
|
|
||||||
|
|
||||||
class ButtonsApp(App[str]):
|
class ButtonsApp(App[str]):
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield layout.Vertical(
|
yield layout.Vertical(
|
||||||
Button("foo", id="foo"),
|
Button("foo", id="foo"),
|
||||||
@@ -19,7 +18,7 @@ class ButtonsApp(App[str]):
|
|||||||
self.exit(event.button.id)
|
self.exit(event.button.id)
|
||||||
|
|
||||||
|
|
||||||
app = ButtonsApp(log="textual.log", log_verbosity=2)
|
app = ButtonsApp(log_path="textual.log", log_verbosity=2)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
result = app.run()
|
result = app.run()
|
||||||
|
|||||||
@@ -34,4 +34,4 @@ class BasicApp(App):
|
|||||||
self.panic(self.tree)
|
self.panic(self.tree)
|
||||||
|
|
||||||
|
|
||||||
BasicApp.run(css_file="dev_sandbox.scss", watch_css=True, log="textual.log")
|
BasicApp.run(css_path="dev_sandbox.scss", watch_css=True, log_path="textual.log")
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ class BasicApp(App):
|
|||||||
self.log(header.styles)
|
self.log(header.styles)
|
||||||
|
|
||||||
|
|
||||||
BasicApp.run(css_file="local_styles.css", log="textual.log")
|
BasicApp.run(css_path="local_styles.css", log_path="textual.log")
|
||||||
|
|||||||
@@ -144,4 +144,4 @@ class BasicApp(App):
|
|||||||
self.mount(example.widget)
|
self.mount(example.widget)
|
||||||
|
|
||||||
|
|
||||||
BasicApp.run(css_file="tabs.scss", watch_css=True, log="textual.log")
|
BasicApp.run(css_path="tabs.scss", watch_css=True, log_path="textual.log")
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class BasicApp(App):
|
|||||||
self.focused.styles.border_top = ("solid", "invalid-color")
|
self.focused.styles.border_top = ("solid", "invalid-color")
|
||||||
|
|
||||||
|
|
||||||
app = BasicApp(css_file="uber.css", log="textual.log", log_verbosity=1)
|
app = BasicApp(css_path="uber.css", log_path="textual.log", log_verbosity=1)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run()
|
app.run()
|
||||||
|
|||||||
@@ -106,20 +106,20 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
driver_class: Type[Driver] | None = None,
|
driver_class: Type[Driver] | None = None,
|
||||||
log: str | PurePath = "",
|
log_path: str | PurePath = "",
|
||||||
log_verbosity: int = 1,
|
log_verbosity: int = 1,
|
||||||
title: str = "Textual Application",
|
title: str = "Textual Application",
|
||||||
css_file: str | PurePath | None = None,
|
css_path: str | PurePath | None = None,
|
||||||
watch_css: bool = True,
|
watch_css: bool = True,
|
||||||
):
|
):
|
||||||
"""Textual application base class
|
"""Textual application base class
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
driver_class (Type[Driver] | None, optional): Driver class or ``None`` to auto-detect. Defaults to None.
|
driver_class (Type[Driver] | None, optional): Driver class or ``None`` to auto-detect. Defaults to None.
|
||||||
log (str | PurePath, optional): Path to log file, or "" to disable. Defaults to "".
|
log_path (str | PurePath, optional): Path to log file, or "" to disable. Defaults to "".
|
||||||
log_verbosity (int, optional): Log verbosity from 0-3. Defaults to 1.
|
log_verbosity (int, optional): Log verbosity from 0-3. Defaults to 1.
|
||||||
title (str, optional): Default title of the application. Defaults to "Textual Application".
|
title (str, optional): Default title of the application. Defaults to "Textual Application".
|
||||||
css_file (str | PurePath | None, optional): Path to CSS or ``None`` for no CSS file. Defaults to None.
|
css_path (str | PurePath | None, optional): Path to CSS or ``None`` for no CSS file. Defaults to None.
|
||||||
watch_css (bool, optional): Watch CSS for changes. Defaults to True.
|
watch_css (bool, optional): Watch CSS for changes. Defaults to True.
|
||||||
"""
|
"""
|
||||||
# N.B. This must be done *before* we call the parent constructor, because MessagePump's
|
# N.B. This must be done *before* we call the parent constructor, because MessagePump's
|
||||||
@@ -151,8 +151,8 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
|
|
||||||
self._log_console: Console | None = None
|
self._log_console: Console | None = None
|
||||||
self._log_file: TextIO | None = None
|
self._log_file: TextIO | None = None
|
||||||
if log:
|
if log_path:
|
||||||
self._log_file = open(log, "wt")
|
self._log_file = open(log_path, "wt")
|
||||||
self._log_console = Console(
|
self._log_console = Console(
|
||||||
file=self._log_file,
|
file=self._log_file,
|
||||||
markup=False,
|
markup=False,
|
||||||
@@ -171,10 +171,10 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
self.stylesheet = Stylesheet(variables=self.get_css_variables())
|
self.stylesheet = Stylesheet(variables=self.get_css_variables())
|
||||||
self._require_styles_update = False
|
self._require_styles_update = False
|
||||||
|
|
||||||
self.css_file = css_file
|
self.css_path = css_path
|
||||||
self.css_monitor = (
|
self.css_monitor = (
|
||||||
FileMonitor(css_file, self._on_css_change)
|
FileMonitor(css_path, self._on_css_change)
|
||||||
if (watch_css and css_file)
|
if (watch_css and css_path)
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -450,13 +450,13 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
|
|
||||||
async def _on_css_change(self) -> None:
|
async def _on_css_change(self) -> None:
|
||||||
"""Called when the CSS changes (if watch_css is True)."""
|
"""Called when the CSS changes (if watch_css is True)."""
|
||||||
if self.css_file is not None:
|
if self.css_path is not None:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
time = perf_counter()
|
time = perf_counter()
|
||||||
self.stylesheet.read(self.css_file)
|
self.stylesheet.read(self.css_path)
|
||||||
elapsed = (perf_counter() - time) * 1000
|
elapsed = (perf_counter() - time) * 1000
|
||||||
self.log(f"loaded {self.css_file} in {elapsed:.0f}ms")
|
self.log(f"loaded {self.css_path} in {elapsed:.0f}ms")
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
# TODO: Catch specific exceptions
|
# TODO: Catch specific exceptions
|
||||||
self.console.bell()
|
self.console.bell()
|
||||||
@@ -647,8 +647,8 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
except DevtoolsConnectionError:
|
except DevtoolsConnectionError:
|
||||||
self.log(f"Couldn't connect to devtools ({self.devtools.url})")
|
self.log(f"Couldn't connect to devtools ({self.devtools.url})")
|
||||||
try:
|
try:
|
||||||
if self.css_file is not None:
|
if self.css_path is not None:
|
||||||
self.stylesheet.read(self.css_file)
|
self.stylesheet.read(self.css_path)
|
||||||
if self.CSS is not None:
|
if self.CSS is not None:
|
||||||
self.stylesheet.add_source(
|
self.stylesheet.add_source(
|
||||||
self.CSS, path=f"<{self.__class__.__name__}>"
|
self.CSS, path=f"<{self.__class__.__name__}>"
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ def _check_selectors(selectors: list[Selector], node: DOMNode) -> bool:
|
|||||||
|
|
||||||
DESCENDENT = CombinatorType.DESCENDENT
|
DESCENDENT = CombinatorType.DESCENDENT
|
||||||
|
|
||||||
css_path = node.css_path
|
css_path_nodes = node.css_path_nodes
|
||||||
path_count = len(css_path)
|
path_count = len(css_path_nodes)
|
||||||
selector_count = len(selectors)
|
selector_count = len(selectors)
|
||||||
|
|
||||||
stack: list[tuple[int, int]] = [(0, 0)]
|
stack: list[tuple[int, int]] = [(0, 0)]
|
||||||
@@ -51,7 +51,7 @@ def _check_selectors(selectors: list[Selector], node: DOMNode) -> bool:
|
|||||||
if selector_index == selector_count or node_index == path_count:
|
if selector_index == selector_count or node_index == path_count:
|
||||||
pop()
|
pop()
|
||||||
else:
|
else:
|
||||||
path_node = css_path[node_index]
|
path_node = css_path_nodes[node_index]
|
||||||
selector = selectors[selector_index]
|
selector = selectors[selector_index]
|
||||||
if selector.combinator == DESCENDENT:
|
if selector.combinator == DESCENDENT:
|
||||||
# Find a matching descendent
|
# Find a matching descendent
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class DOMNode(MessagePump):
|
|||||||
return self.__class__.__name__.lower()
|
return self.__class__.__name__.lower()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def css_path(self) -> list[DOMNode]:
|
def css_path_nodes(self) -> list[DOMNode]:
|
||||||
"""A list of nodes from the root to this node, forming a "path".
|
"""A list of nodes from the root to this node, forming a "path".
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|||||||
@@ -132,4 +132,4 @@ if __name__ == "__main__":
|
|||||||
async def on_mount(self, event: events.Mount) -> None:
|
async def on_mount(self, event: events.Mount) -> None:
|
||||||
await self.screen.dock(DirectoryTree("/Users/willmcgugan/projects"))
|
await self.screen.dock(DirectoryTree("/Users/willmcgugan/projects"))
|
||||||
|
|
||||||
TreeApp.run(log="textual.log")
|
TreeApp(log_path="textual.log").run()
|
||||||
|
|||||||
@@ -328,4 +328,4 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
await message.node.toggle()
|
await message.node.toggle()
|
||||||
|
|
||||||
TreeApp.run(log="textual.log")
|
TreeApp(log_path="textual.log").run()
|
||||||
|
|||||||
Reference in New Issue
Block a user