Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources]

David Ascher, Alex Martelli, Anna Ravenscroft

نسخه متنی -صفحه : 394/ 232
نمايش فراداده

Recipe 11.6. Embedding Inline GIFs Using Tkinter

Credit: Brent Burley

Problem

You need to embed GIF images inside your source codefor use in Tkinter buttons, labels, and so onto make toolbars and the like without worrying about installing the right icon files.

Solution

A lively Tkinter GUI can include many small images. However, you don't want to require that a small GIF file be present for each of these images. Ensuring the presence of many small files is a bother, and if they're missing, your GUI may be unusable. Fortunately, you can construct Tkinter PhotoImage objects with inline data. It's easy to convert a GIF to inline form as Python source code, with a little script or snippet that you can save and run separately.

import base64
print "icon='''\\\n" + base64.encodestring
(open("icon.gif").read( )) + "'''"

This emits to standard output a lot of strange-looking "text", which you can capture (typically using your shell's facilities for output redirection, or with copy and paste) and split into lines of reasonable length:

icon=
'''R0lGODdhFQAVAPMAAAQ2PESapISCBASCBMTCxPxmNCQiJJya/ISChGRmzPz+/PxmzDQyZ
DQyZDQyZDQyZCwAAAAAFQAVAAAElJDISau9Vh2WMD0gqHHel
JwnsXVloqDd2hrMm8pYYiSHYfMMRm
53ULlQHGFFx1MZCciUiVOsPmEkKNVp3UBhJ4Ohy1
UxerSgJGZMMBbcBACQlVhRiHvaUsXHgywTdyc
LdxyB gm1vcTyIZW4MeU6NgQEBXEGRcQcIlwQIAwEHoioCAgWmCZ0Iq5+
hA6wIpqislgGhthEAOw==
'''

Now, you can use this Python-inlined data in Tkinter:

import Tkinter
if _ _name_ _ == '_ _main_ _':
root = Tkinter.Tk( )
iconImage = Tkinter.PhotoImage(master=root, data=icon)
Tkinter.Button(image=iconImage).pack( )

Discussion

The basic technique is to encode the GIF with the standard Python module base64 and store the results as a string literal in the Python code. At runtime, the Python code passes that string object to Tkinter's PhotoImage. The current release of PhotoImage supports GIF and PPM, but inline data is supported only for GIF. To convert between image formats, see Recipe 11.7. Of course, you can use file='filename', instead of data=string, for either GIF or PPM, if your image data is indeed in a file.

You must keep a reference to the PhotoImage object yourself; that reference is not kept by the Tkinter widget. If you pass the object to Button and forget it, you will become frustrated! Here's an easy workaround for this minor annoyance:

def makeImageWidget(icondata, *args, **kwds):
if args:
klass = args.pop(0)
else:
klass = Tkinter.Button
class Widget(klass):
def _ _init_ _(self, image, *args, **kwds):
kwds['image'] = image
klass._ _init_ _(self, *args, **kwds)
self._ _image = image
return Widget(Tkinter.PhotoImage(data=icondata), *args, **kwds)

Using this handy makeImageWidget function, the equivalent of the example in the recipe becomes:

makeImageWidget(icon).pack( )

The master argument on PhotoImage is optional; it defaults to the default application window. If you create a new application window (by calling Tk again), you must create your images in that context and supply the master argument, so the makeImageWidget function has to be updated to let you optionally pass the master argument to the PhotoImage constructor. However, most applications do not require this refinement.

See Also

Information about Tkinter can be obtained from a variety of sources, such as Fredrik Lundh, An Introduction to Tkinter (PythonWare: http://www.pythonware.com/library), New Mexico Tech's Tkinter Reference (http://www.nmt.edu/tcc/help/lang/python/docsl), Python in a Nutshell, and various other books.