296 lines
9 KiB
Python
296 lines
9 KiB
Python
#
|
|
# 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.
|
|
#
|
|
|
|
import Tkinter, Image
|
|
|
|
##
|
|
# The <b>ImageTk</b> module contains support to create and modify
|
|
# Tkinter <b>BitmapImage</b> and <b>PhotoImage</b> objects.
|
|
# <p>
|
|
# For examples, see the demo programs in the <i>Scripts</i>
|
|
# directory.
|
|
##
|
|
|
|
# --------------------------------------------------------------------
|
|
# Check for Tkinter interface hooks
|
|
|
|
_pilbitmap_ok = None
|
|
|
|
def _pilbitmap_check():
|
|
global _pilbitmap_ok
|
|
if _pilbitmap_ok is None:
|
|
try:
|
|
im = Image.new("1", (1,1))
|
|
Tkinter.BitmapImage(data="PIL:%d" % im.im.id)
|
|
_pilbitmap_ok = 1
|
|
except Tkinter.TclError:
|
|
_pilbitmap_ok = 0
|
|
return _pilbitmap_ok
|
|
|
|
# --------------------------------------------------------------------
|
|
# PhotoImage
|
|
|
|
##
|
|
# Creates 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.
|
|
|
|
class PhotoImage:
|
|
|
|
##
|
|
# Create a photo image object. The constructor takes either
|
|
# a PIL image, or a mode and a size. Alternatively, you can
|
|
# use the <b>file</b> or <b>data</b> options to initialize
|
|
# the photo image object.
|
|
# <p>
|
|
# @def __init__(image=None, size=None, **options)
|
|
# @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.
|
|
# @keyparam file A filename to load the image from (using
|
|
# Image.open(file)).
|
|
# @keyparam data An 8-bit string containing image data (as
|
|
# loaded from an image file).
|
|
|
|
def __init__(self, image=None, size=None, **kw):
|
|
|
|
# Tk compatibility: file or data
|
|
if image is None:
|
|
if kw.has_key("file"):
|
|
image = Image.open(kw["file"])
|
|
del kw["file"]
|
|
elif kw.has_key("data"):
|
|
from StringIO import StringIO
|
|
image = Image.open(StringIO(kw["data"]))
|
|
del kw["data"]
|
|
|
|
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:
|
|
mode = "RGB" # default
|
|
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 = apply(Tkinter.PhotoImage, (), kw)
|
|
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:
|
|
pass # ignore internal errors
|
|
|
|
##
|
|
# 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).
|
|
|
|
def __str__(self):
|
|
return str(self.__photo)
|
|
|
|
##
|
|
# Get the width of the image.
|
|
#
|
|
# @return The width, in pixels.
|
|
|
|
def width(self):
|
|
return self.__size[0]
|
|
|
|
##
|
|
# Get the height of the image.
|
|
#
|
|
# @return The height, in pixels.
|
|
|
|
def height(self):
|
|
return self.__size[1]
|
|
|
|
##
|
|
# 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 A 4-tuple defining the left, upper, right, and
|
|
# lower pixel coordinate. If None is given instead of a
|
|
# tuple, all of the image is assumed.
|
|
|
|
def paste(self, im, box=None):
|
|
|
|
# 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)
|
|
image.convert2(block, image) # convert directly between buffers
|
|
|
|
tk = self.__photo.tk
|
|
|
|
try:
|
|
tk.call("PyImagingPhoto", self.__photo, block.id)
|
|
except Tkinter.TclError, v:
|
|
# activate Tkinter hook
|
|
try:
|
|
import _imagingtk
|
|
try:
|
|
_imagingtk.tkinit(tk.interpaddr(), 1)
|
|
except AttributeError:
|
|
_imagingtk.tkinit(id(tk), 0)
|
|
tk.call("PyImagingPhoto", self.__photo, block.id)
|
|
except (ImportError, AttributeError, Tkinter.TclError):
|
|
raise # configuration problem; cannot attach to Tkinter
|
|
|
|
# --------------------------------------------------------------------
|
|
# BitmapImage
|
|
|
|
##
|
|
# Create a Tkinter-compatible bitmap image. This can be used
|
|
# everywhere Tkinter expects an image object.
|
|
|
|
class BitmapImage:
|
|
|
|
##
|
|
# Create a Tkinter-compatible bitmap image.
|
|
# <p>
|
|
# 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 <b>foreground</b>,
|
|
# which is used to specify the colour for the non-transparent
|
|
# parts. See the Tkinter documentation for information on how to
|
|
# specify colours.
|
|
#
|
|
# @def __init__(image=None, **options)
|
|
# @param image A PIL image.
|
|
|
|
def __init__(self, image=None, **kw):
|
|
|
|
# Tk compatibility: file or data
|
|
if image is None:
|
|
if kw.has_key("file"):
|
|
image = Image.open(kw["file"])
|
|
del kw["file"]
|
|
elif kw.has_key("data"):
|
|
from StringIO import StringIO
|
|
image = Image.open(StringIO(kw["data"]))
|
|
del kw["data"]
|
|
|
|
self.__mode = image.mode
|
|
self.__size = image.size
|
|
|
|
if _pilbitmap_check():
|
|
# fast way (requires the pilbitmap booster patch)
|
|
image.load()
|
|
kw["data"] = "PIL:%d" % image.im.id
|
|
self.__im = image # must keep a reference
|
|
else:
|
|
# slow but safe way
|
|
kw["data"] = image.tobitmap()
|
|
self.__photo = apply(Tkinter.BitmapImage, (), kw)
|
|
|
|
def __del__(self):
|
|
name = self.__photo.name
|
|
self.__photo.name = None
|
|
try:
|
|
self.__photo.tk.call("image", "delete", name)
|
|
except:
|
|
pass # ignore internal errors
|
|
|
|
##
|
|
# Get the width of the image.
|
|
#
|
|
# @return The width, in pixels.
|
|
|
|
def width(self):
|
|
return self.__size[0]
|
|
|
|
##
|
|
# Get the height of the image.
|
|
#
|
|
# @return The height, in pixels.
|
|
|
|
def height(self):
|
|
return self.__size[1]
|
|
|
|
##
|
|
# 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).
|
|
|
|
def __str__(self):
|
|
return str(self.__photo)
|
|
|
|
##
|
|
# Copies the contents of a PhotoImage to a PIL image memory.
|
|
|
|
def getimage(photo):
|
|
photo.tk.call("PyImagingPhotoGet", photo)
|
|
|
|
# --------------------------------------------------------------------
|
|
# Helper for the Image.show method.
|
|
|
|
def _show(image, title):
|
|
|
|
class UI(Tkinter.Label):
|
|
def __init__(self, master, im):
|
|
if im.mode == "1":
|
|
self.image = BitmapImage(im, foreground="white", master=master)
|
|
else:
|
|
self.image = PhotoImage(im, master=master)
|
|
Tkinter.Label.__init__(self, master, image=self.image,
|
|
bg="black", bd=0)
|
|
|
|
if not Tkinter._default_root:
|
|
raise IOError, "tkinter not initialized"
|
|
top = Tkinter.Toplevel()
|
|
if title:
|
|
top.title(title)
|
|
UI(top, image).pack()
|