mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
eof
This commit is contained in:
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
|
||||||
|
## [0.1.13] - 2022-01-01
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed spurious characters when exiting app
|
||||||
|
- Fixed increasing delay when exiting
|
||||||
|
|
||||||
## [0.1.12] - 2021-09-20
|
## [0.1.12] - 2021-09-20
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "textual"
|
name = "textual"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
homepage = "https://github.com/willmcgugan/textual"
|
homepage = "https://github.com/willmcgugan/textual"
|
||||||
description = "Text User Interface using Rich"
|
description = "Text User Interface using Rich"
|
||||||
authors = ["Will McGugan <willmcgugan@gmail.com>"]
|
authors = ["Will McGugan <willmcgugan@gmail.com>"]
|
||||||
|
|||||||
@@ -113,10 +113,10 @@ class Animator:
|
|||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
if self._timer_task is None:
|
if self._timer_task is None:
|
||||||
self._timer_task = asyncio.get_event_loop().create_task(self._timer.run())
|
self._timer_task = self._timer.start()
|
||||||
|
|
||||||
async def stop(self) -> None:
|
async def stop(self) -> None:
|
||||||
self._timer.stop()
|
await self._timer.stop()
|
||||||
if self._timer_task:
|
if self._timer_task:
|
||||||
await self._timer_task
|
await self._timer_task
|
||||||
self._timer_task = None
|
self._timer_task = None
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import selectors
|
|||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import termios
|
import termios
|
||||||
from time import time
|
|
||||||
import tty
|
import tty
|
||||||
from typing import Any, TYPE_CHECKING
|
from typing import Any, TYPE_CHECKING
|
||||||
from threading import Event, Thread
|
from threading import Event, Thread
|
||||||
@@ -15,12 +14,14 @@ from threading import Event, Thread
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
|
from . import log
|
||||||
|
|
||||||
from . import events
|
from . import events
|
||||||
from .driver import Driver
|
from .driver import Driver
|
||||||
from .geometry import Size
|
from .geometry import Size
|
||||||
from ._types import MessageTarget
|
from ._types import MessageTarget
|
||||||
from ._xterm_parser import XTermParser
|
from ._xterm_parser import XTermParser
|
||||||
|
from ._profile import timer
|
||||||
|
|
||||||
|
|
||||||
class LinuxDriver(Driver):
|
class LinuxDriver(Driver):
|
||||||
@@ -53,7 +54,7 @@ class LinuxDriver(Driver):
|
|||||||
write("\x1b[?1006h") # SET_SGR_EXT_MODE_MOUSE
|
write("\x1b[?1006h") # SET_SGR_EXT_MODE_MOUSE
|
||||||
|
|
||||||
# write("\x1b[?1007h")
|
# write("\x1b[?1007h")
|
||||||
# self.console.file.flush()
|
self.console.file.flush()
|
||||||
|
|
||||||
# Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr
|
# Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr
|
||||||
# extensions.
|
# extensions.
|
||||||
@@ -64,7 +65,7 @@ class LinuxDriver(Driver):
|
|||||||
write("\x1b[?1003l") #
|
write("\x1b[?1003l") #
|
||||||
write("\x1b[?1015l")
|
write("\x1b[?1015l")
|
||||||
write("\x1b[?1006l")
|
write("\x1b[?1006l")
|
||||||
# self.console.file.flush()
|
self.console.file.flush()
|
||||||
|
|
||||||
def start_application_mode(self):
|
def start_application_mode(self):
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ class LinuxDriver(Driver):
|
|||||||
|
|
||||||
self.console.show_cursor(False)
|
self.console.show_cursor(False)
|
||||||
self.console.file.write("\033[?1003h\n")
|
self.console.file.write("\033[?1003h\n")
|
||||||
|
self.console.file.flush()
|
||||||
self._key_thread = Thread(
|
self._key_thread = Thread(
|
||||||
target=self.run_input_thread, args=(asyncio.get_event_loop(),)
|
target=self.run_input_thread, args=(asyncio.get_event_loop(),)
|
||||||
)
|
)
|
||||||
@@ -145,25 +146,30 @@ class LinuxDriver(Driver):
|
|||||||
if not self.exit_event.is_set():
|
if not self.exit_event.is_set():
|
||||||
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
|
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
|
||||||
self._disable_mouse_support()
|
self._disable_mouse_support()
|
||||||
|
termios.tcflush(self.fileno, termios.TCIFLUSH)
|
||||||
self.exit_event.set()
|
self.exit_event.set()
|
||||||
if self._key_thread is not None:
|
if self._key_thread is not None:
|
||||||
self._key_thread.join()
|
self._key_thread.join()
|
||||||
except Exception:
|
except Exception as error:
|
||||||
# TODO: log this
|
# TODO: log this
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def stop_application_mode(self) -> None:
|
def stop_application_mode(self) -> None:
|
||||||
|
|
||||||
self.disable_input()
|
with timer("disable_input"):
|
||||||
|
self.disable_input()
|
||||||
|
|
||||||
if self.attrs_before is not None:
|
with timer("tcsetattr"):
|
||||||
try:
|
if self.attrs_before is not None:
|
||||||
termios.tcsetattr(self.fileno, termios.TCSANOW, self.attrs_before)
|
try:
|
||||||
except termios.error:
|
termios.tcsetattr(self.fileno, termios.TCSANOW, self.attrs_before)
|
||||||
pass
|
except termios.error:
|
||||||
|
pass
|
||||||
|
|
||||||
self.console.set_alt_screen(False)
|
with timer("set_alt_screen False, show cursor"):
|
||||||
self.console.show_cursor(True)
|
with self.console:
|
||||||
|
self.console.set_alt_screen(False)
|
||||||
|
self.console.show_cursor(True)
|
||||||
|
|
||||||
def run_input_thread(self, loop) -> None:
|
def run_input_thread(self, loop) -> None:
|
||||||
try:
|
try:
|
||||||
@@ -180,7 +186,7 @@ class LinuxDriver(Driver):
|
|||||||
|
|
||||||
def more_data() -> bool:
|
def more_data() -> bool:
|
||||||
"""Check if there is more data to parse."""
|
"""Check if there is more data to parse."""
|
||||||
for key, events in selector.select(0.1):
|
for key, events in selector.select(0.01):
|
||||||
if events:
|
if events:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -199,11 +205,11 @@ class LinuxDriver(Driver):
|
|||||||
unicode_data = decode(read(fileno, 1024))
|
unicode_data = decode(read(fileno, 1024))
|
||||||
for event in parser.feed(unicode_data):
|
for event in parser.feed(unicode_data):
|
||||||
self.process_event(event)
|
self.process_event(event)
|
||||||
except Exception:
|
except Exception as error:
|
||||||
pass
|
log(error)
|
||||||
# TODO: log
|
|
||||||
finally:
|
finally:
|
||||||
selector.close()
|
with timer("selector.close"):
|
||||||
|
selector.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import weakref
|
import weakref
|
||||||
from asyncio import CancelledError, Event, TimeoutError, wait_for
|
from asyncio import (
|
||||||
|
get_event_loop,
|
||||||
|
CancelledError,
|
||||||
|
Event,
|
||||||
|
sleep,
|
||||||
|
Task,
|
||||||
|
)
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
from typing import Awaitable, Callable, Union
|
from typing import Awaitable, Callable, Union
|
||||||
|
|
||||||
@@ -42,7 +48,6 @@ class Timer:
|
|||||||
self._callback = callback
|
self._callback = callback
|
||||||
self._repeat = repeat
|
self._repeat = repeat
|
||||||
self._skip = skip
|
self._skip = skip
|
||||||
self._stop_event = Event()
|
|
||||||
self._active = Event()
|
self._active = Event()
|
||||||
if not pause:
|
if not pause:
|
||||||
self._active.set()
|
self._active.set()
|
||||||
@@ -59,22 +64,33 @@ class Timer:
|
|||||||
raise EventTargetGone()
|
raise EventTargetGone()
|
||||||
return target
|
return target
|
||||||
|
|
||||||
def stop(self) -> None:
|
def start(self) -> Task:
|
||||||
self._active.set()
|
"""Start the timer return the task.
|
||||||
self._stop_event.set()
|
|
||||||
|
Returns:
|
||||||
|
Task: A Task instance for the timer.
|
||||||
|
"""
|
||||||
|
self._task = get_event_loop().create_task(self._run())
|
||||||
|
return self._task
|
||||||
|
|
||||||
|
async def stop(self) -> None:
|
||||||
|
"""Stop the timer, and block until it exists."""
|
||||||
|
self._task.cancel()
|
||||||
|
await self._task
|
||||||
|
|
||||||
def pause(self) -> None:
|
def pause(self) -> None:
|
||||||
|
"""Pause the timer."""
|
||||||
self._active.clear()
|
self._active.clear()
|
||||||
|
|
||||||
def resume(self) -> None:
|
def resume(self) -> None:
|
||||||
|
"""Result a paused timer."""
|
||||||
self._active.set()
|
self._active.set()
|
||||||
|
|
||||||
async def run(self) -> None:
|
async def _run(self) -> None:
|
||||||
|
"""Run the timer."""
|
||||||
count = 0
|
count = 0
|
||||||
_repeat = self._repeat
|
_repeat = self._repeat
|
||||||
_interval = self._interval
|
_interval = self._interval
|
||||||
_wait = self._stop_event.wait
|
|
||||||
_wait_active = self._active.wait
|
|
||||||
start = monotonic()
|
start = monotonic()
|
||||||
try:
|
try:
|
||||||
while _repeat is None or count <= _repeat:
|
while _repeat is None or count <= _repeat:
|
||||||
@@ -82,11 +98,9 @@ class Timer:
|
|||||||
if self._skip and next_timer < monotonic():
|
if self._skip and next_timer < monotonic():
|
||||||
count += 1
|
count += 1
|
||||||
continue
|
continue
|
||||||
try:
|
wait_time = max(0, next_timer - monotonic())
|
||||||
if await wait_for(_wait(), max(0, next_timer - monotonic())):
|
if wait_time:
|
||||||
break
|
await sleep(wait_time)
|
||||||
except TimeoutError:
|
|
||||||
pass
|
|
||||||
event = events.Timer(
|
event = events.Timer(
|
||||||
self.sender, timer=self, count=count, callback=self._callback
|
self.sender, timer=self, count=count, callback=self._callback
|
||||||
)
|
)
|
||||||
@@ -95,7 +109,6 @@ class Timer:
|
|||||||
await self.target.post_message(event)
|
await self.target.post_message(event)
|
||||||
except EventTargetGone:
|
except EventTargetGone:
|
||||||
break
|
break
|
||||||
|
await self._active.wait()
|
||||||
await _wait_active()
|
|
||||||
except CancelledError:
|
except CancelledError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -127,8 +127,7 @@ class MessagePump:
|
|||||||
name: str | None = None,
|
name: str | None = None,
|
||||||
) -> Timer:
|
) -> Timer:
|
||||||
timer = Timer(self, delay, self, name=name, callback=callback, repeat=0)
|
timer = Timer(self, delay, self, name=name, callback=callback, repeat=0)
|
||||||
timer_task = asyncio.get_event_loop().create_task(timer.run())
|
self._child_tasks.add(timer.start())
|
||||||
self._child_tasks.add(timer_task)
|
|
||||||
return timer
|
return timer
|
||||||
|
|
||||||
def set_interval(
|
def set_interval(
|
||||||
@@ -142,7 +141,7 @@ class MessagePump:
|
|||||||
timer = Timer(
|
timer = Timer(
|
||||||
self, interval, self, name=name, callback=callback, repeat=repeat or None
|
self, interval, self, name=name, callback=callback, repeat=repeat or None
|
||||||
)
|
)
|
||||||
self._child_tasks.add(asyncio.get_event_loop().create_task(timer.run()))
|
self._child_tasks.add(timer.start())
|
||||||
return timer
|
return timer
|
||||||
|
|
||||||
async def call_later(self, callback: Callable, *args, **kwargs) -> None:
|
async def call_later(self, callback: Callable, *args, **kwargs) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user