Copyright © 2006 David Schmidt

Chapter 8:
Classes and Objects


8.1 What is a class? What is an object?
8.2 Using the Tkinter classes to construct GUIs
    8.2.1 Frames and layouts
    8.2.2 Customizing widgets
8.3 Event handling
    8.3.1 Design study: Telephone directory in an MVC-architecture
8.4 Customized event handlers for customized buttons
    8.4.1 Customized event-handling functions
    8.4.2 Customized events
    8.4.3 Customized handler classes
    8.4.4 Controllers can contact one another
    8.4.5 Model-View-Controller architecture
8.5 The general format for writing a class
    8.5.1 Case study: Smiley objects
    8.5.2 Placing a class in a file by itself
8.6 Subclasses: Extending a class with new functions
    8.6.1 Case study: Extending class Smiley
    8.6.2 Subclassing class Button to build a controller
    8.6.3 Building GUIs in Python
8.7 Animations
    8.7.1 Sprites are moving objects
    8.7.2 User input and animation loops
8.8 Installing the support software for animations
8.9 Summary


Look at the Graphical User Interfaces (GUIs) in Dawson, Chapter 10: There are buttons, text fields, boxes, etc., in them. There are lots of little things --- objects --- assembled together to make a GUI.

In particular, look at the GUI for the program Lazy Buttons:


There are three buttons placed inside a frame. Each button has its own label and its own position in the frame. Yet, each button is coded more-or-less the same and was constructed from the same piece of coding. We will learn how to construct the three button objects from the one program piece (called a class).

Next, look at the Pizza-Panic game in Dawson, Chapter 11: When the game operates, there are multiple pizza objects that appear and move downwards across the display. The pizzas look the same, but each has a unique position and velocity. We will see that the pizza objects are constructed from a pizza class.


Finally, look at the Astrocrash game in Chapter 12: when the game operates, you see multiple asteroids that move across the frame. The asteroids are similar, but each has its own size, position, direction, and velocity. The asteroid objects are constructed from an asteroid class.

When a program must make many copies of a slightly complicated, ''little'' data structure (e.g., a GUI button or a pizza or an asteroid), it is best to make copies from the same piece of program code. A module is a good construction for making a data structure, but remember there can be only one copy of a module per program --- we cannot make (import) multiple copies! This is a fundamental limitation of modules.

To solve this problem, Python uses a construction called a class, which looks like a module one writes to implement a data structure. A class is a kind of ``mini-module.'' But unlike a module, one can ``import'' (construct) multiple copies of the class within the same program. Each copy is called an object. This is how buttons, pizzas, and asteroids are constructed within Dawson's programs.

Our first goal is to become intelligent users of pre-written classes for GUIs and animations, and our second goal is to become a writer of simple classes.


8.1 What is a class? What is an object?

Look again at the picture of the ``Lazy Buttons'' GUI --- there are three buttons displayed. What does a button look like inside the computer? It must be some kind of data structure, but what? A list? A dictionary? A button has these attributes: The last attribute is in fact a function, and this suggests a button is a collection of some variables and some functions --- like a module --- with its own namespace that holds the button's attributes.

But a module can be imported only once, and the example GUI has three buttons, and there must be three namespaces, one for each button.

A class is a ``mini-module'' that can be ``imported'' more than once. Each time it is ``imported'', the class constructs in heap storage a new namespace, called an object.

The namespaces must be given distinct names, so Python uses a syntax like this to construct the three button objects and give each a distinct name. Say that Button is the name of the class that holds the coding for making a button:

button1 = Button( ... )
button2 = Button( ... )
button3 = Button( ... )
The three objects (namespaces) are constructed in heap storage, and the addresses of the namespaces are assigned to the three variable names --- this is how we can distinguish the three buttons in our programming of the GUI --- by their variable names, button1, button2, and button3.

Now, a module can hold both variables and functions, and the objects we construct also appear to hold both variables and functions. We reference an attribute of an object, like a button object, by mentioning the button's name and the attribute's name, in module-style ``dot notation,'' like this:

button2.configure( ... )
This calls the configure function in the namespace for object button2. This reinforces our intuition that an object is a ``mini module,'' and that we have three disctinct Button mini-modules.

For the moment, pretend that a Python class is coded just like a Python module, that is, as a file holding variable definitions and function definitions. Actually, a class is coded a bit differently than a module is, but we need not worry about the details just yet --- we want to learn how to use classes first and how to write them second.


8.2 Using the Tkinter classes to construct GUIs

A typical Graphical User Interface (GUI) is a computerized assembly of Windows, Frames, Buttons, Labels, Textentries, Textareas, Menus, and many other things. In Python, one builds a GUI from a large set of classes that are grouped together in a huge module named Tkinter.

A good reference to Tkinter is found at http://www.pythonware.com/library/tkinter/introduction/ You will need this reference to build interesting GUIs.

To build a Tkinter GUI, one starts with a window into which one inserts a frame. Into the frame, one inserts buttons, labels, and other items. The items must be arranged within the frame by using some form of layout, typically a ``grid'' layout.


8.2.1 Frames and layouts

Here is a first example that shows how we construct a window, then a frame, and within the frame, a label, a textentry, and two buttons:


Here is the Python program that constructed the window:
FIGURE 1===================================================

# Demo0 shows a window holding a frame holding
#   a label, a textentry, and two buttons:

# Tkinter is a huge module that holds lots of classes:
from Tkinter import *

# construct a window object:
window = Tk()
# give it a title:
window.title("Demo0")

# construct a frame and attach it to the window:
frame = Frame(window)
# place the frame within the window using ``grid layout'':
frame.grid()

# construct a label and attach it to the frame:
label = Label(frame, text = "I am a label")
# position it in the frame:
label.grid()

# construct a text entry and attach it to the frame:
textentry = Entry(frame, width = 20)
textentry.grid()

# construct a button and attach it to the frame:
button1 = Button(frame, text = "Press me")
button1.grid()

# again:
button2 = Button(frame, text = "Don't press me")
button2.grid()

# This command starts the window's controller function so that
#  the window can respond to the user's interactions with it:
window.mainloop()

ENDFIGURE=====================================================
The program uses the classes, Tk, Frame, Label, Entry, and Button to construct objects and assemble them into a GUI. Notice how we use the title method of the window to cause the title to appear at the top of the window. Each object has a grid method, which positions the object within its ``parent'' object. (The parent of a frame is the window, the parent of a button is the frame, etc.)

Notice the use of keyword parameters in the construction of the label, textentry and buttons:

label = Label(frame, text = "I am a label")
 . . .
textentry = Entry(frame, width = 20)
 . . .
button1 = Button(frame, text = "Press me")
Recall that the keywords label the arguments that will be assigned to the parameters of the same name within the body of the method. (In Chapter 6A, we saw that a function like,
def f(x) :
    ... x ...
can be called in the classic way, like f(3), or with keyword parameters, like f(x = 3). The latter case is often preferred because it is easier to read and makes explicit the assignment of argument to parameter. It will prove useful for using the GUI classes)

As an exercise, start the Python interpreter and type the above commands one at a time, like this.:

$python
>>> from Tkinker import *
>>> window = TK()
>>> window.title("Demo0")
>>> frame = Frame(window)
>>> frame.grid()
 etc.
You will see an empty window appear, then see its title appear, then see the window ``collapse'' around an empty frame, then see a label appear in the frame, and so on.

Objects that paint graphical components on the display are called widgets. Here is a drawing of the widgets constructed in heap storage by the above program:


Each widget (object) remembers the class it is constructed from as well as its local variables (attributes).


8.2.2 Customizing widgets

The appearances of the label and buttons in the above example are not so attractive, and the objects are positioned rather awkwardly within the frame. With the use of additional keywords and methods, we can customize our example window, say, like this:


Here is the program that generated the GUI:
FIGURE 2======================================================
 
# Demo1 shows a window holding a frame holding
#   a label, a textentry and two buttons, laid out in a grid.
# Various fonts and colors are used with the widgets.

from Tkinter import *

window = Tk()
window.title("Demo1")
# set the window's size at 200 pixels width by 80 pixels height:
window.geometry("250x150")

frame = Frame(window)
frame.grid()

# construct the label with foreground (fg) color of red, using Arial bold font:
label = Label(frame, text = "I am a label", fg = "red",
              font=("Arial", 12, "bold") )
# place the label in the frame's grid, at position 0,0, left (W) justified:
label.grid(row = 0, column = 0, sticky = W)

textentry = Entry(frame, width = 20)
# place it in the grid at 1,0 extending into 1,1:
textentry.grid(row = 1, column = 0, columnspan = 2)

# construct a button and insert it into the frame:
button1 = Button(frame, text = "Press me", font=("Arial", 14, "bold"))
# ``pad'' the button with 10 extra pixels all around:
button1.grid(row = 2, column = 0, padx = 10, pady = 10)

# again, but make the button's background (bg) white:
button2 = Button(frame, text = "Don't press me", bg="white")
button2.grid(row = 2, column = 1)


window.mainloop()

ENDFIGURE==========================================================

The meanings of most of the new keyword tricks are easy to guess. The row and column keywords used with the grid method let one place objects within the frame as if the frame was a grid whose cells were numbered (0,0), (0,1), ..., (1,0), (1,1), ..., and so on.


8.3 Event handling

The two GUIs just seen are useless --- when one types text into the text entry or presses the buttons, nothing happens. We must ``activate'' the buttons by attaching to them event handling functions (also known as event handlers).

A press of a button causes an event, and the associated event handling function is automatically called (by a combination of efforts of the operating system, the Python interpreter, and Tkinter) when the event occurs. Each button should have its own event handling function.

Here is an example. This GUI lets a person type some text into a textentry:


When the user presses the button, the text is copied to a label at the bottom of the GUI:

This GUI's button is connected to an event handling function that does the copying. Here is the program:
FIGURE 3==================================================

# Demo2 shows a window holding a text entry, a button, and a label.
#   When the button is pressed, the contents of the text area is
#   copied into the label.

from Tkinter import *

def handleButtonPress() :
    """This is the event handler for the button: It copies
       the text typed into the textentry to the label, and it
       clears the textentry.
    """
    message = textentry.get()   # get the text from the textentry
    label.configure(text = message)   # reset the label's text
    textentry.delete(0, END)          # clear the textentry
    print message               # print trace info to the command window


myfont = ("Arial", 14, "bold")

window = Tk()
window.title("Demo1")
window.geometry("220x150")

frame = Frame(window)
frame.grid()

textentry = Entry(frame, width = 15, font = myfont, fg = "red")
textentry.grid()

# IMPORTANT: attach  handleButtonPress  as the event handler to the button:
button1 = Button(frame, text = "Copy", command = handleButtonPress,
                 font = myfont, fg = "blue", bg = "yellow")
button1.grid()

label = Label(frame, text = "********", font = myfont)
label.grid()

# This command starts the window's controller function:
window.mainloop()

ENDFIGURE==========================================================
The command that constructs the button,
button1 = Button(frame, text = "Copy", command = handleButtonPress,
                 font = myfont, fg = "blue", bg = "yellow")
also attaches the event handling function, by means of the command keyword. When the button is pressed, the operating system contacts the Python interpreter, which contacts Tkinter, which starts the event handling function. The command,
label.configure(text = message) 
uses the configure function with object label to reset the text displayed by the label.

The configure function can be used to reconfigure any attribute of any widget.


8.3.1 Design study: Telephone directory in an MVC-architecture

The GUIs seen so far have a pretty appearance but no intelligence. The solution is to connect a GUI to a model module, which can act as the computational ``brain'' of the program.

Let's reuse the TelephoneBook module from the previous chapter and build a GUI for it that lets a person insert, lookup, and print telephone numbers. The GUI looks like this:


The use can insert a name and phone number by typing the information into the text field and pressing Insert:

Later, the number for the person can be retrieved by typing the person's name and pressing Lookup:

The third button prints the complete contents of the telephone book in the command window.

The GUI code lives by itself in the ``view module.'' The program uses three event handler functions, one per button, and when an event handler is called, it calls a corresponding function in the TelephoneBook module. The event handler functions are actually the program's ``controller,'' and if they were large and complex, we would move them into their own module, a ``controller module.'' But we won't do that here.

Here is the view module along with the event-handler functions.

FIGURE==========================================================

"""The view module shows the GUI for the TelephoneBook application."""

from Tkinter import *

import TelephoneBook

#### The event handling functions:

def handleInsert():
    """This is the event handler for the Insert button: It inserts 
       the name and telephone number typed by the user.
    """
    data = textentry.get()
    items = data.split(":")
    name = items[0]
    number = int(items[1])
    number = TelephoneBook.insert(name, number)
    textentry.delete(0, END)
    label2.configure(text = "")


def handleLookup() :
    """This is the event handler for the Lookup button: It looks up
       the telephone number of the person typed by the user.
    """
    name = textentry.get()
    number = TelephoneBook.lookup(name)
    label2.configure(text = name + " has number, " + str(number))
    textentry.delete(0, END)

def handlePrint() :
    """This prints the contents of the telephone book to the
       command window.
    """
    TelephoneBook.printBook()

########

myfont = ("Arial", 14, "bold")

window = Tk()
window.title("Telephone Directory")
window.geometry("400x170")

frame = Frame(window)
frame.grid()

label1 = Label(frame, text = "Input: ", font = myfont)
label1.grid(row = 0, column = 0)

textentry = Entry(frame, width = 15, font = myfont)
textentry.grid(row = 0, column = 1)

button1 = Button(frame, text = "Lookup",
                 command = handleLookup,
                 font = myfont, fg = "blue", bg = "white")
button1.grid(row = 1, column = 0, padx = 10, pady = 10 )

button2 = Button(frame, text = "Insert",
                 command = handleInsert,
                 font = myfont, fg = "blue", bg = "white")
button2.grid(row = 1, column = 1, padx = 10, pady = 10 )

button3 = Button(frame, text = "Print",
                 command = handlePrint,
                 font = myfont, fg = "blue", bg = "white")
button3.grid(row = 1, column = 2, padx = 10, pady = 10)

label2 = Label(frame, text = "          ", font = myfont)
label2.grid(row = 2, column = 0, columnspan = 2)

window.mainloop()

ENDFIGURE=============================================
The coding is routine, because all the computational effort is embedded in the functions in the model module, which we reproduce from the previous chapter:
FIGURE=================================================

# TelephoneBook   holds the data structure and maintenance functions
#  for a telephone book.

# The data structure is a list of  (name,number)  pairs
# Example:  book = [("Mary Smith", 3453), ("Jake Jones", 2458)]
book = []   # the book starts empty

def printBook() :
    """printBook  prints the entire contents of the phone book"""
    global book
    for entry in book :
        print entry[0] + ":",  entry[1]
    
def inBook(name) :
    """inBook checks if  name  is already in the phone book.
    
       parameter: name, a string - a person's name
       returns: the index number of where the name is found in the book;
         returns -1, if the name is not in the book
    """
    global book
    answer = -1
    index = 0
    for entry in book :
        if entry[0] == name :
            answer = index
            break
        else :
            index = index + 1
    return answer

def insert(name, number) :
    """addPerson  adds a new  name and number to the phone book,
       provided that the name is not already in the book.

       parameters: name - a string, the person's name
                   number - an int, the four-digit phone number
    """
    global book
    if  inBook(name) == -1 :  # name not in the book?
        book = book + [(name,number)]
    else :
        print "error: " + name + " is already in the book"



def lookup(name) :
    """lookup searches the phone book for name.

       parameter: name - a string, the person's name
       returns: the phone number, an int, for the person
         (if the name isn't in the book,  -1 is returned)
    """
    global book
    number = -1
    for entry in book :
        if entry[0] == name :
            number = entry[1]
            break
    return number

def delete(name) :
    """delete removes the entry for  name  in the book.

       parameter: name - the name to be removed
    """
    global book
    where = inBook(name)
    if where != -1 :
        book = book[:where] + book[where+1:]

ENDFIGURE=========================================================
This module organization is standard for programs that use GUIs: The code for building the GUI is kept separate from the code that does the computation. The latter is typically saved in a model module that has functions which are contacted to do the computation. The module that holds the GUI code is called the View module. When the event-handling functions are placed in their own module, which is often the case when the functions are detailed, the third module is called the controller module, because its functions control the computation.

The resulting program --- a trio of model, view, and controller modules --- is connected in a Model-View-Controller (MVC) architecture, which we surveyed in the previous chapter.

Design study: Pig-latin translator in an MVC-architecture

Here is a second example. Perhaps we have built a module that maintains a dictionary of translations of English words to Pig Latin. (Recall that ``tree'' translates to ``eetray'', ``schedule'' to ``eduleschay,'' ``and'' to ``andshay,'' etc., in Pig Latin --- leading consonants are moved to the end of the word and ``ay'' is appended. If there are no leading consonants, ``shay'' is appended.)

The module can be connected to a GUI. The user types an English word:


When she presses the Translate button, the translation appears:

The Dump button prints a dictionary of all the English-to-pig-latin translations accomplished so far.

The GUI code lives by itself in the ``view module.'' The program uses two event handler functions, one per button, and when an event handler is called, it calls a corresponding function in the PigLatin module. The event handler functions are actually the program's ``controller,'' and if they were large and complex, we would move them into their own module, a ``controller module.'' But we won't do that here.

Here is the view module along with the two event handler functions.

FIGURE 5==========================================================

"""The view module shows the GUI for the PigLatin translator."""

from Tkinter import *

import PigLatin

#### The event handling functions:

def handleTranslate() :
    """This is the event handler for the Translate button: It looks up
       the PigLatin translation of the word typed by the user.
    """
    english_word = textentry.get()
    pig_word = PigLatin.lookup(english_word)
    label2.configure(text = '"' + english_word + '"  translates to  "' + pig_word + '"')
    textentry.delete(0, END)

def handleDump() :
    """This prints the contents of the PigLatin dictionary to the
       command window.
    """
    PigLatin.printPig()

########

myfont = ("Arial", 14, "bold")

window = Tk()
window.title("PigLatin Translator")
window.geometry("400x170")

frame = Frame(window)
frame.grid()

label1 = Label(frame, text = "English word: ", font = myfont)
label1.grid(row = 0, column = 0)

textentry = Entry(frame, width = 15, font = myfont)
textentry.grid(row = 0, column = 1)

button1 = Button(frame, text = "Translate",
                 command = handleTranslate,
                 font = myfont, fg = "blue", bg = "white")
button1.grid(row = 1, column = 0, padx = 10, pady = 10 )

button2 = Button(frame, text = "Dump",
                 command = handleDump,
                 font = myfont, fg = "blue", bg = "white")
button2.grid(row = 1, column = 1, padx = 10, pady = 10)

label2 = Label(frame, text = "          ", font = myfont)
label2.grid(row = 2, column = 0, columnspan = 2)

window.mainloop()

ENDFIGURE======================================================
The coding is entirely routine, because all the serious computational effort is embedded in the functions in the model module:
FIGURE 4========================================================

"""PigLatin models a dictionary that maps English words to their pig-latin
   translations and counts the number of times each English word was
   consulted.
"""

# Variable  pig  holds the dictionary.  It uses English words as keys. 
# Each key maps to a 2-list, holding the corresponding pig-latin word
#   and the count of how many times the word has been looked up:
#         pig : { [ word : [ pig_latin_translation, lookup_count ]* }
#
# Example, built from the input,  "It is, is it not?"
#   pig : { "it":["itshay", 2],  "is":["isshay", 2],  "not":["otnay", 1] }

pig = { }  # starts at empty


def lookup(word) :
    """lookup  looks up  word  in the dictionary to find its pig-latin
       equivalent.

       If  word  isn't in the dictionary, the translation is made on the
       spot, stored in the dictionary, and returned.
       In all cases, the count for the word is increased by one.

       parameter: word - the English word to translate
       returns: the pig-latin translation of the word.
    """
    global pig

    if not(word in pig) :  # word not there ?
        pig[word] = [translate(word), 0]  # translate and insert it

    translation =  pig[word][0]
    pig[word][1] = pig[word][1] + 1 
    return translation


def translate(word) :
    """translate  converts an English word into pig-latin.

       parameter: word - the English word
       returns the word in pig-latin.
    """
    vowels = "aeiou"
    if word[0] in vowels :
        word = word + "sh"   # no leading consonants, so attach "sh" to end
    else :
        # move the leading consonant to the end and add  y  to  vowels:
        word = word[1:] + word[0]
        vowels = vowels + "y"
        # rotate remaining leading consonants to the end of the word:
        # IMPORTANT: every English word holds at least one vowel!
        while not(word[0] in vowels) :
            word = word[1:] + word[0]
    word = word + "ay"
    return word


def printPig() :
    """printPig prints the contents of the dictionary, words arranged alphabetically.
    """
    global pig
    keylist = pig.keys()  # builds a list of all the keys used in   pig
    keylist.sort()  # reorders the list alphabetically
    for k in keylist :
        record = pig[k]
        print k, ":", record[0], record[1]

ENDFIGURE===========================================================
This module organization is standard for programs that use GUIs: The code for building the GUI is kept separate from the code that does the computation. The latter is typically saved in a model module that has functions which are contacted to do the computation. The module that holds the GUI code is called the View module. When the event-handling functions are placed in their own module, which is often the case when the functions are detailed, the third module is called the controller module, because its functions control the computation.

The resulting program --- a trio of model, view, and controller modules --- is connected in a Model-View-Controller (MVC) architecture, which we surveyed in the previous chapter.


8.4 Customized event handlers for customized buttons

The previous examples suggested that each button should have its own event-handling function. What if we must build many many buttons and the event-handling functions are basically the same for each of the buttons --- must we copy-and-paste code? Here is an example that shows the problem and leads us to several important solution techniques.

Say that we have a GUI whose buttons display on their faces the number of times the buttons are pressed:


When a button is pressed, its count increases:

Each button has its own event handling function, and it is not too painful to write the three buttons and their functions; it looks like this:

=========================================================

"""A GUI with three counter buttons"""
from Tkinter import *

myfont = ("Arial", 16, "bold")

window = Tk()
window.title("Three counters")
window.geometry("300x120")

frame = Frame(window)
frame.grid()

# Construct three buttons:
button0 = Button(frame, font = ("Arial", 14, "bold"), text = "0",
                 fg = "blue", bg = "white", width = 4, height = 2)
button0.grid(row = 1, column = 0, padx = 10, pady = 10 )

button1 = Button(frame, font = ("Arial", 14, "bold"), text = "0",
                 fg = "blue", bg = "white", width = 4, height = 2)
button1.grid(row = 1, column = 1, padx = 10, pady = 10 )

button2 = Button(frame, font = ("Arial", 14, "bold"), text = "0",
                 fg = "blue", bg = "white", width = 4, height = 2)
button2.grid(row = 1, column = 2, padx = 10, pady = 10 )

#Construct three event-handling functions for the three buttons:
count0 = 0
def handle0() :
    global count0
    count0 = count0 + 1
    button0.configure(text = str(count0))
button0.configure(text = str(count0), command = handle0)

count1 = 0
def handle1() :
    global count1
    count1 = count1 + 1
    button1.configure(text = str(count1))
button1.configure(text = str(count1), command = handle1)

count2 = 0
def handle2() :
    global count2
    count2 = count2 + 1
    button2.configure(text = str(count2))
button2.configure(text = str(count2), command = handle2)

window.mainloop()

========================================================
Each event-handling function maintains its own integer counter variable, which it uses to reconfigure the text on the button it controls.

This is not a pretty program. Worse yet, what if the GUI portrayed a ``game board,'' where each button represented a square on the board? Here is an example of an 8-square GUI:


Would we write 8 distinct yet basically identical event-handling functions? NO.

We now examine no less than three possible solution strategies for this problem. The intention of each stategy is to ``customize'' one form of event-handling for each button in the GUI:

  1. The first approach generates a customized event-handling function for each button constructed. (This approach can be used for GUIs written in C#.)
  2. The second approach uses one event-handling function that uses customized events that reveal which button was pressed. The event-handling function takes the correct action based on what the event indicates. (This approach can be used for GUIs written in C++.)
  3. The third approach uses a ``mini-module'' --- a class we write ourselves --- that constructs an event-handling function and its own variables for each button constructed. (This approach can be used for GUIs written in Java.)
All three approaches work fine with GUIs written in Python and Tkinter.


8.4.1 Customized event-handling functions

The first solution approach uses a simple and clever idea: we write a function that returns a function as an answer. Specifically, we write a function, constructHandler, that is called each time we want to construct a new, distinct, event-handling function for a newly constructed button.

Let's study functions-that-return-functions with a small example:

def makeNewFunction(num) :
    """constructs an answer function and returns it"""
    def printNumber() :
        """this function remembers and prints the value of  num 
           which was active when this function is returned as the answer
        """
        print num
        
    return printNumber

f1 = makeNewFunction(2)
f2 = makeNewFunction(4)
f3 = makeNewFunction(5)

f1()
f2()
f3()
Function makeNewFunction returns an answer that is a function, namely, printNumber; the answer function remembers the value of the parameter, num, that was active when the function is returned as the answer. This means the assignment,
f1 = makeNewFunction(2)
assigns to variable f1 a function that prints 2. In a similar way, functions that print 4 and 5 are assigned to the names f2 and f3. Then, the functions can be called by using the variable names! (Try it.) This is not crazy, but completely sensible --- if a function can return an answer that is an int that can be later used in an arithmetic expression, then a function can just as well return a function that can be later used in a function call. Each answer function that is returned is ``customized'' to the values of the parameters that were used to construct the answer function.

We use this technique to write a function that returns an event-handling function customized to the button that uses it. See this example, which is the eight-button GUI written with this technique:

FIGURE==========================================================

"""Buttons2  shows how to construct a customized event-handling function for
   each button in a GUI by using a _function that returns a function as its answer_.
"""
from Tkinter import *

HOW_MANY = 8  # how many buttons we place into the GUI

all_counts = HOW_MANY * [0]  # a list of counts for all the buttons

def constructHandler(but, location) :
    """constructs a custom event-handling function for a GUI button.

       parameters:  but - a button object, the button just constructed
              that requires its own event-handling function
          location - the position of the button on the GUI and also the
              position of the button's count in the  all_counts  list
    """
    def handlePress() :
        """is called when the button,  but,  is pressed: increases the count for
           but  and updates the text on  but's  face
        """
        global all_counts
        all_counts[location] = all_counts[location]+1  # increment the count
        but.configure(text=all_counts[location])  # reset the pressed button's text

    return handlePress  # return the  handlePress function,  customized by
                        # the current values of parameters  but  and  location


myfont = ("Arial", 16, "bold")
window = Tk()
window.title("counters")

width = HOW_MANY * 100
window.geometry(str(width) + "x120")

frame = Frame(window)
frame.grid()

for i in range(HOW_MANY) :

    new_button = Button(frame, font = myfont, text = 0,
                    fg = "blue", bg = "white", width = 4, height = 2)
    new_button.grid(row = 1, column = i, padx = 10, pady = 10 )

    new_handler_function = constructHandler(new_button,i)
    # connect the new handler function to the new button:
    new_button.configure(command = new_handler_function)

window.mainloop()

ENDFIGURE======================================================
Notice how constructHandler uses the arguments assigned to its parameters, but and location, to customize handlePress and return it as its answer. The returned answer is used here:
for i in range(HOW_MANY) :
    new_button = Button(frame, font = myfont, text = 0, ...)
     . . .
    new_handler_function = constructHandler(new_button,i)
    # connect the new handler function to the new button:
    new_button.configure(command = new_handler_function)
Now, when button number i is pressed, the customized function returned by constructHandler(new_button,i) is called and updates the count for the i-th button and the text on the face of the i-th button.


8.4.2 Customized events

It is possible in Python to make Tkinter remember the name of the button that was pressed. This information can be used by an event-handling function to update information about the button that was pressed.

The key technical trick is to attach an event-handling function to a new button like this:

new_button = Button(frame, font = myfont, text = 0, ...)
new_button.bind("<Button-1>", handlePress)
The second command uses the bind method to bind explicitly the new button to a function, handlePress, that will be called when ``Button 1'' (the left mouse button) is pressed on the button's icon on the display. When the mouse is clicked, Tkinker will call handlePress with an argument that is the name of the button pressed..

Here is how we use this trick to rebuild the GUI with its eight buttons:

FIGURE================================================

"""Buttons3  shows how to use an event-handling function that receives an
   _event argument_ that knows the name of the button that was pressed
"""
from Tkinter import *

HOW_MANY = 8  # how many buttons we place into the GUI

all_counts = HOW_MANY * [0]  # a list of counts for all the buttons
all_buttons = []  # a list of all the buttons placed into the GUI

def handlePress(event) :
    """This function handles presses of _all_ the buttons on the GUI.
       It must determine which button was pressed, and then it updates the
       count and text for the button that was pressed

       parameter: event - an object that holds the name of the button that was pressed.
         This argument is constructed by Tkinter when a button is pressed.
    """
    global all_counts, all_buttons
    button_pressed = event.widget  # get the name of the button pressed
    # find the button in  all_buttons  that was pressed:
    location = 0
    for but in all_buttons :   
        if  but == button_pressed :
            break
        else :
            location = location + 1
    # assert:  the button at position  location  in the GUI was pressed
    # (NOTE: the above loop can be replaced by:   location = buttons.index(event.widget)
    all_counts[location] = all_counts[location]+1
    button_pressed.configure(text = all_counts[location])
        

myfont = ("Arial", 16, "bold")

window = Tk()
window.title("counters")

width = HOW_MANY * 100
window.geometry(str(width) + "x120")

frame = Frame(window)
frame.grid()

for i in range(HOW_MANY) :

    new_button = Button(frame, font = myfont, text = 0,
                    fg = "blue", bg = "white", width = 4, height = 2)
    new_button.grid(row = 1, column = i, padx = 10, pady = 10 )
    all_buttons = all_buttons + [new_button]

    # connect the  handle  function as the event handler for left-mouse clicks:
    new_button.bind("<Button-1>", handlePress)

window.mainloop()

ENDFIGURE========================================================
Because the event argument is customized to hold the name of the button pressed, the handlePress function can search its list of all_buttons to learn which button was pressed and which count value should be increased.


8.4.3 Customized handler classes

The third solution to the problem of customized event handling for each button is to write a ``mini-module'' that holds one counter variable and one event-handling function for each button constructed. We ``import'' the mini-module each time we construct a new button.

More precisely stated, we write our own class and use it to construct a new controller object (counter variable plus event-handling function) that connects to each new button we construct.

Here is what the 8-button GUI looks like. It uses the class we write ourselves:

FIGURE===========================================================

"""The view module shows the GUI for the counters."""

from Tkinter import *
import CounterButton   # this module holds the class we wrote

myfont = ("Arial", 16, "bold")
HOW_MANY = 8

window = Tk()
window.title("Lots of counters")

width = HOW_MANY * 100
window.geometry(str(width) + "x120")

frame = Frame(window)
frame.grid()

# Construct the buttons and controllers and connect them:
for i in range(HOW_MANY) :

    button = Button(frame, font = myfont, text = "0",
                    fg = "blue", bg = "white", width = 4, height = 2)
    button.grid(row = 1, column = i, padx = 10, pady = 10 )

    # use  class Control,  the class we wrote within  module CounterButton,
    #   to construct new a controller object:
    controller = CounterButton.Control(button)

    # connect the controller object's  handle  function as the event handler:
    button.configure(command = controller.handle)

window.mainloop()

==================================================================
Within another module, CounterButton.py, there is a class named class Control. This class, a ``mini-module,'' holds its own counter variable and a function named handle. The command:
controller = CounterButton.Control(button)
``imports'' the class and builds a new object (namespace) with its own variable and function. The address of the namespace is assigned to variable controller.

The next command,

button.configure(command = controller.handle)
uses the handle function for the new object as the event-handling function for the new button. In this way, each button is attached to a distinct counter variable and event-handling function.

Here is module CounterButton; you will see it holds class Control. You must look closely to see where the counter variable is hiding; you will find it within the start-up commands in function __init__:

FIGURE=========================================================

"""The CounterButton module contains the class that builds a customized
   controller for the each button of the GUI.
"""
from Tkinter import *

total_presses = 0   # remembers how many times _all_ buttons were pressed


class Control(object) :
    """Control constructs a customized controller that remembers the
       number of times the corresponding button is pressed.
       The controller holds the event-handling function.
       It also remembers these attributes:
          mybutton - the button that this controller is connected to
          mycount - a nonnegative int, how many times the button is pressed
    """

    def __init__(self, b) :
        """init  constructs the controller

           parameters:  self - the new controller object we are constructing
                        b - the button controlled by this controller
        """
        # create these two variables in the controller's namespace:
        self.mybutton = b
        self.mycount = 0

    def handle(self) :
        """This is the event handler for the button."""
        global total_presses
        # update  mycount  in this controller's namespace:
        self.mycount = self.mycount + 1
        # reconfigure the text on the button controlled by this controller:
        self.mybutton.configure(text = str(self.mycount))
        # update the total count of button presses and print it:
        total_presses = total_presses + 1
        print total_presses

======================================================================
Here is what happens when we construct a new Control object, with the command,
controller = CounterButton.Control(button)
  1. A namespace is constructed for the new object in heap storage, at some address, say, addr1.
  2. The function,
    Control.__init__(self = addr1, b = button)
    
    is called. That is, the __init__ function inside class Control is called.

    The __init__ function contains the start-up commands for building the new object. Its first argument gives the address of its own new namespace, and the second argument gives the address of the button object it will connect to.

  3. The commands in __init__ are executed:
    self.mybutton = b
    self.mycount = 0
    
    The first command saves the value of parameter, b as variable mybutton in the object's new namespace. (Read this as saying, ``namespace self gets the variable mybutton with value b.'') The second command creates a new variable, mycount, in the namespace and sets it at 0.

    The style of the commands is meant to match the style we use to reference variables and functions within a module.

Here is a diagram of the program and its storage after the buttons and their matching controllers are constructed:


Each button object has a namespace, and the command attribute (variable) in each namespace is connected to the namespace and function of the corresponding control object and event handler.

Say that the second button on the GUI is pressed; its namespace lives at addr5. The operating system contacts the Python interpreter, which contacts Tkinter, which locates the namespace at addr5.

Here is what happens next:

  1. The value of addr5.command is fetched; it is addr6.handle. This calls the function,
    Control.handle(self = addr6)
    
    because addr6 is the namespace of the Control object.
  2. The commands of handle are executed one by one:
    global total_presses
    self.mycount = self.mycount + 1
    self.mybutton.configure(text = str(self.mycount))
    total_presses = total_presses + 1
    print total_presses
    
    First, self.mycount = self.mycount + 1 makes the mycount variable in the addr6 namespace increase by one. Next, self.mybutton computes to the value addr6.mybutton, which computes to addr5. This calls the function, configure(self = addr5, text = "1"), which reconfigures the text so that the button displays 1. Next, the global variable, total_presses, which is shared by all the controller objects, is incremented and printed.
Please build, test, and modify this example so that you understand how each button gets its own counter variable and event-handling function.


8.4.4 Controllers can contact one another

Here is a small modification to the above exercise. What if we change the behavior so that, when one button is pressed, all the other buttons ``go blank''? That is, if we start like this


and we press the rightmost button, we see this:

If we next press the leftmost button three times in succession, we see this:

and when we next press the rightmost button, we see this:

The underlying controllers are correctly maintaining the courts for the buttons, but only the count for the most recently pressed button is displayed. To implement this behavior, the controller that reacts to a button press asks all the other buttons' controllers to erase the text from the faces of the buttons they control.

We make this behavior come to life by inserting an erase function in class Control:

def erase(self) :
    """erases the text on the button controlled by this controller"""
    self.mybutton.configure(text = "")
Next, we make a master list of all the controllers, so that when a button is pressed, we systematically ask all controllers to erase the text from the buttons:
for controller in master_controller_list :
    controller.erase()
Here are the pieces, fitted together, in the modified controller module:
FIGURE=============================================================

"""CounterButton module contains the class that builds a customized
   controller for the each button of the GUI.
"""
from Tkinter import *

total_presses = 0   # remembers how many times _all_ buttons were pressed
master_controller_list = [] # master list of all controllers we build

class Control(object) :
    """Control constructs a customized controller that remembers the
       number of times the corresponding button is pressed.
       The controller holds the event-handling function.
       It also remembers these attributes:
          mybutton - the button that this controller is connected to
          mycount - a nonnegative int, how many times the button is pressed
    """

    def __init__(self, b) :
        """init  constructs the controller

           parameters:  self - the new controller object we are constructing
                        b - the button controlled by this controller
        """
        global master_controller_list
        # create these two variables in the controller's namespace:
        self.mybutton = b
        self.mycount = 0
        master_controller_list.append(self)  # add new controller to master list

    def handle(self) :
        """This is the event handler for the button."""
        global total_presses, master_controller_list

        # tell all the controllers to erase each button's text"
        for controller in master_controller_list :
            controller.erase()

        # update  mycount  and display it on my button:
        self.mycount = self.mycount + 1
        self.mybutton.configure(text = str(self.mycount))

        # update the total count of button presses and print it:
        total_presses = total_presses + 1
        print total_presses

    def erase(self) :
        """erases the text on the button controlled by this controller"""
        self.mybutton.configure(text = "")

==================================================================
Each time a new controller object is constructed, its __init__ function appends the new object's address to the master_controller_list. In the handle function, each controller in master_controller_list is called to erase the text from its button.

This technique, where controllers call one another, is standard in complex GUI building.


8.4.5 Model-View-Controller architecture

Say that the previous exercise is in fact a display of an eight-squared game board, where each button displays the ``playing piece'' (here, merely an integer) that rests on the corresponding square of the board.

If the game board, pieces, and the rules for using the board are complex, it is best to isolate the board, its squares, its playing pieces, and its laws in a separate model module. Then, when a button is pressed, signifying a ``move'' of a playing piece, the corresponding controller's event-handling function asks the model module to move the piece on the corresponding square of the board. In this way, we have an important division of labors in the program:

  1. model module: holds the program's data structure (game board) and its maintenance functions (the rules for using the board and its pieces)
  2. view module: holds the GUI and in particular, gives a presentation of the model (here, the buttons that represent the squares of the board).
  3. controller module: holds the controllers that are activated when a button is pressed, triggering computation in the model module (here, a move of a piece on the game board).
This organization into three modules is the Model-View-Controller architecture that is standard for single-user programs like spreadsheets, text editors, games, web browsers, and mail readers.

Here is the previous example rebuilt with an explicit model module, which remembers the contents of the 8-celled game board. Its maintenance functions are called when a move is made or an object wishes to know the contents of the board:

================================================================

"""GameBoard  models a simple game board that keeps numbers on
   each of its squares.
"""

SIZE = 8
board = 8 * [0]   # the game board

def getContentsAt(squarenum) :
   """getContentsAt returns the number resting on the board at
       position  squarenum.

       parameter: squarenum, an int
         precondition:  0 <= squarenum < SIZE
       returns: the value at  board[squarenum].  If squarenum is
        out of bounds, returns -1.
   """
   if squarenum < 0 or squarenum >= SIZE :
       answer = -1
   else :
       answer = board[squarenum]
   return answer

def updateAt(squarenum, howmuch) :
   """updateAt alters the board at position  squarenum  by  howmuch.

      parameters: squarenum, howmuch, are ints
         precondition:  0 <= squarenum < SIZE
         postcondition:  board[squarenum]_new = board[squarenum]_old + howmuch
   """
   if squarenum >= 0 and squarenum < SIZE :
        board[squarenum] = board[squarenum] + howmuch

def toString() :
   """toString returns a string representation of the board 
      for printing for trace and debugging purposes
   """
   answer = ""
   for square in board :
       answer = answer + " " + str(square)
   return answer

====================================================================

The revised controller objects no longer remember the counts for their respective buttons. This job has been entrusted to the model module. Each controller merely triggers computation in the model module and tells its button to reset its text:

===============================================================

"""Controllers  contains the class that builds a customized
   controller for the each button of the GUI.  Each controller
   asks the GameBoard module for the correct number to display.
"""
from Tkinter import *
import GameBoard

controller_list = [] # master list of all controllers we build

class Control(object) :
    """Control constructs a customized controller that remembers the
       number of times the corresponding button is pressed.
       The controller holds the event-handling function.
       It remembers this attribute:
          mybutton - the button that this controller is connected to
    """

    def __init__(self, b, index) :
        """init  constructs the controller

           parameters:  self - the new controller object we are constructing
                        b - the button controlled by this controller
                        index - the gameboard square represented by button  b
        """
        global controller_list
        # create these two variables in the controller's namespace:
        self.mybutton = b
        self.myindex = index
        controller_list.append(self)  # add new controller to master list

    def handle(self) :
        """This is the event handler for the button."""
        global total_presses, cotroller_list
        # tell all the controllers to erase all the buttons' text"
        for controller in controller_list :
            controller.erase()
        # Ask the GameBoard to update:
        GameBoard.updateAt(self.myindex, howmuch = 1)
        # Ask the GameBoard for the new contents of the square:
        new_contents = GameBoard.getContentsAt(self.myindex)
        self.mybutton.configure(text = str(new_contents))

    def erase(self) :
        """erases the text on the button controlled by this controller"""
        self.mybutton.configure(text = "")

===================================================================

Finally, the view module remains essentially unchanged:

==================================================================

"""The view module shows the GUI for the simple gameboard."""
from Tkinter import *
import Controllers
import GameBoard

myfont = ("Arial", 16, "bold")
SIZE = GameBoard.SIZE

window = Tk()
window.title("One-row gameboard")
width = SIZE * 100
window.geometry(str(width) + "x120")
frame = Frame(window)
frame.grid()

for i in range(SIZE) :

    button = Button(frame, font = myfont, text = "0",
                    fg = "blue", bg = "white", width = 4, height = 2)
    button.grid(row = 1, column = i, padx = 10, pady = 10 )
    controller = Controllers.Control(button, i)
    button.configure(command = controller.handle)

window.mainloop()

================================================================


8.5 The general format for writing a class

A class is a ``mini-module,'' and its start-up commands must be contained in a specially named function, __init__. When a class is used to ``import'' (build a namespace for) an object, the commands in the __init__ function define the variables that will be saved in the namespace. The init function is also called a constructor method.

The class also contains additional functions, which are called by other modules and objects to ``enter'' the class and use its variables. Such functions are called methods.

The syntax format looks like this:

============================

class NAME ( BASE_CLASS ) :  
    def __init__(self, ... ) :
        """constructs the object (namespace) in heap storage.  The
           address of the new object is assigned to the parameter,  self.
           The object's variables are saved in the namespace named by  self.
       """
        . . .
       self.mydata = ...  # make a variable,  mydata,  in the object's namespace
        . . .
       # the function automatically returns the value of  self  as its answer.


    def f(self, ... ) :
       """method  f  can lookup and alter the variables in the namespace
          named by  self.
       """
         . . . self.mydata . . .

====================================

For now, the BASE_CLASS is object, but we will see in a later example why this keyword might change.

When the class is used to construct an object, we write this:

x = NAME( ... )
The occurrence of NAME on the right-hand side of the assignment is a disguised call of the __init__ function. Indeed, the Python interpreter reformats the above assignment into a ``module'' call in dot notation:
x = NAME.__init__( self = getNewAddressInHeapForTheNewObject, ... )
The __init__ function returns the address, getNewAddressInHeapForTheNewObject and it is assigned to x.

A method within the object is called like this:

x.f(...)
Again, the Python intepreter reformats this call, to look like this:
NAME.f(self = x, ...)
The address held by variable x is assigned to parameter self in handle, correctly connecting the object to its function.

Here is a side-by-side comparison of how we write a class and how we write a module. The class is placed in a module by itself, where it can use global variables of its own; this is the usual format.

===============================================================

In Module1.py:                        In Module2.py:

g = ...  # global var to be            g = ...
         # shared by all objects

class C(object) :
  def __init__(self,p,...)             # these are global vars, too:
    self.x = p  # define vars in       x = ... # no easy way to obtain  p
    self.y = ...# object's n.s.        y = ...

  def f(self,...) :                    def f(...) :
    global g                             global x,y,g
    ... self.x ... self.y ... g ...      ... x ... y ... g ...

===================================================================
When we import a module, we can do it at most once, but we can ``import'' (construct) multiple objects from the same class. Then we can use the functions in each. The comparison looks like this:
==================================================================

In Main1.py:                                        In Main2.py:

import Module1                                        import Module2
                                                        # there is only
d1 = Module1.C(arg1,...)                                # one namespace
  #  translates internally to  
  # d1 = Module1.C.__init__(self=newaddr1,arg1,...)
  #  d1 is assigned  newaddr1

d2 = Module1.C(arg2,...)
  #  translates internally to
  # d2 = Module1.C.__init__(self=newaddr2,arg1,...)
  #  d2 is assigned  newaddr2

... d1.f(...) ...                                     ... Module2.f(...) ...
  # translates internally to                            # there is only one
  # ... Module1.C.f(self=d1,...) ...                    # function, f

... d2.f(...) ...
  # translates internally to
  # ... Module1.C.f(self=d2,...) ...

===================================================================
Of course, if we import a module, M, with the command, from M import *, then we rewrite the above examples to read like this:
==================================================================

from Module1 import *             from Module2 import *

d1 = C(arg1,...)
d2 = C(arg2,...)

... d1.f(...) ...                 ... f(...) ...
... d2.f(...) ...

====================================================================


8.5.1 Case study: Smiley objects

Classes are often used to build simulations and ``virtual reality'' inside the computer, where real-life objects (cars or clouds or genes or ants) are modelled by objects constructed from classes. Often, this has nothing to do with GUIs, but it has everything to do with constructing multiple objects that are ``born,'' ``live,'' ``communicate,'' and ``die'' within computer storage.

We now study an example of this form of class writing: It is a class Smiley, which constructs silly biology life forms that remember their name (a string) and age (an int). The name and the age are the ``data structure'' held within the object.

FIGURE 1==============================================

class Smiley(object) :
    """Smiley objects know their name and age."""  

    def __init__(self, new_name) :
        """__init__ constructs a new Smiley object.

           It holds two variables:  name, a string
                                    age, an integer (initialized to 0).
           parameters:  self - the new object we are constructing
                        new_name - the string that we assign to variable,  name
        """
        self.name = new_name   # make the object hold a  name
	self.age = 0           # make the object hold an  age
	print "Smiley object named", self.name, "constructed." # print trace info

    def talk(self) :
        """talk  makes an object say its name and age.

           parameter: self - the object that will talk
        """
        print "(-:  I am", self.name, "and I am", self.age, "days old."
	self.age = self.age + 1

    def tellSecret(self) :
        """tellSecret makes an object tell a ``secret message''
            parameters: self - the object that will tell the secret
            returns: a string, the secret
        """
        return "I don't know"

ENDFIGURE =================================================
Class Smiley's __init__ function constructs an object that holds two variables: name and age. The two assignments,
self.name = new_name   # make the object hold a  name
self.age = 0           # make the object hold an  age
insert the two variables into the new object and give them values.

As stated earlier, the name, self, refers to the object that we are building. The name must be listed as the first argument to __init__.

Next, function talk is called when we want to make an object print its name and age. The function again uses the name, self, to refer to the object that from which one extracts the name and age for printing. The third function, tellSecret, will be improved later.

The class can be typed as shown above, just like it was a big function, into a program. Or, it can be placed in a file by itself, like a module. Say that we place the class into a module and test it interactively:

$ python -i Smile.py
>>>
The Python interpreter reads and saves the definition of class Smiley. We can try constructing some objects from the class. Here's how:
s1 = Smiley("larry")
This constructs an object and names it s1. It is a bit like ``importing'' a ``module'' and naming it s1. Let's review the key steps taken:
  1. An empty Smiley object (namespace) is built in heap storage, say, at address addr1.
  2. Next, this function call starts:
    Smiley.__init__(addr1, "larry")
    
    That is, the __init__ function within class Smiley starts, where addr1 is the argument assigned to parameter self. When the function executes, the object (namespace) at address addr1 gets the name, "larry":
    self.name = new_name   # make the object hold a  name
    self.age = 0           # make the object hold an  age
    
  3. Once the __init__ function finishes, it automatically returns as its answer, addr1, and this address is assigned to variable s1. This makes s1 the handle to the object --- this is somewhat like naming a module.
After the command, s1 = Smiley("larry"), is completely finished, we have

We use an object's functions just like the object was a little module:

s1.talk()
The function call executes the talk function using the object (namespace) for s1. This prints
(-:  I am larry and I am 0 days old.
Let's understand the precise semantics of the execution:
  1. Because s1 has value addr1, s1.talk() computes to addr1.talk().
  2. Within the object at addr1, the class variable has value Smiley. This generates the function call, Smiley.talk(addr1) --- the talk function in class Smiley is used with the object at addr1.
  3. Function talk in class Smiley is started, and argument addr1 is assigned to parameter self.
  4. This executes the command,
    print "(-:  I am", self.name, "and I am", self.age, "days old."
    
    and we see printed
    (-:  I am larry and I am 0 days old.
    
    because self.name refers to the string held in variable name in the object at addr1.
  5. The assignment,
    self.age = self.age + 1
    
    updates the age variable in the same object.

Now, if we repeat the same command, s1.talk(), this calls the same function using the same object and prints

(-:  I am larry and I am 1 days old.

We can create a second, distinct, new object with this command:

s2 = Smiley("moe")
This command constructs an object, say, at address addr2, in heap storage, calls function Smiley.__init__(addr2, "moe"), and assigns addr2 to s2. When these steps finish, heap storage now holds two objects:
addr1 :
+--------------
| name: "larry"
| age: 2
| class: Smiley
+-------------

addr2 :
+--------------
| name: "moe"
| age: 0
| class: Smiley
+--------------
We can tell the new object to talk:
s2.talk()
and it of course says,
(-:  I am moe and I am 0 days old.
Here is the complete program we just developed:
 FIGURE 2==================================

"""Test defines class Smiley and builds Smiley objects."""

class Smiley(object) :
    def __init__(self, new_name) :
        self.name = new_name   # make the object hold a  name
        self.age = 0           # make the object hold an  age
        print "Smiley object named", self.name, "constructed."

    def talk(self) :
        print "(-:  I am", self.name, "and I am", self.age, "days old."
        self.age = self.age + 1

    def tellSecret(self) :
        return "I don't know"

# Test the class by constructing Smiley objects and asking them to talk:

s1 = Smiley("larry") # Smiley("larry") does these steps:
                     #  (1) it constructs a Smiley object in heap storage,
                     #      say, at address  addr1
		     #  (2) it calls function  Smiley.__init__(addr1, "larry")
		     #  (3) it returns as its answer,  addr1
		     #
		     # At address  addr1  in heap storage is now an object
		     # (actually, a namespace) that holds three variables:
		     #   name: "larry"
		     #   age: 0
		     #   class: Smiley

s1.talk()            # s1.talk()  is shorthand for  Smiley.talk(s1)
s1.talk()

s2 = Smiley("moe")
s2.talk()            # s2.talk()  is shorthand for  Smiley.talk(s2)
s1.talk()

s3 = Smiley("curly")
s1.talk()
s2.talk()
s3.talk()

ENDFIGURE=====================================
Within the script, we use the names s1, s2, and s3 for the three distinct objects --- we never see or learn the values of the objects' addresses in heap storage.

Here is a diagram of computer storage that shows the program, the three objects, and the execution of the program's very last command, s3.talk():


To summarize,

A class is a ``little-module'' builder.
Each time we ``import'' the class, the class's initialization function constructs a new object (namespace) in the heap.
We use the class's functions like a module's functions to maintain the variables in an object.


8.5.2 Placing a class in a file by itself

When we write a class we like, we should save it in a file so that it can be used in other programs, just like we save a module in a file. Say that we save class Smiley in Figure 1 in a file named TextSmiley.py.

Now, here is another program that uses the class:

from TextSmiley import Smiley 

s1 = Smiley("larry")
s1.talk()
s1.talk()

s2 = Smiley("moe")
# and so on....
That is, file TextSmiley is a module that holds as its contents class Smiley. So, we must import the class from its file (module) to use it to construct objects. This is the style used by Dawson, and we will follow this style. But if we wished, we could also say this:
import TextSmiley
s1 = TextSmiley.Smiley("larry")
 . . .
Take your pick.


8.6 Subclasses: Extending a class with new functions

Classes and objects became popular when people started building programs with Graphical User Interfaces (GUIs) --- it takes lots of work to build frames, buttons, text fields, etc., from scratch and get them to connect to a program that does computation! As a result, programmers relied on pre-written frames, buttons, text fields, etc., and used them in their programs. The prewritten frames, buttons, text fields, etc., are coded as classes from which you construct real frames, buttons, and text field objects.

This is what Dawson does in Chapter 10 --- Python has several modules of GUI-classes that you import and use. To do this correctly, you must first learn how to extend an existing, pre-written class with additional functions. This is done by writing another class, a subclass that extends the pre-written class with new functions.


8.6.1 Case study: Extending class Smiley

In Figure 1, class Smiley contains a useless function named tellSecret. Perhaps we want to make a ``smarter'' variant of Smileys that remembers secrets and learns the secrets of other Smileys. We must (1) improve the coding of tellSecret, and (2) write a new function that learns other Smileys' secrets.

To do this, we code an extension --- a subclass --- of Smiley, called SmartSmiley, that holds the revised coding of tellSecret and a new function, learnsSecretOf. Here is the subclass:

FIGURE 3========================================================

from TextSmiley import Smiley

class SmartSmiley(Smiley) :   # this class extends class Smiley
    """SmartSmiley is a Smiley that can also remember and tell secrets."""

    def __init__(self, new_name, my_secret) :
        """__init__ constructs a new SmartSmiley object.

           It holds the variables from a Smiley -- name and age -- along with
             a new variable,  secret.
           parameters:  self - the new object we are constructing
                        new_name - a string that we assign to  name
                        my_secret - a string that we assign to  secret
        """
        Smiley.__init__(self, new_name)  # call Smiley's __init__ function
	self.secret = my_secret          # define a new variable,  secret

    def tellSecret(self) :  # this cancels the version of  tellSecret in Smiley
        """tellSecret makes an object tell a ``secret message''.

           parameters: self - the object that will tell the secret
           returns: a string, the secret
        """
        return self.secret

    def learnsSecretOf(self, anotherSmiley) :
        """learnsSecretOf  lets one object learn another object's secret message

           parameters: self - the object that will learn a new secret
              anotherSmiley - a Smiley object that will surrender its secret
        """
        self.secret = self.secret + ", and " + anotherSmiley.tellSecret()

ENDFIGURE=============================================
The header line shows that class SmartSmiley contains all the coding of class Smiley. Next, there are three functions:

Let's use class SmartSmiley. We might start like this:

s1 = SmartSmiley("larry", "eat your vegetables")
s1.talk()
print s1.tellSecret()
This prints:
Smiley object named larry constructed.
(-:  I am larry and I am 0 days old.
eat your vegetables
Inside heap storage, is the object:
addr1 :
+--------------
| name: "larry"
| age: 1
| secret: "eat your vegetables"
| class: SmartSmiley
+--------------

Let's make a second object:

s2 = SmartSmiley("moe", "get enough sleep")
Heap storage looks like this:
addr1 :
+--------------
| name: "larry"
| age: 1
| secret: "eat your vegetables"
| class: SmartSmiley
+--------------

addr2 :
+--------------
| name: "moe"
| age: 0
| secret: "get enough sleep"
| class: SmartSmiley
+--------------
Let's make the object give its secret to the first object:
s1.learnsSecretOf(s2)
print s1.tellSecret()
The second command prints
eat your vegetables, and get enough sleep
showing that the secret held within s2 was appended to the secret held by s1.

Now, we can still use the orginal class Smiley:

s3 = Smiley("curly")
Heap storage looks like this:
addr1 :
+--------------
| name: "larry"
| age: 1
| secret: "eat your vegetables, and get enough sleep"
| class: SmartSmiley
+--------------

addr2 :
+--------------
| name: "moe"
| age: 0
| secret: "get enough sleep"
| class: SmartSmiley
+--------------

addr3 :
+-------------
| name: "curly"
| age: 0
| class: Smiley
+-------------
Next, when we try
print s3.tellSecret()
the command prints, I don't know, because the coding of tellSecret for Smiley objects has no secret.

We can even do this:

s1.learnsSecretOf(s3)
print s1.tellSecret()
and we see that s1 has learned
eat your vegetables, and get enough sleep, and I don't know
But if we try this:
s3.learnsSecretOf(s1)
the Python interpreter shows us this error message:
Traceback (most recent call last):
  File "Smiley2.py", line 49, in ?
    test()
  File "Smiley2.py", line 44, in test
    s3.learnsSecretOf(s1)
AttributeError: 'Smiley' object has no attribute 'learnsSecretOf'
The message tells us that s3 does not own a function named learnsSecretOf (because s3 is a Smiley object and not a SmartSmiley object).

Here is program that includes the subclass and the tests we tried:

FIGURE 4==================================

"""Test2  contains class SmartSmiley and some tests of SmartSmiley objects."""

from TextSmiley import Smiley

class SmartSmiley(Smiley) :   # this class extends class Smiley

    def __init__(self, new_name, my_secret) :
        Smiley.__init__(self, new_name)  # call Smiley's __init__ function
        self.secret = my_secret          # define a new variable,  secret

    def tellSecret(self) :  # this cancels the version of  tellSecret in Smiley
        return self.secret
 
    def learnsSecretOf(self, anotherSmiley) :
        self.secret = self.secret + ", and " + anotherSmiley.tellSecret()


s1 = SmartSmiley("larry", "eat your vegetables")
s1.talk()
print s1.tellSecret()

s2 = SmartSmiley("moe", "get enough sleep")
s1.learnsSecretOf(s2)
print s1.tellSecret()

s3 = Smiley("curly")
print s3.tellSecret()

s1.learnsSecretOf(s3)
print s1.tellSecret()

s3.learnsSecretOf(s1)

FIGURE 4==================================
Here is a diagram of the program at the point when s1.learnsSecretOf(s3) executes and it calls the tellSecret function with object s3:


8.6.2 Subclassing class Button to build a controller

It is common for GUI widgets to be customized, and the subclass techniques just seen are used to do this. Here is an example, where we rebuild the GUI of eight buttons that display their counts. This time, instead of writing a separate class Control, we embed the counter and the event-handling function into a ``customized button,'' which is Tkinter's class Button extended by a subclass we call class CounterButton.

The GUI is constructed like the:

==================================

"""The view module shows the GUI for the counters."""

from Tkinter import *
import CountController # module that holds  class CounterButton

myfont = ("Arial", 16, "bold")
HOW_MANY = 8

window = Tk()
window.title("Lots of counters")

width = HOW_MANY * 100
window.geometry(str(width) + "x120")

frame = Frame(window)
frame.grid()

for i in range(HOW_MANY) :
    # construct a button customized with its own counter and event handler:
    button = CountController.CounterButton(frame)  
    button.grid(row = 1, column = i, padx = 10, pady = 10 )

window.mainloop()

==============================================
Class CounterButton uses the techniques we learned when we experimented with Smiley objects. Here is the module and the class:
=============================================

"""The CountController module contains the class that builds a customized
   button/controller for the GUI.
"""
from Tkinter import *

total_presses = 0   # how many times _all_ buttons were pressed


class CounterButton(Button) :
    """CounterButton constructs a customized button with its own control
       that remembers the number of times the button is pressed.
       It holds this attribute:
          mycount - a nonnegative int, how many times the button is pressed
    """

    def __init__(self, parent) :
        """init  constructs the controller

           parameters:  self - the new controller object we are constructing
                        parent - the parent widget
        """
        Button.__init__(self, parent, text = "0",
                        font = ("Arial", 14, "bold"), fg = "blue", bg = "white",
                        width = 4, height = 2)
        self.mycount = 0
        self.configure(command = self.handle)  # see immediately below

    def handle(self) :
        """The event handler for the button."""
        global total_presses
        self.mycount = self.mycount + 1
        self.configure(text = str(self.mycount))
        total_presses = total_presses + 1
        print total_presses

========================================
CounterButton is a subclass of Button. (See again the class's header line: class CounterButton(Button).) Its __init__ method calls the __init__ method of Button to build the usual, generic button. The command,
self.configure(command = self.handle)
connects the button to the handle method contained within this very object, and
self.mycount = count
adds a variable to the button's namespace to remember the count of button presses for this button.

The handle method is the event handling function. When the button is pressed, the method looks into its own namespace to find self.mycount, which it increments and displays.


8.6.3 Building GUIs in Python

Now you are ready to study Dawson, Chapter 10, and learn the style of programming Dawson uses to build subclasses of the classes in module Tkinter.

A typical example is Dawson's ``click counter,'' which is a Frame that holds a single button whose label states how many times the button is pressed:


Here is Dawson's coding of the frame and its button and the code that updates the button's label. Notice that Dawson defines a ``customized frame'' for his GUI. (There is no strong reason to do this, but try to understand what he is doing.)
FIGURE==============================

# Click Counter
# Demonstrates binding an event with an event handler
# Michael Dawson - 6/6/03

from Tkinter import *

class Application(Frame):
    """ GUI application which counts button clicks. """ 
    def __init__(self, master):
        """ Initialize the frame. """
        Frame.__init__(self, master)
        self.grid()
        self.bttn_clicks = 0    # the number of button clicks
        self.create_widget()

    def create_widget(self):
        """ Create button which displays number of clicks. """
        self.bttn = Button(self)
        self.bttn["text"]= "Total Clicks: 0"
        self.bttn["command"] = self.update_count
        self.bttn.grid()

    def update_count(self):
        """ Increase click count and display new total. """
        self.bttn_clicks += 1
        self.bttn["text"] = "Total Clicks: " + str(self.bttn_clicks)
          
# main
root = Tk()
root.title("Click Counter")
root.geometry("200x50")

app = Application(root)

root.mainloop()

ENDFIGURE==============================
Dawson's customized frame --- a subclass of Frame --- holds the button's event handling function, update_count. Dawson also simplifies his __init__ method by calling a helper method, create_widget, to do the details of building a button.

Finally, note the command,

self.bttn["command"] = self.update_count
which connects the button object to its event handling function, update_count. This is exactly the same as saying,
self.bttn.configure(command = self.update_count)
The former works because object-namespaces in Python as saved as dictionaries in the heap.

A collection of related classes, like Tkinter, that help you build a complex program, like a GUI, is called a framework. When you use a drag-and-drop tool, like Visual Basic, to build programs with GUIs, you are using a framework.


8.7 Animations

Note: These notes were written for the First Edition of Dawson's text, but the concepts apply to the recodings in the Second Edition, too.

In Chapters 11 and 12, Dawson demonstrates how to use the pygame and livewires modules to build animations. Within the modules are classes that can display images and moving objects within a frame. The key classes are class Screen (builds an animation screen) and class Sprite (buids moving objects).


8.7.1 Sprites are moving objects

A sprite is an object that moves within a screen. A sprite object has an appearance, a position, and a velocity.

A position is an x,y integer pair. A screen's pixels are numbered just like the cells in a grid. For example, position (0,0) is the screen's upper left corner and (150, 300) is the pixel that is located 150 pixels to the right and 300 pixels down from the upper left corner.

A velocity is an x,y integer pair that can be added to a position to make an object ``move.'' For example, a velocity of (2, -1) would make an object move rightward 2 pixels and upwards one pixel after one clock tick. For example, if an object is located at position (150, 300), then a velocity of (2, -1) would make the object move to (152, 299).

When an animation starts, the animation controller starts a clock. Each ``clock tick'' is an event, and at each event, the animation controller moves each sprite according to the position and velocity saved within the sprite's namespace. Then the controller calls each sprite's event handling function, named moved, to see if the sprite has other activities to do.

Sprites lie at the heart of computer animations. Consider Dawson's ``bouncing pizza'' animation from Chapter 11, which constructs a screen and places one pizza sprite within it:


Each clock tick, the pizza moves and its moved function is called. The latter checks if the pizza has reached an edge of the screen; if so, the sprite's velocity is reversed, causing it to ``bounce'':
BEGINFIGURE============================================

# Bouncing Pizza
# Demonstrates dealing with moving sprites and screen boundries
# Michael Dawson 5/11/03

from livewires import games

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480

class Pizza(games.Sprite):
    """ A bouncing pizza. """
    def __init__(self, screen, x, y, image, dx, dy):
        """ Initialize pizza object. """
        self.init_sprite(screen = screen, x = x, y = y,
                         image = image, dx = dx, dy = dy)

    def moved(self):
        """ Reverse a velocity component if edge of screen reached. """
        dx, dy = self.get_velocity()
        if self.get_right() > SCREEN_WIDTH or self.get_left() < 0:
            self.set_velocity(-dx, dy)
        if self.get_bottom() > SCREEN_HEIGHT or self.get_top() < 0:
            self.set_velocity(dx, -dy)

# main
my_screen = games.Screen(width = SCREEN_WIDTH, height = SCREEN_HEIGHT)

wall_image = games.load_image("wall.jpg", transparent = False)
my_screen.set_background(wall_image)

pizza_image = games.load_image("pizza.bmp")
Pizza(screen = my_screen, x = SCREEN_WIDTH/2, y = SCREEN_HEIGHT/2,
      image = pizza_image, dx = 1, dy =1)

my_screen.mainloop()

ENDFIGURE================================================

The program imports the animation module, livewires (which imports another module, pygame.) Class Pizza is a subclass of Sprite, and the __init__ function merely constructs the sprite. (Notice the keyword parameters, which tell us that a generic sprite must be constructed with the address of its parent screen, its x,y position, its dx,dy velocity, and an image to display.) The real reason for writing class Pizza is to include the moved event handling function, which reads the sprite's velocity and position and resets the former if the latter has hit the screen's edge.

The main program constructs the screen, loads a background image into it, and constructs the moving pizza. Note how bitmap (bmp) and jpeg (jpg) pictures must be made into ``image'' objects before they are inserted into screen and sprite, respectively.

When you run this animation, you will see that the pizza travels as if it were weightless in space. To program the proper physics of a pizza's bounce, influenced by mass and gravity, we must use calculus, like Newton did, to define the velocity (more precisely, to define the change in velocity --- acceleration). We won't do this here; instead, we move the pizzas to outer space.


8.7.2 User input and animation loops

Sprites can be controlled by the human user, who can use a mouse or the keyboard to do so. Here is a demo animation of a pan sprite in outer space, controlled by a human with the mouse, who is trying to avoid waves of floating, bouncing pizza sprites:

When a pizza hits the pan, both explode (as portrayed by playing an animation loop of eight jpeg images, taken from Dawson's asteroids game in Chapter 12.)

The animation is constructed from two sprites, Pan and Pizza. Here is class Pan:

FIGURE================================================

# Program  Collide  demonstrates how to use the mouse to move a sprite,
#  how to detect collisions of sprites, and how to run an animation loop:
# The user moves a pan with the mouse, trying to avoid collision with a pizza.

import random
from livewires import games, color

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
THE_SCREEN = games.Screen(SCREEN_WIDTH, SCREEN_HEIGHT)


### THE PAN SPRITE:

class Pan(games.Sprite):
    """Class Pan models a pan controlled by the player.
    """

    def __init__ (self, x, y):
        """Initialize pan object
           Parameters: x,y - ints, the initial position of the pan
        """
        self.init_sprite(screen = THE_SCREEN, x = x, y = y,
                         image = games.load_image("pan.bmp"))

    def moved(self):
        """moved is the pan's event handling function, activated after
           each clock tick
        """
        # move the pan to the current mouse coordinates:
        x, y = self.screen.mouse_pos()
        self.move_to(x, y)
        # don't let the pan escape from the screen:
        if self.get_left() < 0:
            self.set_left(0)
        if self.get_right() > SCREEN_WIDTH:
            self.set_right(SCREEN_WIDTH)
        self.checkForCollision()

    def checkForCollision(self):
        """checkForCollision checks if the pan has collided with any other
           sprites.  If it collides with a pizza, then the game is over
        """
        for ob in self.overlapping_objects():
            if isinstance(ob, Pizza) :  # is the object a Pizza ?
                ob.destroy()   # erase the object from heap storage
                makeBigExplosion(self.get_xpos(), self.get_ypos()) # see below
                self.destroy()
                gameOver()  # see below

# continued...

ENDFIGURE========================================================
The moved function shows that the pan is continually moved to the current mouse position on the screen. Like the bouncing pizza in the earlier example, the pan is not allowed to move past the edge of the screen.

The last command in moved calls the pan's checkForCollision function, which uses a sprite's overlapping_objects method to see if any other sprite might have collided with the pan. If the sprite is indeed an instance of the Pizza class (isinstance(ob, Pizza)), then the colliding objects are removed from the animation (via a sprite's destroy method), the animation loop is played (makeBigExplosion), and the animation is stopped (game_over) --- see below.

Here is the pizza sprite:

FIGURE (cont.)========================================================

### THE PIZZA SPRITE:

PIZZA_IMAGE = games.load_image("pizza.bmp")
PIZZA_SPEED = 1   # pixels per clock tick
TIME_FOR_ANOTHER_PIZZA = 150  # in clock cycles

class Pizza(games.Sprite):
    """Constructs flying pizzas that hold an internal  clock  so that the
       pizza replicates when the clock says it is TIME_FOR_ANOTHER_PIZZA.
    """
    def __init__(self, x, y):
        """ Initialize a pizza object. 
            Parameters: x,y - ints, the initial position of the pizza
        """
        self.clock = 0
        # randomly calculate a direction and velocity in the range -2..2:
        startdx = random.choice([2, -2]) * random.random()
        startdy = random.choice([2, -2]) * random.random()
        self.init_sprite(screen = THE_SCREEN, x = x, y = y,
                         dx = startdx, dy = startdy, image = PIZZA_IMAGE)

    def moved(self):
        """moved ``bounces'' the pizza when it hits the screen's edge.
           It also increments the pizza's clock and spawns a new pizza
           when it is TIME_FOR_ANOTHER_PIZZA.
        """
        dx, dy = self.get_velocity()
        if self.get_right() > SCREEN_WIDTH or self.get_left() < 0:
            self.set_velocity(-dx, dy)
        if self.get_bottom() > SCREEN_HEIGHT or self.get_top() < 0:
            self.set_velocity(dx, -dy)
        self.clock = self.clock + 1
        if self.clock == TIME_FOR_ANOTHER_PIZZA :
            Pizza(self.get_xpos(), self.get_ypos())  # spawn a new pizza
            self.clock = 0
 
# continued...

ENDFIGURE===============================================================
Each pizza is constructed with an internal clock that is incremented at each clock-tick event. A pizza's velocity is randomly calculated to be a pair, x,y, where each integer falls in the range of -2 and 2.

A pizza's moved function makes the pizza bounce when the pizza hits the screen's edge. More importantly, when the pizza's internal clock reaches the value, TIME_FOR_ANOTHER_PIZZA, the pizza spawns a copy of itself (Pizza(self.get_xpos(), self.get_ypos())).

The main program constructs the screen and places within it the pan and one pizza. Two helper functions are called when there is a collision of the pan with a pizza and the animation must end: makeBigExplosion builds a list of 9 bitmap images and uses livewires's Animation class to construct a ``film loop'' object that plays the images one after another, and gameOver displays a shutdown message and tells the screen to terminate after 250 final clock ticks.

FIGURE (concl.)=====================================================

### MAIN PROGRAM --- HELPER FUNCTIONS:

def gameOver():
    """ End the game. """
    games.Message(screen = THE_SCREEN,
                  x = SCREEN_WIDTH/2, y = SCREEN_HEIGHT/2,
                  text = "Game Over", size = 90, color = color.red,
                  lifetime = 250, after_death = THE_SCREEN.quit)

def makeBigExplosion(x, y):
    """makeBigExplosion runs twice a loop of nine images, making an explosion.
                   
       Parameters: x, y - ints, the coordinates of the explosion
    """
    # build loop of images,  explosion_i.bmp :
    images = []
    for i in range(1, 10):
        file_name = "explosion" + str(i) + ".bmp"
        images.append(file_name)
    games.Animation(screen = THE_SCREEN, x = x, y = y,
                    images = images, n_repeats = 2,
                    repeat_interval = 4 )


### MAIN PROGRAM:

nebula = games.load_image("nebula.jpg", transparent = False)
THE_SCREEN.set_background(nebula)
# THE_SCREEN.mouse_visible(False)
Pan(x = SCREEN_WIDTH/2, y = 435)
Pizza(x = random.randrange(SCREEN_WIDTH), y = random.randrange(SCREEN_HEIGHT))

THE_SCREEN.mainloop()

ENDFIGURE===========================================================
Dawsons's book shows many more tricks to building animations, but the key ideas are now in place.


8.8 Installing the support software for animations

The programs in Dawson, Chapters 11 and 12, rely on two support modules, pygame and livewires. If you use your own computer, you must install both of these modules; they are found on the disk that comes with your text.

To install them, load the CD and open it to its start_here web page (the one that shows the cover of the text). Click on I agree and Software.

Click on the link to install Pygame 1.5.6 first. Tell the installer program to install pygame at the folder, C:\Python22\Lib\site-packages.

Next, click on the link to Open the livewires archive. Within the new window that appears, you will see a folder named livewires. Copy that folder into C:\Python22\Lib\site-packages. (You don't have to execute the setup program --- indeed, do not --- it does not function correctly.)

If you have difficulties installing either package, speak with the instructor.


8.9 Summary

A Python class is a ``mini-module'' that can be ``imported'' (constructed) multiple times. Each time the class is ``imported,'' a new, permanent namespace is constructed. Each namespace is called an object.

A class looks like a module --- it has code for building a data structure and functions for maintaining the data structure. The code for building the data structure must be inserted inside a special function, a constructor function, named __init__. There are additional functions, such as the event handling function, handle. The class has this form:

============================

class NAME ( BASE_CLASS ) :  
    def __init__(self, ... ) :
        """constructs the object (namespace) in heap storage.  The
           address of the new object is assigned to the parameter,  self.
           Parameter  self  is used to define and save the object's variables
           in its namespace.
       """
        . . .
       self.mydata = ...   # make a variable,  mydata,  in the object
        . . .
       # the function automatically returns the value of  self  as its answer.


    def handle(self, ... ) :
        """event handling function for the object whose address is  self"""
         . . . self.mydata . . .

====================================
When the class is used to construct an object, we write this:
x = NAME( ... )
The occurrence of NAME on the right-hand side of the assignment is a disguised call of the __init__ function. Indeed, the Python interpreter reformats the above assignment into a ``module'' call in dot notation:
x = NAME.__init__( getNewAddressInHeapForTheNewObject, ... )
Notice that an extra argument is supplied for free --- the address in the heap where the new object will be constructed.

Once the object is constructed, the new address is assigned to variable x. A method within the object is called like this:

x.handle(...)
Again, the Python intepreter reformats this call, to look like this:
C.handle(x, ...)
and now it is clear that the address held by variable x is assigned to parameter self in handle, correctly connecting the object to its event handling function.