Graphical Python Programming part 2: Write Your Own Screensaver
Getting Random

Akkana Peck
Thursday, May 28, 2009 11:43:19 AM
See the complete program on page 4
In part I,
you learned how to use Python and PyGTK to bring up a window
and draw lines and circles. Let's take that example and extend it
to draw some pretty graphics.
You've already seen that you can draw a line in your PyGTK app
like this:
widget.window.draw_line(xgc, x1, y1, x2, y2)
The color, line thickness and line style will all be taken from
whatever you've set in xgc, your graphics context.
You can use that to make some whizzy color-changing graphics --
lines constantly redrawing in different positions and colors.
You could make a screensaver!
Other Stories on LinuxPlanet
|
Getting random
To pick each new color and position,
you'll need Python's random number generator.
It's very easy to use. Just import the random module.
Then you can use randint to get random numbers. If you
wanted a number between 0 and 10:
import random
r = random.randint(0, 10)
How do you use this to draw lots of lines with colors that vary?
First, let's set up the skeleton of what you'll want to do.
Here's the program from the previous article, except that I've
replaced the contents of expose_handler with a loop
that will draw 100 lines. As yet, it won't work, because
there's no code to set x1, y1, x2, y2:
#!/usr/bin/env python
import gtk, random
# This function will be called whenever you click on the button:
def click_handler(widget) :
# quit the application:
gtk.main_quit()
# This function will be called whenever the drawing area is exposed:
def expose_handler(widget, event) :
xgc = widget.window.new_gc()
#
# Set up initial line endpoints and color here
#
for i in range(0, 200) :
#
# Set up color and x1, x2, y1, y2 here
#
widget.window.draw_line(xgc, x1, y1, x2, y2)
# Create the main window:
win = gtk.Window()
# Organize widgets in a vertical box:
vbox = gtk.VBox()
win.add(vbox)
# Create an area to draw in:
drawing_area = gtk.DrawingArea()
drawing_area.set_size_request(600, 400)
vbox.pack_start(drawing_area)
drawing_area.connect("expose-event", expose_handler)
drawing_area.show()
# Make a pushbutton:
button = gtk.Button("Quit")
# When it's clicked, call our handler:
button.connect("clicked", click_handler)
# Add it to the window:
vbox.pack_start(button)
button.show()
# Obey the window manager quit signal:
win.connect("destroy", gtk.main_quit)
vbox.show()
win.show()
gtk.main()
Let's work on those line positions first. To begin with, you'll need
endpoints for your first line. They can be random, but you want them
to be within the window. so you need to get the window's size, like this:
w, h = widget.window.get_size()
Once you have the size, it's easy to choose some random positions
for a line: for instance, x1 = random.randint(0, w).
So now your loop looks like this:
w, h = widget.window.get_size()
for i in range(0, 200) :
#
# Set up color and x1, x2, y1, y2 here
#
x1 = random.randint(0, w)
x2 = random.randint(0, w)
y1 = random.randint(0, h)
y2 = random.randint(0, h)
widget.window.draw_line(xgc, x1, y1, x2, y2)
Not very impressive looking! (Figure 1.) Even aside from the lack of
color, it would be better if each new line was drawn fairly close to
the previous line.

figure 1
To do that, instead of choosing the line endpoints completely randomly,
instead just change each one a little each time.
Decide on how much you'll let them move in each step, and
call that movesize. Since you want to let the lines move
in any direction, you'll want to pick a random number between
-movesize and movesize, like this:
x1 = x1 + random.randint(-movesize, movesize)
Almost -- but if you try that, you'll find that it doesn't look very
interesting because it almost always ends up off the screen
(Figure 2).

figure 2
You need to "clip" the X and Y values to the size of the
screen. Python doesn't have a clipping function, but it's easy enough
to write one:
def clip(i, min, max) :
if i < min :
return min
if i > max :
return max
return i
Getting there ... (Figure 3).

figure 3
Now the loop looks like this:
movesize = 20
for i in range(0, 200) :
#
# Set up color and x1, x2, y1, y2 here
#
x1 = clip(x1 + random.randint(-movesize, movesize), 0, w)
x2 = clip(x2 + random.randint(-movesize, movesize), 0, w)
y1 = clip(y1 + random.randint(-movesize, movesize), 0, h)
y2 = clip(y2 + random.randint(-movesize, movesize), 0, h)
widget.window.draw_line(xgc, x1, y1, x2, y2)
Getting there. But it's awfully boring without colors.
Next: Getting Colorful »