March 22, 2017

Graphical Python Programming part 2: Write Your Own Screensaver

Getting Random

  • May 28, 2009
  • By Akkana Peck

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!

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:

# 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()

# Create an area to draw in:
drawing_area = gtk.DrawingArea()
drawing_area.set_size_request(600, 400)

drawing_area.connect("expose-event", expose_handler)

# Make a pushbutton:
button = gtk.Button("Quit")

# When it's clicked, call our handler:
button.connect("clicked", click_handler)

# Add it to the window:
# Obey the window manager quit signal:
win.connect("destroy", gtk.main_quit)


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
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
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
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.

Sitemap | Contact Us