Read Programming Python Online

Authors: Mark Lutz

Tags: #COMPUTERS / Programming Languages / Python

Programming Python (64 page)

BOOK: Programming Python
11.01Mb size Format: txt, pdf, ePub
ads

[
33
]
As we’ll see later in this book,
exec
can also be dangerous if it is
running code strings fetched from users or network connections.
That’s not an issue for the hardcoded strings used internally in
this example.

Images

In tkinter, graphical
images are displayed by creating independent
PhotoImage
or
Bitmap
Image
objects, and then attaching those
image objects to other widgets via
image
attribute settings. Buttons, labels,
canvases, text, and menus can display images by associating prebuilt image
objects in this way. To illustrate,
Example 8-37
throws a picture up on
a button.

Example 8-37. PP4E\Gui\Tour\imgButton.py

gifdir = "../gifs/"
from tkinter import *
win = Tk()
igm = PhotoImage(file=gifdir + "ora-pp.gif")
Button(win, image=igm).pack()
win.mainloop()

I could try to come up with a simpler example, but it would be
tough—all this script does is make a tkinter
PhotoImage
object for a GIF file stored in
another directory, and associate it with a
Button
widget’s
image
option. The result is captured in
Figure 8-37
.

Figure 8-37. imgButton in action

PhotoImage
and its cousin,
BitmapImage
, essentially load graphics
files and allow those graphics to be attached to other kinds of widgets.
To open a picture file, pass its name to the
file
attribute of these
image
objects. Though simple, attaching images
to buttons this way has many uses; in
Chapter 9
, for instance, we’ll use this
basic idea to implement toolbar buttons at the bottom of a window.

Canvas
widgets
—general drawing surfaces covered in more detail in the next
chapter—
can display pictures too.
Though this is a bit of a preview for the upcoming chapter, basic canvas
usage is straightforward enough to demonstrate here;
Example 8-38
renders
Figure 8-38
(shrunk here for display):

Example 8-38. PP4E\Gui\Tour\imgCanvas.py

gifdir = "../gifs/"
from tkinter import *
win = Tk()
img = PhotoImage(file=gifdir + "ora-lp4e.gif")
can = Canvas(win)
can.pack(fill=BOTH)
can.create_image(2, 2, image=img, anchor=NW) # x, y coordinates
win.mainloop()

Figure 8-38. An image on canvas

Buttons are automatically sized to fit an associated photo, but
canvases are not (because you can add objects to a canvas later, as we’ll
see in
Chapter 9
). To make a canvas
fit the picture, size it according to the
width
and
height
methods of image objects, as in
Example 8-39
. This version will make
the canvas smaller or larger than its default size as needed, lets you
pass in a photo file’s name on the command line, and can be used as a
simple image viewer utility. The visual effect of this script is captured
in
Figure 8-39
.

Example 8-39. PP4E\Gui\Tour\imgCanvas2.py

gifdir = "../gifs/"
from sys import argv
from tkinter import *
filename = argv[1] if len(argv) > 1 else 'ora-lp4e.gif' # name on cmdline?
win = Tk()
img = PhotoImage(file=gifdir + filename)
can = Canvas(win)
can.pack(fill=BOTH)
can.config(width=img.width(), height=img.height()) # size to img size
can.create_image(2, 2, image=img, anchor=NW)
win.mainloop()

Figure 8-39. Sizing the canvas to match the photo

Run this script with other filenames to view other images (try this
on your own):

C:\...\PP4E\Gui\Tour>
imgCanvas2.py ora-ppr-german.gif

And that’s all there is to it. In
Chapter 9
, we’ll see images show up again
in the items of a
Menu
, in the buttons
of a window’s toolbar, in other
Canvas
examples, and in the image-friendly
Text
widget. In later chapters, we’ll find them
in an image slideshow (PyView), in a paint program (PyDraw), on clocks
(PyClock), in a generalized photo viewer (PyPhoto), and so on. It’s easy
to add graphics to GUIs in Python/tkinter.

Once you start using photos in earnest, though, you’re likely to run
into two tricky bits that I want to warn you about here:

Supported file types

At present, the standard tkinter
PhotoImage
widget supports only GIF, PPM,
and PGM graphic file formats, and
BitmapImage
supports X Windows-style
.xbm
bitmap files. This may be expanded in
future releases, and you can convert photos in other formats to
these supported formats ahead of time, of course. But as we’ll see
later in this chapter, it’s easy to support additional image types
with the PIL open source extension toolkit and its
PhotoImage
replacement.

Hold on to your images!

Unlike all other tkinter widgets, an image is utterly lost if
the corresponding Python image object is garbage collected. That
means you must retain an explicit reference to image objects for as
long as your program needs them (e.g., assign them to a long-lived
variable name, object attribute, or data structure component).
Python does not automatically keep a reference to the image, even if
it is linked to other GUI components for display. Moreover, image
destructor methods erase the image from memory. We saw earlier that
tkinter
variables
can behave oddly when
reclaimed, too (they may be unset), but the effect is much worse and
more likely to happen with images. This may change in future Python
releases, though there are good reasons for not retaining big image
files in memory indefinitely; for now, though, images are a “use it
or lose it” widget.

Fun with Buttons and Pictures

I tried to come up with an image demo for this section that was
both fun and useful. I settled for the fun part.
Example 8-40
displays a
button that changes its image at random each time it is
pressed.

Example 8-40. PP4E\Gui\Tour\buttonpics-func.py

from tkinter import *                # get base widget set
from glob import glob # filename expansion list
import demoCheck # attach checkbutton demo to me
import random # pick a picture at random
gifdir = '../gifs/' # where to look for GIF files
def draw():
name, photo = random.choice(images)
lbl.config(text=name)
pix.config(image=photo)
root=Tk()
lbl = Label(root, text="none", bg='blue', fg='red')
pix = Button(root, text="Press me", command=draw, bg='white')
lbl.pack(fill=BOTH)
pix.pack(pady=10)
demoCheck.Demo(root, relief=SUNKEN, bd=2).pack(fill=BOTH)
files = glob(gifdir + "*.gif") # GIFs for now
images = [(x, PhotoImage(file=x)) for x in files] # load and hold
print(files)
root.mainloop()

This code uses a handful of built-in tools from the Python
library:

  • The Python
    glob
    module
    we first met in
    Chapter 4
    gives a list of all files
    ending in
    .gif
    in a directory; in other words,
    all GIF files stored there.

  • The Python
    random
    module
    is used to select a random GIF from files in the
    directory:
    random.choice
    picks
    and returns an item from a list at random.

  • To change the image displayed (and the GIF file’s name in a
    label at the top of the window), the script simply calls the widget
    config
    method with new option
    settings; changing on the fly like this changes the widget’s display
    dynamically.

Just for fun, this script also attaches an instance of the
demoCheck
check button demo bar from
Example 8-22
, which in turn
attaches an instance of the
Quitter
button we wrote earlier in
Example 8-7
. This is an
artificial example, of course, but it again demonstrates the power of
component class attachment at work.

Notice how this script builds and holds on to all images in its
images
list. The list comprehension
here applies a
PhotoImage
constructor
call to every
.gif
file in the photo directory,
producing a list of
(filename,
imageobject)
tuples that is saved in a global variable (a
map
call using a one-argument
lambda
function could do the same).
Remember, this guarantees that image objects won’t be garbage collected
as long as the program is running.
Figure 8-40
shows this script in action on
Windows.

Although it may not be obvious in this grayscale book, the name of
the GIF file being displayed is shown in red text in the blue label at
the top of this window. This program’s window grows and shrinks
automatically when larger and smaller GIF files are displayed;
Figure 8-41
shows it randomly picking
a taller photo globbed from the image directory.

Figure 8-40. buttonpics in action

Figure 8-41. buttonpics showing a taller photo

And finally,
Figure 8-42
captures
this script’s GUI displaying one of the wider GIFs, selected completely
at random from the photo file directory.
[
34
]

Figure 8-42. buttonpics gets political

While we’re playing, let’s recode this script as a
class
in case we ever want to attach or customize
it later (it could happen, especially in more realistic programs). It’s
mostly a matter of indenting and adding
self
before global variable names, as shown in
Example 8-41
.

Example 8-41. PP4E\Gui\Tour\buttonpics.py

from tkinter import *                # get base widget set
from glob import glob # filename expansion list
import demoCheck # attach check button example to me
import random # pick a picture at random
gifdir = '../gifs/' # default dir to load GIF files
class ButtonPicsDemo(Frame):
def __init__(self, gifdir=gifdir, parent=None):
Frame.__init__(self, parent)
self.pack()
self.lbl = Label(self, text="none", bg='blue', fg='red')
self.pix = Button(self, text="Press me", command=self.draw, bg='white')
self.lbl.pack(fill=BOTH)
self.pix.pack(pady=10)
demoCheck.Demo(self, relief=SUNKEN, bd=2).pack(fill=BOTH)
files = glob(gifdir + "*.gif")
self.images = [(x, PhotoImage(file=x)) for x in files]
print(files)
def draw(self):
name, photo = random.choice(self.images)
self.lbl.config(text=name)
self.pix.config(image=photo)
if __name__ == '__main__': ButtonPicsDemo().mainloop()

This version works the same way as the original, but it can now be
attached to any other GUI where you would like to include such an
unreasonably silly
button.

BOOK: Programming Python
11.01Mb size Format: txt, pdf, ePub
ads

Other books

Wild Passion by Brighton, Lori
Lovesick by Alex Wellen
Dreamspinner by Lynn Kurland
Ordinary Miracles by Grace Wynne-Jones
Second Sight by Carly Fall
Mrs R (Mrs R & Mr V #1) by Jessie Courts
One (One Universe) by LeighAnn Kopans