Pretty Pictures

Introduction

This sheet will teach you a little about doing graphics (pictures or drawings) in Python. It will also explain a very important idea in programming: functions or procedures.

It’s quite a long sheet. Don’t worry if it takes you a while to get through it all.

What you need to know

  • How to edit and run a Python program.

  • What coordinates are — if you aren’t familiar with these, you may want to talk with your math teacher about them.

Points, lines and colors

In Sheet 1 (Introducing Python), you saw a rather boring example of graphics in Python. Let’s go over it again, a little more carefully.

It began like this:

from gasp import *

begin_graphics()

and ended with the line end_graphics().

The first line there should be familiar by now. The second tells the computer to make a new window in which you can put lines and circles and things. The last line, end_graphics(), waits for the graphics window to be closed and then exits the program.

All the interesting stuff happens in between.

from gasp import *

begin_graphics()

Line((200, 100), (100, 300))

update_when('key_pressed')   # This keeps the graphics window open until
                             # you press a key.
end_graphics()

To understand these lines, you need to know how Python (actually, this isn’t Python itself so much as the gasp module, which is why you have to type from gasp import * at the beginning of all your python code.) thinks about graphics.

Pixels

The graphics window — like everything on the computer screen — is made up of pixels: little squares or rectangles of color. If you look closely at your computer screen, you’ll see what we mean.

Why the funny name pixels? It’s short for picture element.

You refer to places in the graphics window by their coordinates: first, how many pixels the place is to the right of the left-hand edge of the window — the x-coordinate — and then how many pixels it is above the bottom of the window — the y-coordinate.

Here’s a picture.

Coordinates illustration

The current point

When you read Sheet 1 (Introducing Python), we hope you actually tried typing in those commands we’re talking about now. If you didn’t, go back to Sheet 1 (Introducing Python) and type them in now.

The command beginning with Line does exactly what it sounds like it should do — it draws a line. In this case it draws a line starting from the pixel called (200, 100) to the pixel called (100, 300). Because both points have a different y-coordinate (second part) and x-coordinate (first part) the line is drawn at an angle.

You might — or might not — find it helpful to imagine a little man, or a robot, walking around the graphics window. When you say Line((200, 100), (100, 300)) the robot walks over to (200, 100), then drops a little marker at the pixel then as it walks over to the second point (100, 300) it draws a line underneath it creating the line you see on the screen.

Facing the facts

Let’s try to draw something a little more interesting than a plain old line. How about a face? Later you can even go further than that by drawing a whole person.

Unfortunately, it’s not easy to draw anything at all like a face with straight lines. Faces are sort of round, after all. So…

Going round in circles

…you need to be able to draw things that are sort of round. Circles, for instance. Try this little program.

from gasp import *

begin_graphics()

Circle((300, 200), 100)

update_when('key_pressed')  # again, this keeps the graphics window open...
end_graphics()

The three numbers are the two coordinates of the center of the circle, and the radius of the circle (i.e., the distance from the center to the edge).

Try running the same program but with different values for the three numbers, to make sure you understand what they all do.

What does this do? Try to guess before you actually run it.

from gasp import *

begin_graphics()
for r in 100, 110, 120, 130, 140:
    Circle((300, 200), r)

update_when('key_pressed')      # you know what this does by now...
end_graphics()

If you still aren’t sure, ask for help…

So, what about drawing that face? Try to draw something that looks a bit like this:

Face illustration

In other words, you’ll need to draw

  • One large circle — the head

  • Two small circles — the eyes

  • Part of a circle — the mouth

  • Two straight lines — the nose

You’ll find this easier if you have a bit of squared paper or graph paper. Failing that, you might find it helpful to draw some for yourself! Then, sketch the face on the squared paper:

Grid illustration

If you’re really too lazy, you can just use the face-on-a-grid above!

That grid is 10 squares on each side. Let’s suppose that the very center of the grid is the point (300, 200) (there’s nothing special about that point; it’s just somewhere near the middle of the graphics window), and that each of our little grid squares is 10 pixels across.

The mouth is a little tricky, so we’ll worry about that a little later on. Everything else should be pretty easy to locate. For instance, the big circle has its center at (300, 200) and its radius is 4 small squares, or 40 pixels. The left eye has its center at (285, 210) and its radius is 5 pixels (half a square). We’ll let you work out all the other coordinates you need. So:

Write a program that draws all of that face except the mouth.

Mouthing off

The mouth, as we said, is harder. You need to be able to draw just part of a circle. You can do this by using a special command just like Line and Circle, this time called Arc. All you need to know is where the center of the arc will be, the radius of the circle, and then two angles. These angles determine where on the circle the computer starts and stops drawing.

It turns out that what you need to add to your program is:

Arc((300, 200), 30, 225, 315)

That’s a bit intimidating, so we’ll try to explain. First you give the center of the circle, just as if you were drawing all of it. Then you tell the computer the radius of the circle, just like when you drawing a full circle. The last set of numbers are the angles, since a circle is made up of 360 degrees, that’s what you’ll use to tell the computer where to start and stop drawing the circle.

What should happen when you type Arc((300, 200), 30, 225, 20)? What actually happens?

The reason this happens is because when you pass the 360 mark on a circle, it doesn’t automatically jump back to 0. Instead it keeps on counting. This means that typing Arc((300, 200), 30, 225, 20) is the same as typing Arc((300, 200), 30, 225, 380).

Challenge 1

Modify your face-drawing program so that it draws the face in a different place. You’ll need to change all the numbers that define positions of points. You won’t need to change the numbers that say how big the circles are!

Challenge 2

Make some other changes to the face, to get the hang of how these graphics commands work. For instance:

  • Move the eyes up a little.

  • Add eyebrows — lines across the eyes, maybe sloping a bit.

  • Add ears, or hair.

  • Change the shape of the nose slightly.

Two heads are better than one

What if you wanted to draw two of these faces?

You could copy out everything in your program twice, and then change one copy. This would work, but it would be boring and easy to get wrong. And if you then wanted a third face, you’d have to go through all the effort again.

There’s a better way.

You already know that Python lets you give names to numbers, strings, lists and other things. It also lets you give names to bits of program. A piece of Python program with a name is called a function — for complicated reasons that don’t matter just now.

Sometimes the word procedure is used instead. For instance, the language Logo – which you may have used in school – calls them procedures.

Functions

There’s much more about functions in Sheet F (Functions). When you’ve read the following stuff, you might like to turn to Sheet F. It might also be helpful if you get confused. Type in the following:

note: the prompt Python prints at the start of each line will change, like in the for loop examples way back in Sheet 1 (Introducing Python). Don’t forget to look out for spaces at the starts of lines!

>>> def shout():
...     print('Python is wonderful!')
...     print('And so am I.')
...                                      # Just press Enter here.

What you’ve just done is to teach Python a new trick. If you now say

>>>  shout()

what do you think will happen? Try it.

What you’ve done is to define a new word that Python will understand — def is short for define.

Challenge 3

Write a definition so that saying moan() will make the machine print:

Python is useless!
And so are these worksheets.

Try it out and make sure it works.

A good argument

Functions are more than just ways of saving typing, though. Try typing in the following definition, and think about what it might mean.

>>> def twice(x):
...     print(x, '+', x, 'is', x + x)
...                                     # Just press Enter here.

When you think you understand it (or when you’ve decided you’ll never understand it), experiment with it. Ask Python for twice(3), or twice(99), or even twice('ouch').

What’s going on here is rather clever. When you say twice(7), the machine does the stuff inside the definition of twice (just like it did for shout and moan earlier), except that x is 7. So it’s rather as if you’d said

print(7, '+', 7, 'is', 7 + 7)

For some reason, things that get inside function definitions in this way are called arguments. So, when you say twice('beri'), the string 'beri' is the argument to the function twice. You’ll need to know this word to understand what happens if you type twice() instead of twice(7). Try it.

Challenge 4

Write a function that behaves a bit like twice but, instead of telling you what x + x is (where x is the argument of the function), tells you what x * x is. Try it out. (Unfortunately it won’t work on strings!)

A function can have more than one argument. The way this works is pretty obvious once you’ve seen it, so we’ll just give an example.

def add(x, y):
    print(x, '+', y, '=', x + y)

Then you can say add(7, 5) and the computer will tell you what happens when you add 7 and 5.

Built-in functions

Incidentally, lots of the things you’ve told Python to do are really functions. For instance, when you say Line((100, 200), (300, 150)) you’re just using a function with two arguments (two sets of coordinates). The only difference is that you didn’t have to write the definition of the Line function yourself.

As we were saying…

This is all a digression. Before we started blathering about functions, you were thinking about how to draw two — or more — of those faces. Maybe you’ve guessed what’s coming next: the right thing to do is to write a function that draws a face. The function should take arguments saying where in the graphics window the face should go. When you’ve done that, drawing 5 faces will just mean 5 uses of your function, instead of having to copy out the face-drawing program 5 times and make lots of changes to each copy.

So, what should your face-drawing function look like?

It needs to be able to be told where to draw the face, so let’s give it two arguments saying where — like the two arguments to move or draw.

So far, you know that the function will look like this.

def draw_face(x, y):
    blah blah    # Whatever you need to draw a face at (x, y)

Obviously filling in the blah blah is the difficult bit.

What exactly should the arguments, x and y, mean?

When you were designing the face, you thought about the positions of all the bits of the face relative to the point right in the middle of the big circle. That was (300, 200), remember?

So a reasonable idea would be to say that x and y are the coordinates (i.e., the two parts of the name) of the pixel right in the middle of the face. So, saying draw_face(300, 200) ought to do exactly the same as the program you’ve already written.

Challenge 5

If you’re feeling brave, go back to your face-drawing program and try to turn it into a face-drawing function. Then test the function. — If you’re not feeling brave, read on and we’ll try to lead you through the process.

Note: If you want to take up this challenge, you should stop reading right now.

To begin with, edit your face-drawing program so that instead of saying

first line
second line
third line
etc

it says

def draw_face(x, y):
    first line
    second line
    third line
    etc

You now have a function that takes two arguments, and then ignores them and draws a face centered at (300, 200) whatever the arguments were. Now you need to tidy it up so that the face is centered at (x, y) instead.

The outline

The first thing you drew originally was the circle around the outside of the face. This also happens to be the easiest thing to adapt for putting into our function.

Originally, you had a line Circle((300, 200), 40). The first two arguments to Circle say where the center of the circle is, so they should become x, y. The last argument (40) says how big the circle is. All your faces are going to be the same size, so that should be left alone.

Incidentally, you’ll find all this much easier to follow if you look at the face-on-a-grid earlier in this sheet.

So the new line should be Circle((x, y), 40).

The eyes

You drew the eyes next. Originally the eyes were at (285, 310) and (315, 310) — 15 pixels away from the middle horizontally (one in each direction), and both 10 pixels above the center.

Well, that’s easy enough. Here’s how to draw the left eye; you can probably work out how to draw the right eye.

Circle((x-15, y+10), 5)

The nose

The nose is made up of two lines. The first one goes from 10 pixels above the very center of the face to 10 pixels leftward and 10 pixels downward from the center. That’s easy enough.

Line((x, y+10), (x-10, y-10))

The second line goes from where the first one left off, to a point 10 pixels down and 10 pixels to the right from the center of the face. we’ll leave drawing that line up to you,

The mouth

You might expect this to be harder than the other bits. Instead, it’s only about as difficult as figuring out the eyes.

The reason for this is that you only need to edit two of the numbers — the coordinates of the circle. This is because the other numbers — the radius and the angles — are all based off of the coordinates of the arc, not the center of the face.

We’re going to be mean, and leave you to work out exactly how to adapt the line of program that draws the face’s mouth. It really isn’t very difficult.

Does it work?

Test your function. Add a few lines below it:

draw_face(300, 200)
draw_face(400, 200)
draw_face(350, 270)

and run the program. Do you get three heads? Or a ghastly mess of detached bits of head? Or what? It’s important to put those lines below the definition of draw_face, because until it’s read that definition the computer doesn’t know what draw_face is.

The whole person

If you’ve had enough of graphics, you might want to skip ahead a little. But if you’re still having fun, you might like to use your head-drawing function to make a person-drawing function.

_images/stick-basic.svg

Here’s a simple stick figure. Sketch something like this on graph paper or squared paper, and use it to design a function that draws a stick person. Where I’ve just drawn a circle for the head, you should actually use the draw_face() function.

_images/stick.svg

This may take you a while. You’ll have to think carefully about what arguments you give to draw_face to make the head and the body fit together properly.

Make sure your functions work by making your program draw several stick figures in different places on the screen.

You may well find it much easier if you start by just drawing a single stick figure anywhere, and then turn it into a function in the same sort of way as you did for the face.

Challenge 6

Work out how to give one of your stick figures a hat.

I do not know what should go in here

Some nice patterns

This doesn’t have anything to do with the stick figures. We’re just including it because we think it looks nice. Give it a try and see if you agree.

from gasp import *

begin_graphics()

for i in range(0, 400):
    Line((i, 0), (0, 400-i))

The weird thing about this is that all it does is to draw a lot of straight lines, but the result is to produce a nice smooth curve!

Ending it all

At the end of any program that draws graphics, you should actually have a line

end_graphics()

to tell the computer that you’re finished with the graphics window. When it sees that line, the computer will sit and wait until you close the graphics window — if it just went ahead and closed it for you, you might not have time to see what it had drawn in the window. That would be a shame.

What next?

  • Read Sheet G (Graphics/Gasp) for more information on doing graphics with Python

  • Work out how to draw faces and people of different sizes as well as in different places. Work out how to make them face in different directions, too. Then you can draw a scene in which some of them are talking to others, or standing alone, or whatever.

You could also just go on to Sheet 4 (Higher! Lower!), in which you’ll write a simple game, which doesn’t use graphics, we’re afraid.