#
# 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()
