mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
added press to run
This commit is contained in:
@@ -248,7 +248,7 @@ You may have noticed that the stop button (`#stop` in the CSS) has `display: non
|
||||
|
||||
We want our Stopwatch widget to have two states. An _unstarted_ state with a Start and Reset button, and a _started_ state with a Stop button.
|
||||
|
||||
There are other differences between the two states. It would be nice if the stopwatch turns green when it is started. And we could make the time text bold, so it is clear it is running. It's possible to do this in code, but
|
||||
There are other visual differences between the two states. When a stopwatch is running it should have a green background and bold text.
|
||||
|
||||
|
||||
```{.textual path="docs/examples/introduction/stopwatch04.py" title="stopwatch04.py" press="tab,enter"}
|
||||
|
||||
@@ -57,6 +57,7 @@ from .drivers.headless_driver import HeadlessDriver
|
||||
from .features import FeatureFlag, parse_features
|
||||
from .file_monitor import FileMonitor
|
||||
from .geometry import Offset, Region, Size
|
||||
from .messages import CallbackType
|
||||
from .reactive import Reactive
|
||||
from .renderables.blank import Blank
|
||||
from .screen import Screen
|
||||
@@ -560,6 +561,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
quit_after (float | None, optional): Quit after a given number of seconds, or None
|
||||
to run forever. Defaults to None.
|
||||
headless (bool, optional): Run in "headless" mode (don't write to stdout).
|
||||
press (str, optional): An iterable of keys to simulate being pressed.
|
||||
|
||||
Returns:
|
||||
ReturnType | None: The return value specified in `App.exit` or None if exit wasn't called.
|
||||
@@ -574,17 +576,24 @@ class App(Generic[ReturnType], DOMNode):
|
||||
if quit_after is not None:
|
||||
self.set_timer(quit_after, self.shutdown)
|
||||
if press is not None:
|
||||
app = self
|
||||
|
||||
async def press_keys(app: App):
|
||||
async def press_keys() -> None:
|
||||
"""A task to send key events."""
|
||||
assert press
|
||||
await asyncio.sleep(0.05)
|
||||
driver = app._driver
|
||||
assert driver is not None
|
||||
for key in press:
|
||||
print(f"press {key!r}")
|
||||
await app.post_message(events.Key(self, key))
|
||||
await asyncio.sleep(0.01)
|
||||
driver.send_event(events.Key(self, key))
|
||||
await asyncio.sleep(0.02)
|
||||
|
||||
self.call_later(lambda: asyncio.create_task(press_keys(self)))
|
||||
async def press_keys_task():
|
||||
"""Press some keys in the background."""
|
||||
asyncio.create_task(press_keys())
|
||||
|
||||
await self.process_messages(ready_callback=press_keys_task)
|
||||
else:
|
||||
await self.process_messages()
|
||||
|
||||
if _ASYNCIO_GET_EVENT_LOOP_IS_DEPRECATED:
|
||||
@@ -933,7 +942,9 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.error_console.print(renderable)
|
||||
self._exit_renderables.clear()
|
||||
|
||||
async def process_messages(self) -> None:
|
||||
async def process_messages(
|
||||
self, ready_callback: CallbackType | None = None
|
||||
) -> None:
|
||||
self._set_active()
|
||||
|
||||
if self.devtools_enabled:
|
||||
@@ -975,6 +986,8 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.refresh()
|
||||
await self.animator.start()
|
||||
await self._ready()
|
||||
if ready_callback is not None:
|
||||
await ready_callback()
|
||||
await process_messages()
|
||||
await self.animator.stop()
|
||||
await self.close_all()
|
||||
@@ -1210,11 +1223,14 @@ class App(Generic[ReturnType], DOMNode):
|
||||
Returns:
|
||||
bool: True if the key was handled by a binding, otherwise False
|
||||
"""
|
||||
print("press", key)
|
||||
try:
|
||||
binding = self.bindings.get_key(key)
|
||||
except NoBinding:
|
||||
print("no binding")
|
||||
return False
|
||||
else:
|
||||
print(binding)
|
||||
await self.action(binding.action)
|
||||
return True
|
||||
|
||||
|
||||
@@ -116,7 +116,8 @@ def import_app(import_name: str) -> App:
|
||||
@run.command("run")
|
||||
@click.argument("import_name", metavar="FILE or FILE:APP")
|
||||
@click.option("--dev", "dev", help="Enable development mode", is_flag=True)
|
||||
def run_app(import_name: str, dev: bool) -> None:
|
||||
@click.option("--press", "press", help="Comma separated keys to simulate press")
|
||||
def run_app(import_name: str, dev: bool, press: str) -> None:
|
||||
"""Run a Textual app.
|
||||
|
||||
The code to run may be given as a path (ending with .py) or as a Python
|
||||
@@ -156,7 +157,8 @@ def run_app(import_name: str, dev: bool) -> None:
|
||||
console.print(str(error))
|
||||
sys.exit(1)
|
||||
|
||||
app.run()
|
||||
press_keys = press.split(",") if press else None
|
||||
app.run(press=press_keys)
|
||||
|
||||
|
||||
@run.command("borders")
|
||||
|
||||
@@ -228,7 +228,7 @@ class DOMNode(MessagePump):
|
||||
while node and not isinstance(node, Screen):
|
||||
node = node._parent
|
||||
if not isinstance(node, Screen):
|
||||
raise NoScreen("{self} has no screen")
|
||||
raise NoScreen(f"{self} has no screen")
|
||||
return node
|
||||
|
||||
@property
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncio import Lock
|
||||
from itertools import islice
|
||||
from fractions import Fraction
|
||||
from operator import attrgetter
|
||||
@@ -124,6 +125,8 @@ class Widget(DOMNode):
|
||||
|
||||
self._styles_cache = StylesCache()
|
||||
|
||||
self._lock = Lock()
|
||||
|
||||
super().__init__(
|
||||
name=name,
|
||||
id=id,
|
||||
@@ -1159,13 +1162,18 @@ class Widget(DOMNode):
|
||||
Args:
|
||||
event (events.Idle): Idle event.
|
||||
"""
|
||||
if self._parent is not None:
|
||||
if self._parent is not None and not self._closing:
|
||||
try:
|
||||
screen = self.screen
|
||||
except NoScreen:
|
||||
pass
|
||||
else:
|
||||
if self._repaint_required:
|
||||
self._repaint_required = False
|
||||
self.screen.post_message_no_wait(messages.Update(self, self))
|
||||
screen.post_message_no_wait(messages.Update(self, self))
|
||||
if self._layout_required:
|
||||
self._layout_required = False
|
||||
self.screen.post_message_no_wait(messages.Layout(self))
|
||||
screen.post_message_no_wait(messages.Layout(self))
|
||||
|
||||
def focus(self) -> None:
|
||||
"""Give input focus to this widget."""
|
||||
|
||||
Reference in New Issue
Block a user