PythonGTK Programming part 3: Screensaver, Objects, and User Input
Python Objects

Akkana Peck
Wednesday, June 10, 2009 11:16:57 PM
But wait! Don't add another global variable yet! I mentioned
in the last article that it's bad programming style to use globals like that.
They make it harder to maintain your code -- the variables are
declared far away from where they're used -- but more important,
they make it hard to re-use your code as part of another program.
For a simple one-time script that isn't so important, but as you
develop more code you'll find it becomes much more important.
Fortunately, Python objects let you clean up the code nicely,
and they also play well with PyGTK.
Object-oriented Python is mostly fairly straightforward. You
define classes, like, say, QixWindow; then you can have
objects which are instances of those classes, so you
could say something like:
mywindow = QixWindow()
to create a new object of type QixWindow.
You could potentially have lots of different QixWindows on the
screen at once; mywindow is just one of them.
("Qix", for you youngsters, was a video game in the 70's that had
a pattern of lines moving around the screen kind of like the one
in this program.)
A class is defined like this:
class QixWindow :
Let's start by moving those global variables inside the class.
If you've used other object-oriented languages, Python's syntax is a little
different. As you define functions and variables inside the class,
you'll have to make references to self -- the current object.
(Some languages use the word this instead of self.)
Each QixWindow has a function that's run when it's created,
called __init__ (that's two underscores on each end).
Use that to define the initial values of variables inside the class.
If you move all those globals into the class, they look like this:
class QixWindow :
def __init__(self) :
self.xgc = None
self.x1 = None
self.x2 = None
self.y1 = None
self.y2 = None
self.r = random.randint(0, 65535)
self.g = random.randint(0, 65535)
self.b = random.randint(0, 65535)
self.movesize = 20
self.colorjump = 2048
Each function inside the class will take self as its first
argument, like __init__ does. Furthermore, they will need to use "self."
in front of any member variable, like this:
widget.window.draw_line(self.xgc, self.x1, self.y1, self.x2, self.y2)
Those selfs are an irritating feature of object-oriented Python
and will make your program lines much longer,
but the benefits of Python objects are worth it in the long run.
Other Stories on LinuxPlanet
|
Static functions
There's one type of function that doesn't need self
as an argument: static functions -- functions that don't
need access to any member variables. For instance, clip, the
function that ensures a number lies between a low and a high boundary,
doesn't need any of those other variables. So you can define it like this:
@staticmethod
def clip(i, min, max) :
if i < min :
return min
if i > max :
return max
return i
and call it like this from within the idle handler:
self.x1 = QixWindow.clip(self.x1 + random.randint(-self.movesize, self.movesize), 0, w)
Converting the existing program
That's most of what you need to know to translate your existing procedural
Python script to an object-oriented Python program. Take each existing
function and move it inside the QixWindow class, indenting every line
by four extra spaces; add "self" as the first argument to every function,
unless it will be static; remove the "global" lines that declare the
global variables, and add "self." in front of every reference
to one of the global variables or any member function and "QixWindow."
in front of any call to static functions like clip.
Finally, take the code that creates the window and move that
into a function, then call the function from outside the class.
That sounds like a lot, doesn't it? But it's fairly straightforward,
just tedious. The only tricky part is remembering when you need to
add self.
To save you the work, here's the the finished product.
I've added a new variable, is_fullscreen, so you can toggle in and
out of fullscreen mode.
Next time: let's add mouse events and turn that screensaver into a game!
Next: The Whole Program »