|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
|
import tkinter |
|
from io import BytesIO |
|
from typing import TYPE_CHECKING, Any, cast |
|
|
|
from . import Image, ImageFile |
|
|
|
if TYPE_CHECKING: |
|
from ._typing import CapsuleType |
|
|
|
|
|
|
|
|
|
|
|
def _get_image_from_kw(kw: dict[str, Any]) -> ImageFile.ImageFile | None: |
|
source = None |
|
if "file" in kw: |
|
source = kw.pop("file") |
|
elif "data" in kw: |
|
source = BytesIO(kw.pop("data")) |
|
if not source: |
|
return None |
|
return Image.open(source) |
|
|
|
|
|
def _pyimagingtkcall( |
|
command: str, photo: PhotoImage | tkinter.PhotoImage, ptr: CapsuleType |
|
) -> None: |
|
tk = photo.tk |
|
try: |
|
tk.call(command, photo, repr(ptr)) |
|
except tkinter.TclError: |
|
|
|
|
|
from . import _imagingtk |
|
|
|
_imagingtk.tkinit(tk.interpaddr()) |
|
tk.call(command, photo, repr(ptr)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
class PhotoImage: |
|
""" |
|
A Tkinter-compatible photo image. This can be used |
|
everywhere Tkinter expects an image object. If the image is an RGBA |
|
image, pixels having alpha 0 are treated as transparent. |
|
|
|
The constructor takes either a PIL image, or a mode and a size. |
|
Alternatively, you can use the ``file`` or ``data`` options to initialize |
|
the photo image object. |
|
|
|
:param image: Either a PIL image, or a mode string. If a mode string is |
|
used, a size must also be given. |
|
:param size: If the first argument is a mode string, this defines the size |
|
of the image. |
|
:keyword file: A filename to load the image from (using |
|
``Image.open(file)``). |
|
:keyword data: An 8-bit string containing image data (as loaded from an |
|
image file). |
|
""" |
|
|
|
def __init__( |
|
self, |
|
image: Image.Image | str | None = None, |
|
size: tuple[int, int] | None = None, |
|
**kw: Any, |
|
) -> None: |
|
|
|
if image is None: |
|
image = _get_image_from_kw(kw) |
|
|
|
if image is None: |
|
msg = "Image is required" |
|
raise ValueError(msg) |
|
elif isinstance(image, str): |
|
mode = image |
|
image = None |
|
|
|
if size is None: |
|
msg = "If first argument is mode, size is required" |
|
raise ValueError(msg) |
|
else: |
|
|
|
mode = image.mode |
|
if mode == "P": |
|
|
|
image.apply_transparency() |
|
image.load() |
|
mode = image.palette.mode if image.palette else "RGB" |
|
size = image.size |
|
kw["width"], kw["height"] = size |
|
|
|
if mode not in ["1", "L", "RGB", "RGBA"]: |
|
mode = Image.getmodebase(mode) |
|
|
|
self.__mode = mode |
|
self.__size = size |
|
self.__photo = tkinter.PhotoImage(**kw) |
|
self.tk = self.__photo.tk |
|
if image: |
|
self.paste(image) |
|
|
|
def __del__(self) -> None: |
|
try: |
|
name = self.__photo.name |
|
except AttributeError: |
|
return |
|
self.__photo.name = None |
|
try: |
|
self.__photo.tk.call("image", "delete", name) |
|
except Exception: |
|
pass |
|
|
|
def __str__(self) -> str: |
|
""" |
|
Get the Tkinter photo image identifier. This method is automatically |
|
called by Tkinter whenever a PhotoImage object is passed to a Tkinter |
|
method. |
|
|
|
:return: A Tkinter photo image identifier (a string). |
|
""" |
|
return str(self.__photo) |
|
|
|
def width(self) -> int: |
|
""" |
|
Get the width of the image. |
|
|
|
:return: The width, in pixels. |
|
""" |
|
return self.__size[0] |
|
|
|
def height(self) -> int: |
|
""" |
|
Get the height of the image. |
|
|
|
:return: The height, in pixels. |
|
""" |
|
return self.__size[1] |
|
|
|
def paste(self, im: Image.Image) -> None: |
|
""" |
|
Paste a PIL image into the photo image. Note that this can |
|
be very slow if the photo image is displayed. |
|
|
|
:param im: A PIL image. The size must match the target region. If the |
|
mode does not match, the image is converted to the mode of |
|
the bitmap image. |
|
""" |
|
|
|
ptr = im.getim() |
|
image = im.im |
|
if not image.isblock() or im.mode != self.__mode: |
|
block = Image.core.new_block(self.__mode, im.size) |
|
image.convert2(block, image) |
|
ptr = block.ptr |
|
|
|
_pyimagingtkcall("PyImagingPhoto", self.__photo, ptr) |
|
|
|
|
|
|
|
|
|
|
|
|
|
class BitmapImage: |
|
""" |
|
A Tkinter-compatible bitmap image. This can be used everywhere Tkinter |
|
expects an image object. |
|
|
|
The given image must have mode "1". Pixels having value 0 are treated as |
|
transparent. Options, if any, are passed on to Tkinter. The most commonly |
|
used option is ``foreground``, which is used to specify the color for the |
|
non-transparent parts. See the Tkinter documentation for information on |
|
how to specify colours. |
|
|
|
:param image: A PIL image. |
|
""" |
|
|
|
def __init__(self, image: Image.Image | None = None, **kw: Any) -> None: |
|
|
|
if image is None: |
|
image = _get_image_from_kw(kw) |
|
|
|
if image is None: |
|
msg = "Image is required" |
|
raise ValueError(msg) |
|
self.__mode = image.mode |
|
self.__size = image.size |
|
|
|
self.__photo = tkinter.BitmapImage(data=image.tobitmap(), **kw) |
|
|
|
def __del__(self) -> None: |
|
try: |
|
name = self.__photo.name |
|
except AttributeError: |
|
return |
|
self.__photo.name = None |
|
try: |
|
self.__photo.tk.call("image", "delete", name) |
|
except Exception: |
|
pass |
|
|
|
def width(self) -> int: |
|
""" |
|
Get the width of the image. |
|
|
|
:return: The width, in pixels. |
|
""" |
|
return self.__size[0] |
|
|
|
def height(self) -> int: |
|
""" |
|
Get the height of the image. |
|
|
|
:return: The height, in pixels. |
|
""" |
|
return self.__size[1] |
|
|
|
def __str__(self) -> str: |
|
""" |
|
Get the Tkinter bitmap image identifier. This method is automatically |
|
called by Tkinter whenever a BitmapImage object is passed to a Tkinter |
|
method. |
|
|
|
:return: A Tkinter bitmap image identifier (a string). |
|
""" |
|
return str(self.__photo) |
|
|
|
|
|
def getimage(photo: PhotoImage) -> Image.Image: |
|
"""Copies the contents of a PhotoImage to a PIL image memory.""" |
|
im = Image.new("RGBA", (photo.width(), photo.height())) |
|
|
|
_pyimagingtkcall("PyImagingPhotoGet", photo, im.getim()) |
|
|
|
return im |
|
|
|
|
|
def _show(image: Image.Image, title: str | None) -> None: |
|
"""Helper for the Image.show method.""" |
|
|
|
class UI(tkinter.Label): |
|
def __init__(self, master: tkinter.Toplevel, im: Image.Image) -> None: |
|
self.image: BitmapImage | PhotoImage |
|
if im.mode == "1": |
|
self.image = BitmapImage(im, foreground="white", master=master) |
|
else: |
|
self.image = PhotoImage(im, master=master) |
|
if TYPE_CHECKING: |
|
image = cast(tkinter._Image, self.image) |
|
else: |
|
image = self.image |
|
super().__init__(master, image=image, bg="black", bd=0) |
|
|
|
if not getattr(tkinter, "_default_root"): |
|
msg = "tkinter not initialized" |
|
raise OSError(msg) |
|
top = tkinter.Toplevel() |
|
if title: |
|
top.title(title) |
|
UI(top, image).pack() |
|
|