Render images via Kitty's Terminal Graphics Protocol with Rich and Textual
Project description
textual-kitty
Render images in the terminal with Textual and rich.
textual-kitty provides a rich renderable and a Textual Widget utilizing the Terminal Graphics Protocol to display images in terminals. For terminals not supporting the TGP fallback rendering as unicode characters is available.
Supported Terminals
The Terminal Graphics Protocol was introduced by Kitty is is fully supported in this terminal. WezTerm has a mostly complete implementation. Konsole and wayst have partial support.
However, this module was only tested with Kitty. Feedback for other terminals is welcome.
Installing
Install textual-kitty via pip:
pip install textual-kitty
for the rich renderable, or
pip install textual-kitty[textual]
to also include the Textual Widget's dependencies.
Demo
With the package installed, run
python -m textual_kitty rich
for a demo of the rich renderable or
python -m textual_kitty textual
for a demo of the Textual Widget.
It'll pick the best available rendering option. If you want to see the other ones, use the -p
argument to specify tgp
, colored-fallback
, or grayscale-fallback
.
See
python -m textual_kitty --help
for more information.
Usage
rich
Just pass a textual_kitty.renderable.Image
instance to a rich function rendering data:
from rich.console import Console
from textual_kitty.rich import Image
console = Console()
console.print(Image("path/to/image.png"))
The Image
constructor accepts a str
or pathlib.Path
with a file path of an image file readable by Pillow or a Pillow Image
instance.
Per default, the image will render full terminal width or the width of the parent container. A width
parameter can be passed to the constructor to overwrite this behavior and explicitly specify the width of the image in terminal cells.
The aspect ratio of the image will be kept in both cases.
textual_kitty.renderable.Image
is automatically set to the best available rendering option.
You can also explicitly choose how to render by using one of textual_kitty.renderable.tgp.Image
, textual_kitty.renderable.fallback.colored.Image
, or textual_kitty.renderable.fallback.grayscale.Image
.
Textual
textual-python provides an Textual Widget
to render images:
from textual.app import App, ComposeResult
from textual_kitty.widget import Image
class ImageApp(App[None]):
def compose(self) -> ComposeResult:
yield Image("path/to/image.png")
ImageApp().run()
The Image
constructor accepts a str
or pathlib.Path
with a file path of an image file readable by Pillow or a Pillow Image
instance.
Additionally, the image can be set with the image
property of an Image
instance:
from textual.app import App, ComposeResult
from textual_kitty.textual import Image
class ImageApp(App[None]):
def compose(self) -> ComposeResult:
image = Image()
image.image = "path/to/image.png"
yield image
ImageApp().run()
If another image was set before, the Widget updates to display the new data.
The Image
constructor accepts a load_async
parameter. If set to True
, the first render of the image (and subsequent ones after a resize) will not actually render the image, but start processing the image data asynchronously. The Widget will update itself when this is done to show the image. A loading indicator is shown during processing. This helps to keep the app responsive if large images are passed to this class.
But it does come with the overhead of double the update cycles and running asynchronous tasks.
This again uses the best available rendering option. To override this behavior, specify image_renderable_type
in the widget's constructor as one of textual_kitty.renderable.tgp.Image
, textual_kitty.renderable.fallback.colored.Image
, or textual_kitty.renderable.fallback.grayscale.Image
.
Please note determining the best available rendering option queries the terminal. This means data is sent to and read from it.
As Textual starts threads to handle input and output, this query doesn't work anymore once the Textual app is started.
In practice, this means textual_kitty.renderable
needs to be imported before Textual runs (which should be the case in most use cases anyway).
Contribution
If you find this module helpful, please leave this repository a star.
For now, I just moved this functionality from a private project to a public GitHub repo/PyPI package. It works fine for my use case, please fill a bug ticket if you encounter unexpected behavior.
And, of course, pull requests are welcome.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file textual_kitty-0.3.0.tar.gz
.
File metadata
- Download URL: textual_kitty-0.3.0.tar.gz
- Upload date:
- Size: 11.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d6effb4759bae13aa93c27c943b95a19c289f030f3ca65674d45d6f9ab5cb37f |
|
MD5 | 296da95870b952b96055bb4664b03d76 |
|
BLAKE2b-256 | 18fcddc59efa80fcce62d7379d3f9c7f7c9c5f0a4620569010277d67879feaa5 |
File details
Details for the file textual_kitty-0.3.0-py3-none-any.whl
.
File metadata
- Download URL: textual_kitty-0.3.0-py3-none-any.whl
- Upload date:
- Size: 9.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 834697281ddb8e59b661183cea9558261f633b33239f95a50d75f0dd5e937d39 |
|
MD5 | abcb0328e32e362e98571eead8ffc62c |
|
BLAKE2b-256 | ef0b2bea1846458edc1a0dd5e9df8fa5248ddd3f3e424a3a6bbdc8aad479d8e6 |