Skip to main content

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.

Demo App

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

textual_kitty-0.3.0.tar.gz (11.3 kB view details)

Uploaded Source

Built Distribution

textual_kitty-0.3.0-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

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

Hashes for textual_kitty-0.3.0.tar.gz
Algorithm Hash digest
SHA256 d6effb4759bae13aa93c27c943b95a19c289f030f3ca65674d45d6f9ab5cb37f
MD5 296da95870b952b96055bb4664b03d76
BLAKE2b-256 18fcddc59efa80fcce62d7379d3f9c7f7c9c5f0a4620569010277d67879feaa5

See more details on using hashes here.

File details

Details for the file textual_kitty-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for textual_kitty-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 834697281ddb8e59b661183cea9558261f633b33239f95a50d75f0dd5e937d39
MD5 abcb0328e32e362e98571eead8ffc62c
BLAKE2b-256 ef0b2bea1846458edc1a0dd5e9df8fa5248ddd3f3e424a3a6bbdc8aad479d8e6

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page