Python Graphics: Combining Raster and Vector Pictures

Exclusive offer: get 50% off this eBook here
Python 2.6 Graphics Cookbook

Python 2.6 Graphics Cookbook — Save 50%

Over 100 great recipes for creating and animating graphics using Python

$26.99    $13.50
by Mike Ohlson de Fine | November 2010 | Cookbooks Open Source Web Graphics & Video

Vector graphics can be shrunk and expanded to any size and in any direction using simple algebra. They can be animated with rotations using basic trigonometry. Raster graphics are limited. They cannot be resized or rotated dynamically while the code is executing. They are more cumbersome. However, we can get tremendous effects when we combine both vector and raster graphics together. The one thing that Python cannot do is to rotate a GIF image by itself. There are ways of mimicking rotation reasonably but there are limitations you will appreciate after trying out some of these recipes. PIL can rotate them, but not dynamically on a Tkinter canvas. We explore some possibilities and workarounds here.

In this article by Mike Ohlson de Fine, author of Python 2.6 Graphics Cookbook, we will cover:

  • Simple animation of a GIF beach ball
  • The vector walking creature
  • Bird with shoes walking in the karroo
  • Making a partially transparent image with GIMP
  • Diplomat walking at the palace
  • Spider in the forest
  • Moving band of images
  • Continuous band of images
  • Endless background – a passing cloudscape

 

Python 2.6 Graphics Cookbook

Python 2.6 Graphics Cookbook

Over 100 great recipes for creating and animating graphics using Python

  • Create captivating graphics with ease and bring them to life using Python
  • Apply effects to your graphics using powerful Python methods
  • Develop vector as well as raster graphics and combine them to create wonders in the animation world
  • Create interactive GUIs to make your creation of graphics simpler
  • Part of Packt's Cookbook series: Each recipe is a carefully organized sequence of instructions to accomplish the task of creation and animation of graphics as efficiently as possible
        Read more about this book      

(For more resources on Python, see here.)

Because we are not altering and manipulating the actual properties of the images we do not need the Python Imaging Library (PIL) in this chapter. We need to work exclusively with GIF format images because that is what Tkinter deals with.

We will also see how to use "The GIMP" as a tool to prepare images suitable for animation.

Simple animation of a GIF beach ball

We want to animate a raster image, derived from a photograph.

To keep things simple and clear we are just going to move a photographic image (in GIF format) of a beach ball across a black background.

Getting ready

We need a suitable GIF image of an object that we want to animate. An example of one, named beachball.gif has been provided.

How to do it...

Copy a .gif fle from somewhere and paste it into a directory where you want to keep your work-in-progress pictures.

Ensure that the path in our computer's fle system leads to the image to be used. In the example below, the instruction, ball = PhotoImage(file = "constr/pics2/beachball.gif") says that the image to be used will be found in a directory (folder) called pics2, which is a sub-folder of another folder called constr.

Then execute the following code.

# photoimage_animation_1.py
#>>>>>>>>>>>>>>>>>>>>>>>>
from Tkinter import *
root = Tk()

cycle_period = 100

cw = 300 # canvas width
ch = 200 # canvas height
canvas_1 = Canvas(root, width=cw, height=ch, bg="black")
canvas_1.grid(row=0, column=1)
posn_x = 10
posn_y = 10
shift_x = 2
shift_y = 1

ball = PhotoImage(file = "/constr/pics2/beachball.gif")

for i in range(1,100): # end the program after 100 position shifts.
posn_x += shift_x
posn_y += shift_y
canvas_1.create_image(posn_x,posn_y,anchor=NW, image=ball)
canvas_1.update() # This refreshes the drawing on the canvas.
canvas_1.after(cycle_period) # This makes execution pause for 100 milliseconds.
canvas_1.delete(ALL) # This erases everything on the canvas.

root.mainloop()

How it Works

The image of the beach ball is shifted across a canvas. The photo type images always occupy a rectangular area of screen. The size of this box, called the bounding box, is the size of the image. We have used a black background so the black corners on the image of our beach ball cannot be seen.

The vector walking creature

We make a pair of walking legs using the vector graphics. We want to use these legs together with pieces of raster images and see how far we can go in making appealing animations. We import Tkinter, math, and time modules. The math is needed to provide the trigonometry that sustains the geometric relations that move the parts of the leg in relation to each other.

Python Graphics Combining Raster and Vector Pictures

Getting ready

We will be using Tkinter and time modules to animate the movement of lines and circles. You will see some trigonometry in the code. If you do not like mathematics you can just cut and paste the code without the need to understand exactly how the maths works. However, if you are a friend of mathematics it is fun to watch sine, cosine, and tangent working together to make a child smile.

How to do it...

Execute the program as shown in the previous image.

# walking_creature_1.py
# >>>>>>>>>>>>>>>>
from Tkinter import *
import math
import time
root = Tk()
root.title("The thing that Strides")

cw = 400 # canvas width
ch = 100 # canvas height
#GRAVITY = 4
chart_1 = Canvas(root, width=cw, height=ch, background="white")
chart_1.grid(row=0, column=0)

cycle_period = 100 # time between new positions of the ball (milliseconds).

base_x = 20
base_y = 100
hip_h = 40
thy = 20
#===============================================
# Hip positions: Nhip = 2 x Nstep, the number of steps per foot per stride.
hip_x = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 60, 60] #15
hip_y = [0, 8, 12, 16, 12, 8, 0, 0, 0, 8, 12, 16, 12, 8, 0] #15

step_x = [0, 10, 20, 30, 40, 50, 60, 60] # 8 = Nhip
step_y = [0, 35, 45, 50, 43, 32, 10, 0]

# The merging of the separate x and y lists into a single sequence.
#==================================
# Given a line joining two points xy0 and xy1, the base of an isosceles triangle,
# as well as the length of one side, "thy" . This returns the coordinates of
# the apex joining the equal-length sides.

def kneePosition(x0, y0, x1, y1, thy):
theta_1 = math.atan2((y1 - y0), (x1 - x0))
L1 = math.sqrt( (y1 - y0)**2 + (x1 - x0)**2)
if L1/2 < thy:
# The sign of alpha determines which way the knees bend.
alpha = -math.acos(L1/(2*thy)) # Avian
#alpha = math.acos(L1/(2*thy)) # Mammalian
else:
alpha = 0.0
theta_2 = alpha + theta_1
x_knee = x0 + thy * math.cos(theta_2)
y_knee = y0 + thy * math.sin(theta_2)
return x_knee, y_knee

def animdelay():
chart_1.update() # This refreshes the drawing on the canvas.
chart_1.after(cycle_period) # This makes execution pause for 200 milliseconds.
chart_1.delete(ALL) # This erases *almost* everything on the canvas.
# Does not delete the text from inside a function.
bx_stay = base_x
by_stay = base_y

for j in range(0,11): # Number of steps to be taken - arbitrary.
astep_x = 60*j
bstep_x = astep_x + 30
cstep_x = 60*j + 15
aa = len(step_x) -1
for k in range(0,len(hip_x)-1):
# Motion of the hips in a stride of each foot.
cx0 = base_x + cstep_x + hip_x[k]
cy0 = base_y - hip_h - hip_y[k]
cx1 = base_x + cstep_x + hip_x[k+1]
cy1 = base_y - hip_h - hip_y[k+1]
chart_1.create_line(cx0, cy0 ,cx1 ,cy1)
chart_1.create_oval(cx1-10 ,cy1-10 ,cx1+10 ,cy1+10, fill="orange")

if k >= 0 and k <= len(step_x)-2:
# Trajectory of the right foot.
ax0 = base_x + astep_x + step_x[k]
ax1 = base_x + astep_x + step_x[k+1]
ay0 = base_y - step_y[k]
ay1 = base_y - step_y[k+1]
ax_stay = ax1
ay_stay = ay1

if k >= len(step_x)-1 and k <= 2*len(step_x)-2:
# Trajectory of the left foot.
bx0 = base_x + bstep_x + step_x[k-aa]
bx1 = base_x + bstep_x + step_x[k-aa+1]
by0 = base_y - step_y[k-aa]
by1 = base_y - step_y[k-aa+1]
bx_stay = bx1
by_stay = by1

aknee_xy = kneePosition(ax_stay, ay_stay, cx1, cy1, thy)
chart_1.create_line(ax_stay, ay_stay ,aknee_xy[0], aknee_xy[1], width = 3, fill="orange")
chart_1.create_line(cx1, cy1 ,aknee_xy[0], aknee_xy[1], width = 3, fill="orange")

chart_1.create_oval(ax_stay-5 ,ay1-5 ,ax1+5 ,ay1+5, fill="green")
chart_1.create_oval(bx_stay-5 ,by_stay-5 ,bx_stay+5 ,by_stay+5, fill="blue")

bknee_xy = kneePosition(bx_stay, by_stay, cx1, cy1, thy)
chart_1.create_line(bx_stay, by_stay ,bknee_xy[0], bknee_xy[1], width = 3, fill="pink")
chart_1.create_line(cx1, cy1 ,bknee_xy[0], bknee_xy[1], width = 3, fill="pink")

animdelay()

root.mainloop()

How it works...

Without getting bogged down in detail, the strategy in the program consists of defning the motion of a foot while walking one stride. This motion is defned by eight relative positions given by the two lists step_x (horizontal) and step_y (vertical). The motion of the hips is given by a separate pair of x- and y-positions hip_x and hip_y.

Trigonometry is used to work out the position of the knee on the assumption that the thigh and lower leg are the same length. The calculation is based on the sine rule taught in high school. Yes, we do learn useful things at school!

The time-animation regulation instructions are assembled together as a function animdelay().

There's more

In Python math module, two arc-tangent functions are available for calculating angles given the lengths of two adjacent sides. atan2(y,x) is the best because it takes care of the crazy things a tangent does on its way around a circle - tangent ficks from minus infnity to plus infnity as it passes through 90 degrees and any multiples thereof.

A mathematical knee is quite happy to bend forward or backward in satisfying its equations. We make the sign of the angle negative for a backward-bending bird knee and positive for a forward bending mammalian knee.

More Info Section 1

This animated walking hips-and-legs is used in the recipes that follow this to make a bird walk in the desert, a diplomat in palace grounds, and a spider in a forest.

Bird with shoes walking in the Karroo

We now coordinate the movement of four GIF images and the striding legs to make an Apteryx (a fightless bird like the kiwi) that walks.

Python Graphics Combining Raster and Vector Pictures

Getting ready

We need the following GIF images:

  • A background picture of a suitable landscape
  • A bird body without legs
  • A pair of garish-colored shoes to make the viewer smile
  • The walking avian legs of the previous recipe

The images used are karroo.gif, apteryx1.gif, and shoe1.gif. Note that the images of the bird and the shoe have transparent backgrounds which means there is no rectangular background to be seen surrounding the bird or the shoe. In the recipe following this one, we will see the simplest way to achieve the necessary transparency.

How to do it...

Execute the program shown in the usual way.

# walking_birdy_1.py
# >>>>>>>>>>>>>>>>
from Tkinter import *
import math
import time
root = Tk()
root.title("A Walking birdy gif and shoes images")
cw = 800 # canvas width
ch = 200 # canvas height
#GRAVITY = 4
chart_1 = Canvas(root, width=cw, height=ch, background="white")
chart_1.grid(row=0, column=0)

cycle_period = 80 # time between new positions of the ball (milliseconds).
im_backdrop = "/constr/pics1/karoo.gif"
im_bird = "/constr/pics1/apteryx1.gif"
im_shoe = "/constr/pics1/shoe1.gif"
birdy =PhotoImage(file= im_bird)
shoey =PhotoImage(file= im_shoe)
backdrop = PhotoImage(file= im_backdrop)
chart_1.create_image(0 ,0 ,anchor=NW, image=backdrop)
base_x = 20
base_y = 190
hip_h = 70
thy = 60
#==========================================
# Hip positions: Nhip = 2 x Nstep, the number of steps per foot per stride.
hip_x = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 60, 60] #15
hip_y = [0, 8, 12, 16, 12, 8, 0, 0, 0, 8, 12, 16, 12, 8, 0] #15

step_x = [0, 10, 20, 30, 40, 50, 60, 60] # 8 = Nhip
step_y = [0, 35, 45, 50, 43, 32, 10, 0]

#=============================================
# Given a line joining two points xy0 and xy1, the base of an isosceles triangle,
# as well as the length of one side, "thy" this returns the coordinates of
# the apex joining the equal-length sides.

def kneePosition(x0, y0, x1, y1, thy):
theta_1 = math.atan2(-(y1 - y0), (x1 - x0))
L1 = math.sqrt( (y1 - y0)**2 + (x1 - x0)**2)
alpha = math.atan2(hip_h,L1)
theta_2 = -(theta_1 - alpha)
x_knee = x0 + thy * math.cos(theta_2)
y_knee = y0 + thy * math.sin(theta_2)
return x_knee, y_knee

def animdelay():
chart_1.update() # Refresh the drawing on the canvas.
chart_1.after(cycle_period) # Pause execution pause for X millise-conds.
chart_1.delete("walking") # Erases everything on the canvas.

bx_stay = base_x
by_stay = base_y

for j in range(0,13): # Number of steps to be taken - arbitrary.
astep_x = 60*j
bstep_x = astep_x + 30
cstep_x = 60*j + 15
aa = len(step_x) -1
for k in range(0,len(hip_x)-1):
# Motion of the hips in a stride of each foot.
cx0 = base_x + cstep_x + hip_x[k]
cy0 = base_y - hip_h - hip_y[k]
cx1 = base_x + cstep_x + hip_x[k+1]
cy1 = base_y - hip_h - hip_y[k+1]
#chart_1.create_image(cx1-55 ,cy1+20 ,anchor=SW, image=birdy, tag="walking")

if k >= 0 and k <= len(step_x)-2:
# Trajectory of the right foot.
ax0 = base_x + astep_x + step_x[k]
ax1 = base_x + astep_x + step_x[k+1]
ay0 = base_y - 10 - step_y[k]
ay1 = base_y - 10 -step_y[k+1]
ax_stay = ax1
ay_stay = ay1

if k >= len(step_x)-1 and k <= 2*len(step_x)-2:
# Trajectory of the left foot.
bx0 = base_x + bstep_x + step_x[k-aa]
bx1 = base_x + bstep_x + step_x[k-aa+1]
by0 = base_y - 10 - step_y[k-aa]
by1 = base_y - 10 - step_y[k-aa+1]
bx_stay = bx1
by_stay = by1

chart_1.create_image(ax_stay-5 ,ay_stay + 10 ,anchor=SW, im-age=shoey, tag="walking")
chart_1.create_image(bx_stay-5 ,by_stay + 10 ,anchor=SW, im-age=shoey, tag="walking")

aknee_xy = kneePosition(ax_stay, ay_stay, cx1, cy1, thy)
chart_1.create_line(ax_stay, ay_stay-15 ,aknee_xy[0], aknee_xy[1],\
width = 5, fill="orange", tag="walking")
chart_1.create_line(cx1, cy1 ,aknee_xy[0], aknee_xy[1], width = 5,\
fill="orange", tag="walking")

bknee_xy = kneePosition(bx_stay, by_stay, cx1, cy1, thy)
chart_1.create_line(bx_stay, by_stay-15 ,bknee_xy[0], bknee_xy[1],\
width = 5, fill="pink", tag="walking")
chart_1.create_line(cx1, cy1 ,bknee_xy[0], bknee_xy[1], width = 5,\
fill="pink", tag="walking")
chart_1.create_image(cx1-55 ,cy1+20 ,anchor=SW, image=birdy, tag="walking")
animdelay()

root.mainloop()

How it works...

The same remarks concerning the trigonometry made in the previous recipe apply here. What we see here now is the ease with which vector objects and raster images can be combined once suitable GIF images have been prepared.

There's more...

For teachers and their students who want to make lessons on a computer, these techniques offer all kinds of possibilities like history tours and re-enactments, geography tours, and, science experiments. Get the students to do projects telling stories. Animated year books?

Python 2.6 Graphics Cookbook Over 100 great recipes for creating and animating graphics using Python
Published: November 2010
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on Python, see here.)

Making GIF images with transparent backgrounds using GIMP

We make an image with an opaque background into one with a transparent background using the free and open-source GIMP image editor.

Getting ready

We can get GIMP (GNU Image Manipulation Program) at http://www.gimp.org/. There are versions that can be installed on Windows and Linux. GIMP is an excellent package and well-worth the effort of learning to use. It can be frustrating when you are not used to it so this particular recipe is devoted to describing the steps that will transform a .png image with an opaque background into a .gif image with a transparent background.

In Windows, you simply go to the website and click the Download button and it will install and can be used immediately. With Linux, it is often already installed. With any Debian-based Linux sudo apt-get install gimp should get it installed and you are ready to go.

How to do it...

This recipe does not involve running Python code. Instead, it is a list of actions to perform with your mouse on the Gimp GUI. In the following instructions, click Select | Invert is the short-form for "Left-click on, select, then left-click on Invert".

  1. Open GIMP and open the fle apteryx1.png. This is a cartoon bird that has been drawn.

    Python Graphics Combining Raster and Vector Pictures

  2. Click Windows | Dockable dialogs | Layers. This will open up a display panel that shows all the layers making up the image we are working on. Watching what is going on with the layers is the secret to using GIMP.
  3. Click Select | By color, and then place the cursor arrow anywhere on the black portion of the image and click. You will see a shimmering dotted line around the outline of the bird. What we have done is to select for alteration only the black portions of the picture.

    Python Graphics Combining Raster and Vector Pictures

  4. Click Select | Invert. What this does is it changes the selection to everything except the black portion.
  5. Click Edit | Copy. This picks up a copy of the selected portion (everything not black) and places it onto an invisible clipboard.
  6. Click Edit | Paste. This takes a copy from the clipboard and potentially pastes it onto our existing image. But until you have completed the next step, the pasted image is held in a kind of no-man's land.
  7. Click Layer | New. This frmly places the pasted portion of the image onto its own separate layer. The layers are like sheets of clear glass with portions of a composite picture on it. When you work on them and change one layer, the others are unaltered.

    Python Graphics Combining Raster and Vector Pictures

  8. Right-click the Backdrop layer as shown, then click Delete Layer. This discards the Backdrop layer that consists of the original image. You will see there is only one layer left. It contains the bird image placed on a transparent background.

    Python Graphics Combining Raster and Vector Pictures

  9. Click File | Save as. In the save window, type in apteryx1.gif for the file name.

    Python Graphics Combining Raster and Vector Pictures

  10. Close GIMP. You will fnd your new GIF image with a transparent background in whatever folder you sent it to. In Linux systems, transparent areas are shown as a gray checker-board pattern.

How it works...

All images used in this article that have areas which are transparent were prepared using GIMP this way. There are other ways to achieve this but this is possibly the most readily available one. The animations in this chapter consist of a smaller, partially transparent image moving across a larger opaque image.

Diplomat walking at the palace

We now animate a dignifed man using the same legs as before, appropriately colored. For the human style walk, we need to select the correct mammalian knee-bend angle option chosen in the code prior to interpreting.

Python Graphics Combining Raster and Vector Pictures

Getting ready

We need the following GIF images:

  • A background picture of a suitable landscape
  • A human body without legs
  • A pair of sober shoes for dignity
  • The walking mammal legs
  • The images used are palace.gif, ambassador.gif, and ambassador_shoe1.gif. As before, the images of the man and the shoe have transparent backgrounds.

    How to do it...

    Execute the program shown as before.

    # walking_toff_1.py
    # >>>>>>>>>>>>>>>>>
    from Tkinter import *
    import math
    import time
    root = Tk()
    root.title("A Walking Toff in Natural Habitat - gif images")
    cw = 800 # canvas width
    ch = 200 # canvas height
    #GRAVITY = 4
    chart_1 = Canvas(root, width=cw, height=ch, background="white")
    chart_1.grid(row=0, column=0)

    cycle_period = 120 # time between new positions of the ball (milliseconds).
    im_backdrop = "/constr/pics1/toff_bg.gif"
    im_toff = "/constr/pics1/ambassador.gif"
    im_shoe = "/constr/pics1/toff_shoe.gif"
    toff =PhotoImage(file= im_toff)
    shoey =PhotoImage(file= im_shoe)
    backdrop = PhotoImage(file= im_backdrop)
    chart_1.create_image(0 ,0 ,anchor=NW, image=backdrop)
    base_x = 20
    base_y = 190
    hip_h = 60
    thy = 25
    #============================================
    # Hip positions: Nhip = 2 x Nstep, the number of steps per foot per stride.
    hip_x = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 60, 60] #15
    hip_y = [0, 4, 6, 8, 6, 4, 0, 0, 0, 4, 6, 8, 6, 4, 0] #15

    step_x = [0, 10, 20, 30, 40, 50, 60, 60] # 8 = Nhip
    step_y = [0, 15, 25, 30, 25, 22, 10, 0]
    #============================================
    # Given a line joining two points xy0 and xy1, the base of an isosceles triangle,
    # as well as the length of one side, "thy" this returns the coordinates of
    # the apex joining the equal-length sides.

    def kneePosition(x0, y0, x1, y1, thy):
    theta_1 = math.atan2((y1 - y0), (x1 - x0))
    L1 = math.sqrt( (y1 - y0)**2 + (x1 - x0)**2)
    if L1/2 < thy:
    alpha = math.acos(L1/(2*thy))
    else:
    alpha = 0.0
    theta_2 = alpha + theta_1
    x_knee = x0 + thy * math.cos(theta_2)
    y_knee = y0 + thy * math.sin(theta_2)
    return x_knee, y_knee

    def animdelay():
    chart_1.update() # Refresh the drawing on the canvas.
    chart_1.after(cycle_period) # Pause execution for X milliseconds.
    chart_1.delete("walking") # Erases everything on the canvas.

    bx_stay = base_x
    by_stay = base_y

    for j in range(0,13): # Number of steps to be taken - arbi-trary.
    astep_x = 60*j
    bstep_x = astep_x + 30
    cstep_x = 60*j + 15
    aa = len(step_x) -1
    for k in range(0,len(hip_x)-1):
    # Motion of the hips in a stride of each foot.
    cx0 = base_x + cstep_x + hip_x[k]
    cy0 = base_y - hip_h - hip_y[k]
    cx1 = base_x + cstep_x + hip_x[k+1]
    cy1 = base_y - hip_h - hip_y[k+1]

    if k >= 0 and k <= len(step_x)-2:
    # Trajectory of the right foot.
    ax0 = base_x + astep_x + step_x[k]
    ax1 = base_x + astep_x + step_x[k+1]
    ay0 = base_y - 10 - step_y[k]
    ay1 = base_y - 10 -step_y[k+1]
    ax_stay = ax1
    ay_stay = ay1

    if k >= len(step_x)-1 and k <= 2*len(step_x)-2:
    # Trajectory of the left foot.
    bx0 = base_x + bstep_x + step_x[k-aa]
    bx1 = base_x + bstep_x + step_x[k-aa+1]
    by0 = base_y - 10 - step_y[k-aa]
    by1 = base_y - 10 - step_y[k-aa+1]
    bx_stay = bx1
    by_stay = by1
    # The shoes
    chart_1.create_image(ax_stay-5 ,ay_stay + 10 ,anchor=SW, im-age=shoey, tag="walking")
    chart_1.create_image(bx_stay-5 ,by_stay + 10 ,anchor=SW, im-age=shoey, tag="walking")

    # Work out knee positions
    aknee_xy = kneePosition(ax_stay, ay_stay, cx1, cy1, thy)
    bknee_xy = kneePosition(bx_stay, by_stay, cx1, cy1, thy)

    # Right calf.
    chart_1.create_line(ax_stay, ay_stay-5 ,aknee_xy[0], aknee_xy[1],\
    width = 5, fill="black", tag="walking")
    # Right thigh.
    chart_1.create_line(cx1, cy1 ,aknee_xy[0], aknee_xy[1], width = 5,\
    fill="black", tag="walking")
    # Left calf.
    #bknee_xy = kneePosition(bx_stay, by_stay, cx1, cy1, thy)
    chart_1.create_line(bx_stay, by_stay-5 ,bknee_xy[0], bknee_xy[1],\
    width = 5, fill="black", tag="walking")
    # Left thigh.
    chart_1.create_line(cx1, cy1 ,bknee_xy[0], bknee_xy[1], width = 5,\
    fill="black", tag="walking")
    # Torso
    chart_1.create_image(cx1-20 ,cy1+30 ,anchor=SW, image=toff, tag="walking")

    animdelay() # Animation

    root.mainloop()

    How it works...

    The great possibilities offered through the use of image combining using the transparent channel in GIF images allows us to create studio-quality cartoon animations. The same remarks concerning the trigonometry made in the previous recipe apply here.

    Python 2.6 Graphics Cookbook Over 100 great recipes for creating and animating graphics using Python
    Published: November 2010
    eBook Price: $26.99
    Book Price: $44.99
    See more
    Select your format and quantity:
            Read more about this book      

    (For more resources on Python, see here.)

    Spider in the forest

    We now combine both mammal and bird leg motions to create a sinister-looking spider. We also introduce a moving background for the frst time. No transparent images are used here as the entire spider is made of animated vector lines and ovals.

    Python Graphics Combining Raster and Vector Pictures

    Getting ready

    Here, we need one long narrow strip image that is substantially wider than the Tkinter canvas provided. This not a problem and aids us in creating the illusion of a spider walking through an endless forest.

    How to do it...

    Execute the program shown as before.

    # walker_spider_1.py
    # >>>>>>>>>>>>>>>>
    from Tkinter import *
    import math
    import time
    root = Tk()
    root.title("Mr Incy Wincy")
    cw = 500 # canvas width
    ch = 100 # canvas height

    chart_1 = Canvas(root, width=cw, height=ch, background="white")
    chart_1.grid(row=0, column=0)

    cycle_period = 100 # time between new positions of thespider (millise-conds).

    base_x = 20
    base_y = 100
    avian = 1

    ax = [ base_x, base_x+20, base_x+60 ]
    ay = [ base_y, base_y, base_y ]
    bx = [ base_x+90, base_x+130, base_x+170]
    by = [ base_y, base_y, base_y ]

    cx1 = base_x + 80
    cy1 = base_y - 20
    thy = 50
    #=============================================
    posn_x = 0
    posn_y = 00

    spider_backg = PhotoImage(file = "/constr/pics1/jungle_strip_1.gif")


    #===========================================

    foot_lift = [10,10,5,-5,-10,-10] # 3 legs per side, each foot in sequence = 18 moves
    foot_stay = [ 0, 0,0, 0, 0, 0]

    #========================================
    # Given a line joining two points xy0 and xy1, the base of an isosceles triangle,
    # as well as the length of one side, "thy" this returns the coordinates of
    # the apex joining the equal-length sides - the position of the knee.

    def kneePosition(x0, y0, x1, y1, thy, avian):
    theta_1 = math.atan2((y1 - y0), (x1 - x0))
    L1 = math.sqrt( (y1 - y0)**2 + (x1 - x0)**2)
    if L1/2 < thy:
    # The sign of alpha determines which way the knees bend.
    if avian == 1:
    alpha = -math.acos(L1/(2*thy)) # Avian
    else:
    alpha = math.acos(L1/(2*thy)) # Mammalian
    else:
    alpha = 0.0
    theta_2 = alpha + theta_1
    x_knee = x0 + thy * math.cos(theta_2)
    y_knee = y0 + thy * math.sin(theta_2)
    return x_knee, y_knee

    def animdelay():
    chart_1.update() # This refreshes the drawing on the canvas.
    chart_1.after(cycle_period) # This makes execution pause for 100 milliseconds.
    chart_1.delete(ALL) # This erases *almost* everything on the canvas.

    for j in range(0,11): # Number of steps to be taken - arbitrary.

    posn_x -= 1
    chart_1.create_image(posn_x,posn_y,anchor=NW, image=spider_backg)
    for k in range(0,len(foot_lift)*3):
    posn_x -= 1
    chart_1.create_image(posn_x,posn_y,anchor=NW, image=spider_backg)
    #cx1 += 3.5
    cx1 += 2.6
    # Phase 1
    if k >= 0 and k <= 5:
    ay[0] = base_y - 10 - foot_lift[k]
    ax[0] += 8
    by[0] = base_y - 10 - foot_lift[k]
    bx[0] += 8

    # Phase 2
    if k > 5 and k <= 11:
    ay[1] = base_y - 10 - foot_lift[k-6]
    ax[1] += 8
    by[1] = base_y - 10 - foot_lift[k-6]
    bx[1] += 8

    # Phase 3
    if k > 11 and k <= 17:
    ay[2] = base_y - 10 - foot_lift[k-12]
    ax[2] += 8
    by[2] = base_y - 10 - foot_lift[k-12]
    bx[2] += 8

    for i in range(0,3):
    aknee_xy = kneePosition(ax[i], ay[i], cx1, cy1, thy, 1) # Mammal knee
    bknee_xy = kneePosition(bx[i], by[i], cx1, cy1, thy, 0) # Bird knee
    chart_1.create_line(ax[i], ay[i] ,aknee_xy[0], aknee_xy[1], width = 3)
    chart_1.create_line(cx1, cy1 ,aknee_xy[0], aknee_xy[1], width = 3)
    chart_1.create_line(bx[i], by[i] ,bknee_xy[0], bknee_xy[1], width = 3)
    chart_1.create_line(cx1, cy1 ,bknee_xy[0], bknee_xy[1], width = 3)

    chart_1.create_oval(cx1-15 ,cy1-10 ,cx1+15 ,cy1+10, fill="black")
    animdelay()

    root.mainloop()

    How it works...

    The essential art in making the spider walk acceptably is to adjust the length of stride, height of body above the ground, and thigh (leg segment) length to be consistent with each other. With slightly wrong adjustments, the legs roll over or appear made of very stretchy material. There is also the issue of how the spider's leg movements should be synchronized. In this recipe, we have opted to make the limbs move in paired sequences.

    There's more...

    Real spiders have eight legs, not six as in this example. You could try to add the extra pair of legs as a challenge. Real spiders also have an extra pair of segments in each leg. Getting the leg trigonometry is an excellent challenge for the mathematically talented ones

    Moving band of images

    We make a moving band of images like a slideshow. This differs from the typical slideshow by showing the images as a continuously moving strip with the images placed end to end.

    Python Graphics Combining Raster and Vector Pictures

    Getting ready

    We need a set of four images, all of the same size. If they were not the same size, the program would still work but would not look well designed. The images provided for this code are: brass_vase.gif, red_vase.gif, blue_vase.gif, and glass_vase.gif and are 200 pixels high and 100 wide.

    How to do it...

    Execute the program shown as before.

    # passing_show_1.py
    # >>>>>>>>>>>>>>>>>>>>>>
    from Tkinter import *
    import math
    import time
    root = Tk()
    root.title("Vase Show")
    cw = 400 # canvas width
    ch = 200 # canvas height

    chart_1 = Canvas(root, width=cw, height=ch, background="white")
    chart_1.grid(row=0, column=0)

    cycle_period = 100 # time between new positions of the ball (milliseconds).
    #=======================================================================
    posn_x1 = 0
    posn_x2 = 100
    posn_x3 = 200
    posn_x4 = 300

    posn_y = 00

    im_brass = PhotoImage(file = "/constr/pics1/brass_vase.gif")
    im_red = PhotoImage(file = "/constr/pics1/red_vase.gif")
    im_blue = PhotoImage(file = "/constr/pics1/blue_vase.gif")
    im_glass = PhotoImage(file = "/constr/pics1/glass_vase.gif")
    #=======================================================================
    def animdelay():
    chart_1.update() # This refreshes the drawing on the canvas.
    chart_1.after(cycle_period) # This makes execution pause for 100 milliseconds.
    chart_1.delete(ALL) # This erases *almost* everything on the canvas.

    for j in range(0,400): # Number of steps to be taken - arbitrary.
    posn_x1 -= 1
    posn_x2 -= 1
    posn_x3 -= 1
    posn_x4 -= 1
    chart_1.create_image(posn_x1,posn_y,anchor=NW, image=im_brass)
    chart_1.create_image(posn_x2,posn_y,anchor=NW, image=im_red)
    chart_1.create_image(posn_x3,posn_y,anchor=NW, image=im_blue)
    chart_1.create_image(posn_x4,posn_y,anchor=NW, image=im_glass)
    animdelay()

    root.mainloop()
    # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    How it works...

    Each image has its own x position coordinate posn_x1, posn_x2 etc. a 'for' loop adjusts these positions by one pixel each time the loop is executed, causing the images to progressively shift to the left.

    Continuous band of images

    This recipe extends the position-adjusting mechanism used in the previous example to sustain a continuous strip of images.

    Getting ready

    We use the same set of four images that were used in the previous recipe.

    How to do it...

    Execute the program shown in exactly the same way as before.

    # endless_passing_show_1.py
    # >>>>>>>>>>>>>>>>>>>>>
    from Tkinter import *
    import math
    import time
    root = Tk()
    root.title("Vase Show")
    cw = 100 # canvas width
    ch = 200 # canvas height
    chart_1 = Canvas(root, width=cw, height=ch, background="black")
    chart_1.grid(row=0, column=0)

    cycle_period = 100 # time between new positions of the images millise-conds).
    #==============================================
    posn_x1 = 0
    posn_x2 = 100
    posn_x3 = 200
    posn_x4 = 300
    posn_y = 00

    im_brass = PhotoImage(file = "/constr/pics1/brass_vase.gif")
    im_red = PhotoImage(file = "/constr/pics1/red_vase.gif")
    im_blue = PhotoImage(file = "/constr/pics1/blue_vase.gif")
    im_glass = PhotoImage(file = "/constr/pics1/glass_vase.gif")
    #=============================================
    def animdelay():
    chart_1.update() # This refreshes the drawing on the canvas.
    chart_1.after(cycle_period) # This makes execution pause for 100 milliseconds.
    chart_1.delete(ALL) # This erases *almost* everything on the canvas.

    for j in range(0,600): # Number of steps to be taken - arbitrary.
    posn_x1 -= 1
    posn_x2 -= 1
    posn_x3 -= 1
    posn_x4 -= 1
    chart_1.create_image(posn_x1,posn_y,anchor=NW, image=im_brass)
    chart_1.create_image(posn_x2,posn_y,anchor=NW, image=im_red)
    chart_1.create_image(posn_x3,posn_y,anchor=NW, image=im_blue)
    chart_1.create_image(posn_x4,posn_y,anchor=NW, image=im_glass)
    # The numerical parameters below could be turned into
    # a 'for' loop and allow the loop to be compact and interminable.
    if j == 100:
    posn_x1 = 300
    if j == 200:
    posn_x2 = 300
    if j == 400:
    posn_x3 = 300
    if j == 400:
    posn_x4 = 300
    animdelay()

    root.mainloop()

    How it works...

    The trick with this program is to reset the x position coordinates, posn_1, and so on, which control the position of each image on the canvas after the image has exited the canvas on the left. The position coordinates get reset to a position 200 pixels off to the right of the canvas.

    Endless background

    The next thing to achieve is to create what appears to be one practically infnitely wide panorama. We extend the technique used in the previous example and make a background image that appears to be endless.

    Python Graphics Combining Raster and Vector Pictures

    Getting ready

    We have provided a single image that has been doctored so that the right-hand edge fits exactly onto the left-hand edge to create an endless and continuous image if they are placed side by side. The GIMP image manipulation program was used to do this editing. In a very condensed explanation, we do the following:

    1. We copy a portion of the image that does not have too much detail vertically where we make the cut.
    2. This is then pasted onto one end so that there is substantial overlap of the two images.
    3. Then the top layer, containing the copy-and-pasted portion, has the eraser tool with a fuzzy edge applied so that we cannot see the transition from one image to the next.

    How to do it...

    Execute the following code.

    # passing_show_1.py
    # >>>>>>>>>>>>>>>>
    from Tkinter import *
    import time
    root = Tk()
    root.title("Freedom Flight Cloudscape")
    cw = 400 # canvas width
    ch = 239 # canvas height

    chart_1 = Canvas(root, width=cw, height=ch, background="black")
    chart_1.grid(row=0, column=0)

    cycle_period = 50 # time between new positions of the background (milli-seconds).
    #=============================================
    posn_x1 = 0
    posn_x2 = 574
    posn_plane_x = 60
    posn_plane_y = 60
    posn_y = 00
    # Panorama image size = 574 x 239
    im_one = PhotoImage(file = "/constr/pics1/continuous_clouds_panorama.gif")
    im_two = PhotoImage(file = "/constr/pics1/continuous_clouds_panorama.gif")
    im_plane = PhotoImage(file = "/constr/pics1/yellow_airplane_2.gif")
    #===========================================
    def animdelay():
    chart_1.update() # This refreshes the drawing on the canvas.
    chart_1.after(cycle_period) # This makes execution pause for 50 milliseconds.
    chart_1.delete(ALL) # This erases *almost* everything on the canvas.
    num_cycles = 10 # Number of total cycles of the loop.
    k = 0
    for j in range(0,num_cycles*1148): # Number of steps to be taken - arbitrary.
    posn_x1 -= 1
    posn_x2 -= 1
    k += 1
    chart_1.create_image(posn_x1,posn_y,anchor=NW, image=im_one)
    chart_1.create_image(posn_x2,posn_y,anchor=NW, image=im_two)
    chart_1.create_image(posn_plane_x,posn_plane_y,anchor=NW, im-age=im_plane)
    if k == 574:
    posn_x1 = 574
    if k == 1148:
    posn_x2 = 574
    k = 0
    posn_x1 = 0
    animdelay()

    root.mainloop()

    How it works...

    We use the same x coordinate position adjustment technique as we did in the previous recipe. This time we choose the position for readjustment to be a multiple of 574 which is the width, in pixels, of the cloudscape image. We also use the image of an airplane, on a transparent background. The airplane is kept stationary.

    Summary

    In this article we covered:

    • Simple animation of a GIF beach ball
    • The vector walking creature
    • Bird with shoes walking in the karroo
    • Making a partially transparent image with GIMP
    • Diplomat walking at the palace
    • Spider in the forest
    • Moving band of images
    • Continuous band of images
    • Endless background – a passing cloudscape

    Further resources on this subject:


    About the Author :


    Mike Ohlson de Fine

    Mike is a graduate Electrical Engineer specializing in industrial process measurement and control. He has a Diploma in Electronics and Instrumentation from Technikon Witwatersrand, an Electrical Engineering degree from the University of Cape Town, and a Masters in Automatic Control from Rand Afrikaans University. He has worked for mining and mineral extraction companies for the last 30 years. His first encounter with computers was learning Fortran 4 using punched cards on an IBM 360 as an undergraduate. Since then he has experimented with Pascal, Forth, Intel 8080 Assembler, MS Basic, C, and C++ but was never satisfied with any of these. Always restricted by corporate control of computing activities he encountered Linux in 2006 and Python in 2007 and became free at last.

    As a working engineer he needs tools that facilitate the understanding and solution of industrial process control problems using simulations and computer models of real processes. Linux and Python proved to be excellent tools for these challenges. When he retires he would like to be part of setting up a Free and Open Source engineering virtual workshop for his countrymen and people in other poor countries to enable the bright youngsters of these countries to be intellectually free at last.

    His hobbies are writing computer simulations, paddling kayaks in wild water, and surf skiing in the sea.

    Books From Packt


    Python Multimedia
    Python Multimedia

    Python Text Processing with NLTK 2.0 Cookbook
    Python Text Processing with NLTK 2.0 Cookbook

    Python 3 Object Oriented Programming
    Python 3 Object Oriented Programming

    MySQL for Python
    MySQL for Python

    Spring Python 1.1
    Spring Python 1.1

    Matplotlib for Python Developers
    Matplotlib for Python Developers

    Python Testing: Beginner's Guide
    Python Testing: Beginner's Guide

    Python Geo-Spatial Development
    Python Geo-Spatial Development


    No votes yet

    Post new comment

    CAPTCHA
    This question is for testing whether you are a human visitor and to prevent automated spam submissions.
    3
    3
    1
    6
    9
    D
    Enter the code without spaces and pay attention to upper/lower case.
    Code Download and Errata
    Packt Anytime, Anywhere
    Register Books
    Print Upgrades
    eBook Downloads
    Video Support
    Contact Us
    Awards Voting Nominations Previous Winners
    Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
    Resources
    Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software