Recipe 11.6. Embedding Inline GIFs Using TkinterCredit: Brent Burley ProblemYou 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. SolutionA 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 DQyZDQyZDQyZCwAAAAAFQAVAAAElJDISau9Vh2WMD0gqHHelJwnsXVloqDd2hrMm8pYYiSHYfMMRm 53ULlQHGFFx1MZCciUiVOsPmEkKNVp3UBhJ4Ohy1UxerSgJGZMMBbcBACQlVhRiHvaUsXHgywTdyc 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( ) DiscussionThe 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 AlsoInformation 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/docs.html), Python in a Nutshell, and various other books. |