PythonGTK Programming part 3: Screensaver, Objects, and User Input - page 2
The Fullscreen Function
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
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!
- 1Linux Top 3: Fedora 24, Peppermint 7 and Solus 1.2
- 2Linux Top 3: Alpine Linux 3.4, deepin 15.2 and Linux Lite 3.0
- 3Linux 4.7 Set to Boost Live Patching, Security and Power Management
- 4Linux 4.6 Charred Weasel adds USB 3.1 Support
- 5Linux Top 3: OpenIndiana 2016.04, Ubuntu 16.04 and Debian's New Leader