Python Graphics: Combining Raster and Vector Pictures

Python 2.6 Graphics Cookbook


November 2010

$10.00

Learn how to use Python’s built-in graphics capabilities to create static and animated graphics for a range of real-world purposes. Over 100 recipes take you from basic shape creation to developing interactive GUIs.

 

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?

        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.

            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:


    Books to Consider

    comments powered by Disqus