mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
fix screenshots
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
TimerWidget {
|
||||
layout: horizontal;
|
||||
height: 5;
|
||||
background: $panel-darken-1;
|
||||
border: tall $panel-darken-2;
|
||||
height: 5;
|
||||
min-width: 50;
|
||||
margin: 1;
|
||||
padding: 0 1;
|
||||
transition: background 300ms linear;
|
||||
min-width: 50;
|
||||
}
|
||||
|
||||
TimerWidget.started {
|
||||
@@ -35,6 +35,14 @@ Button {
|
||||
dock: left;
|
||||
}
|
||||
|
||||
#stop {
|
||||
dock: left;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#reset {
|
||||
dock: right;
|
||||
}
|
||||
|
||||
TimerWidget.started #start {
|
||||
display: none
|
||||
@@ -48,11 +56,3 @@ TimerWidget.started #reset {
|
||||
visibility: hidden
|
||||
}
|
||||
|
||||
#stop {
|
||||
dock: left;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#reset {
|
||||
dock: right;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,6 @@ class TimerApp(App):
|
||||
timers.last().remove()
|
||||
|
||||
|
||||
app = TimerApp(title="TimerApp", css_path="timers.css")
|
||||
app = TimerApp(css_path="timers.css")
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
|
||||
@@ -10,6 +10,11 @@ By the end of this page you should have a good idea of the steps involved in cre
|
||||
- Installed `textual` from Pypi.
|
||||
- Basic Python skills.
|
||||
|
||||
|
||||
```{.textual path="docs/examples/introduction/timers.py"}
|
||||
|
||||
```
|
||||
|
||||
## A Simple App
|
||||
|
||||
Let's looks at the simplest possible Textual app.
|
||||
|
||||
@@ -12,6 +12,7 @@ def format_svg(source, language, css_class, options, md, attrs, **kwargs):
|
||||
os.environ["LINES"] = attrs.get("lines", "24")
|
||||
path = attrs.get("path")
|
||||
|
||||
print(f"screenshotting {path!r}")
|
||||
if path:
|
||||
cwd = os.getcwd()
|
||||
examples_path, filename = os.path.split(path)
|
||||
@@ -34,4 +35,5 @@ def format_svg(source, language, css_class, options, md, attrs, **kwargs):
|
||||
app = app_vars["app"]
|
||||
app.run()
|
||||
svg = app._screenshot
|
||||
|
||||
return svg
|
||||
|
||||
@@ -188,6 +188,6 @@ def align_lines(
|
||||
get_line_length = Segment.get_line_length
|
||||
for line in lines:
|
||||
left_space = width - get_line_length(line)
|
||||
yield [*line, Segment(" " * left_space, style)]
|
||||
yield [Segment(" " * left_space, style), *line]
|
||||
|
||||
yield from blank_lines(bottom_blank_lines)
|
||||
|
||||
@@ -61,6 +61,7 @@ class Timer:
|
||||
self._repeat = repeat
|
||||
self._skip = skip
|
||||
self._active = Event()
|
||||
self._task: Task | None = None
|
||||
if not pause:
|
||||
self._active.set()
|
||||
|
||||
@@ -82,17 +83,21 @@ class Timer:
|
||||
Returns:
|
||||
Task: A Task instance for the timer.
|
||||
"""
|
||||
self._task = asyncio.create_task(self._run())
|
||||
self._task = asyncio.create_task(self.run())
|
||||
return self._task
|
||||
|
||||
def stop_no_wait(self) -> None:
|
||||
"""Stop the timer."""
|
||||
if self._task is not None:
|
||||
self._task.cancel()
|
||||
self._task = None
|
||||
|
||||
async def stop(self) -> None:
|
||||
"""Stop the timer, and block until it exits."""
|
||||
if self._task is not None:
|
||||
self._active.set()
|
||||
self._task.cancel()
|
||||
await self._task
|
||||
self._task = None
|
||||
|
||||
def pause(self) -> None:
|
||||
"""Pause the timer."""
|
||||
@@ -102,13 +107,19 @@ class Timer:
|
||||
"""Result a paused timer."""
|
||||
self._active.set()
|
||||
|
||||
async def run(self) -> None:
|
||||
"""Run the timer task."""
|
||||
try:
|
||||
await self._run()
|
||||
except CancelledError:
|
||||
pass
|
||||
|
||||
async def _run(self) -> None:
|
||||
"""Run the timer."""
|
||||
count = 0
|
||||
_repeat = self._repeat
|
||||
_interval = self._interval
|
||||
start = _clock.get_time_no_wait()
|
||||
try:
|
||||
while _repeat is None or count <= _repeat:
|
||||
next_timer = start + ((count + 1) * _interval)
|
||||
now = await _clock.get_time()
|
||||
@@ -125,8 +136,6 @@ class Timer:
|
||||
except EventTargetGone:
|
||||
break
|
||||
await self._active.wait()
|
||||
except CancelledError:
|
||||
pass
|
||||
|
||||
async def _tick(self, *, next_timer: float, count: int) -> None:
|
||||
"""Triggers the Timer's action: either call its callback, or sends an event to its target"""
|
||||
@@ -140,5 +149,4 @@ class Timer:
|
||||
count=count,
|
||||
callback=self._callback,
|
||||
)
|
||||
|
||||
await self.target.post_priority_message(event)
|
||||
|
||||
@@ -144,7 +144,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
log_color_system: Literal[
|
||||
"auto", "standard", "256", "truecolor", "windows"
|
||||
] = "auto",
|
||||
title: str = "Textual Application",
|
||||
title: str | None = None,
|
||||
css_path: str | PurePath | None = None,
|
||||
watch_css: bool = False,
|
||||
):
|
||||
@@ -189,6 +189,9 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.animate = self._animator.bind(self)
|
||||
self.mouse_position = Offset(0, 0)
|
||||
self.bindings = Bindings()
|
||||
if title is None:
|
||||
self._title = f"{self.__class__.__name__}"
|
||||
else:
|
||||
self._title = title
|
||||
|
||||
self._log_console: Console | None = None
|
||||
@@ -1015,7 +1018,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self._screenshot = svg # type: ignore
|
||||
await self.shutdown()
|
||||
|
||||
self.set_timer(screenshot_timer, on_screenshot)
|
||||
self.set_timer(screenshot_timer, on_screenshot, name="screenshot timer")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
widgets = self.compose()
|
||||
|
||||
@@ -105,7 +105,7 @@ class DOMNode(MessagePump):
|
||||
self._auto_refresh_timer = None
|
||||
if interval is not None:
|
||||
self._auto_refresh_timer = self.set_interval(
|
||||
interval, self._automatic_refresh
|
||||
interval, self._automatic_refresh, name=f"auto refresh {self!r}"
|
||||
)
|
||||
self._auto_refresh = interval
|
||||
|
||||
|
||||
@@ -1,14 +1,44 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
import asyncio
|
||||
from ..driver import Driver
|
||||
from ..geometry import Size
|
||||
from .. import events
|
||||
|
||||
|
||||
class HeadlessDriver(Driver):
|
||||
"""A do-nothing driver for testing."""
|
||||
|
||||
def start_application_mode(self) -> None:
|
||||
def _get_terminal_size(self) -> tuple[int, int]:
|
||||
width: int | None = 80
|
||||
height: int | None = 25
|
||||
import shutil
|
||||
|
||||
try:
|
||||
width, height = shutil.get_terminal_size()
|
||||
except (AttributeError, ValueError, OSError):
|
||||
try:
|
||||
width, height = shutil.get_terminal_size()
|
||||
except (AttributeError, ValueError, OSError):
|
||||
pass
|
||||
width = width or 80
|
||||
height = height or 25
|
||||
return width, height
|
||||
|
||||
def start_application_mode(self) -> None:
|
||||
loop = asyncio.get_running_loop()
|
||||
|
||||
def send_size_event():
|
||||
terminal_size = self._get_terminal_size()
|
||||
width, height = terminal_size
|
||||
textual_size = Size(width, height)
|
||||
event = events.Resize(self._target, textual_size, textual_size)
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
self._target.post_message(event),
|
||||
loop=loop,
|
||||
)
|
||||
|
||||
send_size_event()
|
||||
|
||||
def disable_input(self) -> None:
|
||||
pass
|
||||
|
||||
@@ -254,7 +254,8 @@ class MessagePump(metaclass=MessagePumpMeta):
|
||||
if self._closed or self._closing:
|
||||
return
|
||||
self._closing = True
|
||||
for timer in self._timers:
|
||||
stop_timers = list(self._timers)
|
||||
for timer in stop_timers:
|
||||
await timer.stop()
|
||||
self._timers.clear()
|
||||
await self._message_queue.put(MessagePriority(None))
|
||||
@@ -274,7 +275,7 @@ class MessagePump(metaclass=MessagePumpMeta):
|
||||
pass
|
||||
finally:
|
||||
self._running = False
|
||||
for timer in self._timers:
|
||||
for timer in list(self._timers):
|
||||
await timer.stop()
|
||||
|
||||
async def _process_messages(self) -> None:
|
||||
|
||||
@@ -33,8 +33,6 @@ class Screen(Widget):
|
||||
|
||||
CSS = """
|
||||
Screen {
|
||||
color: $text-background;
|
||||
background: $background;
|
||||
layout: vertical;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class HeaderClock(Widget):
|
||||
"""
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.set_interval(1, callback=self.refresh)
|
||||
self.set_interval(1, callback=self.refresh, name=f"update header clock")
|
||||
|
||||
def render(self):
|
||||
return Text(datetime.now().time().strftime("%X"))
|
||||
|
||||
Reference in New Issue
Block a user