Pillow/src/PIL/ImageTk.py

305 lines
8.9 KiB
Python
Raw Normal View History

2010-07-31 06:52:47 +04:00
#
# The Python Imaging Library.
# $Id$
#
# a Tk display interface
#
# History:
# 96-04-08 fl Created
# 96-09-06 fl Added getimage method
# 96-11-01 fl Rewritten, removed image attribute and crop method
# 97-05-09 fl Use PyImagingPaste method instead of image type
# 97-05-12 fl Minor tweaks to match the IFUNC95 interface
# 97-05-17 fl Support the "pilbitmap" booster patch
# 97-06-05 fl Added file= and data= argument to image constructors
# 98-03-09 fl Added width and height methods to Image classes
# 98-07-02 fl Use default mode for "P" images without palette attribute
# 98-07-02 fl Explicitly destroy Tkinter image objects
# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch)
# 99-07-26 fl Automatically hook into Tkinter (if possible)
# 99-08-15 fl Hook uses _imagingtk instead of _imaging
#
# Copyright (c) 1997-1999 by Secret Labs AB
# Copyright (c) 1996-1997 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
2019-09-26 15:12:28 +03:00
import tkinter
from io import BytesIO
from . import Image
2022-04-05 16:33:20 +03:00
from ._deprecate import deprecate
2010-07-31 06:52:47 +04:00
# --------------------------------------------------------------------
# Check for Tkinter interface hooks
_pilbitmap_ok = None
2014-08-26 17:47:10 +04:00
2010-07-31 06:52:47 +04:00
def _pilbitmap_check():
global _pilbitmap_ok
if _pilbitmap_ok is None:
try:
2014-08-26 17:47:10 +04:00
im = Image.new("1", (1, 1))
tkinter.BitmapImage(data=f"PIL:{im.im.id}")
2010-07-31 06:52:47 +04:00
_pilbitmap_ok = 1
2012-10-17 06:58:29 +04:00
except tkinter.TclError:
2010-07-31 06:52:47 +04:00
_pilbitmap_ok = 0
return _pilbitmap_ok
2016-05-24 10:36:14 +03:00
2016-05-01 13:55:12 +03:00
def _get_image_from_kw(kw):
source = None
if "file" in kw:
source = kw.pop("file")
elif "data" in kw:
source = BytesIO(kw.pop("data"))
if source:
return Image.open(source)
2014-08-26 17:47:10 +04:00
def _pyimagingtkcall(command, photo, id):
tk = photo.tk
try:
tk.call(command, photo, id)
except tkinter.TclError:
# activate Tkinter hook
2022-02-07 13:06:36 +03:00
# may raise an error if it cannot attach to Tkinter
from . import _imagingtk
try:
2022-02-07 13:06:36 +03:00
if hasattr(tk, "interp"):
# Required for PyPy, which always has CFFI installed
from cffi import FFI
ffi = FFI()
# PyPy is using an FFI CDATA element
# (Pdb) self.tk.interp
# <cdata 'Tcl_Interp *' 0x3061b50>
_imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1)
else:
_imagingtk.tkinit(tk.interpaddr(), 1)
except AttributeError:
_imagingtk.tkinit(id(tk), 0)
tk.call(command, photo, id)
2010-07-31 06:52:47 +04:00
# --------------------------------------------------------------------
# PhotoImage
2019-03-21 16:28:20 +03:00
class PhotoImage:
2013-10-14 09:09:02 +04:00
"""
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.
2020-09-01 20:16:46 +03:00
Alternatively, you can use the ``file`` or ``data`` options to initialize
2013-10-14 09:09:02 +04:00
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).
"""
2010-07-31 06:52:47 +04:00
def __init__(self, image=None, size=None, **kw):
# Tk compatibility: file or data
if image is None:
2016-05-01 13:55:12 +03:00
image = _get_image_from_kw(kw)
2010-07-31 06:52:47 +04:00
if hasattr(image, "mode") and hasattr(image, "size"):
# got an image instead of a mode
mode = image.mode
if mode == "P":
# palette mapped data
image.load()
try:
mode = image.palette.mode
except AttributeError:
2014-08-26 17:47:10 +04:00
mode = "RGB" # default
2010-07-31 06:52:47 +04:00
size = image.size
kw["width"], kw["height"] = size
else:
mode = image
image = None
if mode not in ["1", "L", "RGB", "RGBA"]:
mode = Image.getmodebase(mode)
self.__mode = mode
self.__size = size
self.__photo = tkinter.PhotoImage(**kw)
2010-07-31 06:52:47 +04:00
self.tk = self.__photo.tk
if image:
self.paste(image)
def __del__(self):
name = self.__photo.name
self.__photo.name = None
try:
self.__photo.tk.call("image", "delete", name)
except Exception:
2014-08-26 17:47:10 +04:00
pass # ignore internal errors
2010-07-31 06:52:47 +04:00
def __str__(self):
2013-10-14 09:09:02 +04:00
"""
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).
"""
2010-07-31 06:52:47 +04:00
return str(self.__photo)
def width(self):
2013-10-14 09:09:02 +04:00
"""
Get the width of the image.
:return: The width, in pixels.
"""
2010-07-31 06:52:47 +04:00
return self.__size[0]
def height(self):
2013-10-14 09:09:02 +04:00
"""
Get the height of the image.
:return: The height, in pixels.
"""
2010-07-31 06:52:47 +04:00
return self.__size[1]
def paste(self, im, box=None):
2013-10-14 09:09:02 +04:00
"""
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.
:param box: Deprecated. This parameter will be removed in Pillow 10
(2023-07-01).
2013-10-14 09:09:02 +04:00
"""
2010-07-31 06:52:47 +04:00
if box is not None:
2022-04-05 16:33:20 +03:00
deprecate("The box parameter", 10, None)
2010-07-31 06:52:47 +04:00
# convert to blittable
im.load()
image = im.im
if image.isblock() and im.mode == self.__mode:
block = image
else:
block = image.new_block(self.__mode, im.size)
2014-08-26 17:47:10 +04:00
image.convert2(block, image) # convert directly between buffers
2010-07-31 06:52:47 +04:00
_pyimagingtkcall("PyImagingPhoto", self.__photo, block.id)
2010-07-31 06:52:47 +04:00
2019-03-21 16:28:20 +03:00
2010-07-31 06:52:47 +04:00
# --------------------------------------------------------------------
# BitmapImage
class BitmapImage:
2013-10-14 09:09:02 +04:00
"""
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
2020-09-01 20:16:46 +03:00
used option is ``foreground``, which is used to specify the color for the
2013-10-14 09:09:02 +04:00
non-transparent parts. See the Tkinter documentation for information on
how to specify colours.
:param image: A PIL image.
"""
2010-07-31 06:52:47 +04:00
def __init__(self, image=None, **kw):
# Tk compatibility: file or data
if image is None:
2016-05-01 13:55:12 +03:00
image = _get_image_from_kw(kw)
2010-07-31 06:52:47 +04:00
self.__mode = image.mode
self.__size = image.size
if _pilbitmap_check():
# fast way (requires the pilbitmap booster patch)
image.load()
kw["data"] = f"PIL:{image.im.id}"
2014-08-26 17:47:10 +04:00
self.__im = image # must keep a reference
2010-07-31 06:52:47 +04:00
else:
# slow but safe way
kw["data"] = image.tobitmap()
self.__photo = tkinter.BitmapImage(**kw)
2010-07-31 06:52:47 +04:00
def __del__(self):
name = self.__photo.name
self.__photo.name = None
try:
self.__photo.tk.call("image", "delete", name)
except Exception:
2014-08-26 17:47:10 +04:00
pass # ignore internal errors
2010-07-31 06:52:47 +04:00
def width(self):
2013-10-14 09:09:02 +04:00
"""
Get the width of the image.
:return: The width, in pixels.
"""
2010-07-31 06:52:47 +04:00
return self.__size[0]
def height(self):
2013-10-14 09:09:02 +04:00
"""
Get the height of the image.
:return: The height, in pixels.
"""
2010-07-31 06:52:47 +04:00
return self.__size[1]
def __str__(self):
2013-10-14 09:09:02 +04:00
"""
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).
"""
2010-07-31 06:52:47 +04:00
return str(self.__photo)
def getimage(photo):
2013-10-14 09:09:02 +04:00
"""Copies the contents of a PhotoImage to a PIL image memory."""
2019-04-29 16:43:13 +03:00
im = Image.new("RGBA", (photo.width(), photo.height()))
block = im.im
_pyimagingtkcall("PyImagingPhotoGet", photo, block.id)
2019-04-29 16:43:13 +03:00
return im
2010-07-31 06:52:47 +04:00
2014-08-26 17:47:10 +04:00
2010-07-31 06:52:47 +04:00
def _show(image, title):
2016-05-24 10:36:14 +03:00
"""Helper for the Image.show method."""
2010-07-31 06:52:47 +04:00
2012-10-17 06:58:29 +04:00
class UI(tkinter.Label):
2010-07-31 06:52:47 +04:00
def __init__(self, master, im):
if im.mode == "1":
self.image = BitmapImage(im, foreground="white", master=master)
else:
self.image = PhotoImage(im, master=master)
super().__init__(master, image=self.image, bg="black", bd=0)
2010-07-31 06:52:47 +04:00
2012-10-17 06:58:29 +04:00
if not tkinter._default_root:
raise OSError("tkinter not initialized")
2012-10-17 06:58:29 +04:00
top = tkinter.Toplevel()
2010-07-31 06:52:47 +04:00
if title:
top.title(title)
UI(top, image).pack()