10.5 Widget Objects
10.5.4 The Main Program
Now we are ready to write our main program. TheButtonandDieviewclasses are imported from their respective modules. Here is the program that uses our new widgets.
# roller.py
# Graphics program to roll a pair of dice. Uses custom widgets # Button and DieView.
from random import randrange
from graphics import GraphWin, Point from button import Button
from dieview import DieView def main():
# create the application window win = GraphWin("Dice Roller") win.setCoords(0, 0, 10, 10) win.setBackground("green2") # Draw the interface widgets
die1 = DieView(win, Point(3,7), 2) die2 = DieView(win, Point(7,7), 2)
rollButton = Button(win, Point(5,4.5), 6, 1, "Roll Dice") rollButton.activate()
quitButton = Button(win, Point(5,1), 2, 1, "Quit") # Event loop
pt = win.getMouse()
while not quitButton.clicked(pt): if rollButton.clicked(pt): value1 = randrange(1,7) die1.setValue(value1) value2 = randrange(1,7) die2.setValue(value2) quitButton.activate() pt = win.getMouse() # close up shop win.close()
Notice that near the top of the program I have built the visual interface by creating the twoDieViews and twoButtons. To demonstrate the activation feature of buttons, the roll button is initially active, but the quit button is left deactivated. The quit button is activated inside the event loop below when the roll button is clicked. This approach forces the user to roll the dice at least once before quitting.
The heart of the program is the event loop. It is just a sentinel loop that gets mouse clicks and processes them until the user successfully clicks the quit button. Theifinside the loop ensures that the rolling of the dice only happens when the roll button is clicked. Clicking a point that is not inside either button causes the loop to iterate, but nothing is actually done.
10.6
Exercises
1. Explain the similarities and differences between instance variables and “regular” function variables. 2. Show the output that would result from the following nonsense program.
class Bozo:
def __init__(self, value):
print "Creating a Bozo from:", value self.value = 2 * value def clown(self, x): print "Clowning:", x print x * self.value return x + self.value def main():
print "Clowning around now." c1 = Bozo(3)
c2 = Bozo(4) print c1.clown(3)
print c2.clown(c1.clown(2)) main()
3. Use theButtonclass discussed in this chapter to build a GUI for one (or more) of your projects from previous chapters.
4. Write a modifiedButtonclass that creates circular buttons.
5. Write a shell game program using several different shapes of buttons. The program should draw at least three shapes on the screen and “pick” one of them at random. The user tries to guess which of the shapes is the special one (by clicking in it). The user wins if he/she picks the right one.
6. Write a set of classes corresponding to the geometric solids: cube, rectangular prism (brick), sphere and cylinder. Each class should have a constructor that allows for the creation of different sized objects (e.g., a cube is specified by the length of its side) and methods that return the surface area and volume of the solid.
7. Extend the previous problem to include a method,inside, that determines whether a particular point lies within the solid. The method should accept three numbers representing the x, y and z coordinates of a point and return true if the point is inside the object and false otherwise. You may assume that the objects are always centered at 000.
8. Here is a simple class that draws a (grim) face in a graphics window.
# face.py
from graphics import * class Face:
def __init__(self, window, center, size): eyeSize = 0.15 * size
eyeOff = size / 3.0 mouthSize = 0.8 * size
mouthOff = size / 2.0
self.head = Circle(center, size) self.head.draw(window)
self.leftEye = Circle(center, eyeSize) self.leftEye.move(-eyeOff, -eyeOff) self.rightEye = Circle(center, eyeSize) self.rightEye.move(eyeOff, -eyeOff) self.leftEye.draw(window) self.rightEye.draw(window) p1 = center.clone() p1.move(-mouthSize/2, mouthOff) p2 = center.clone() p2.move(mouthSize/2, mouthOff) self.mouth = Line(p1,p2) self.mouth.draw(window)
(a) Use this class to write a program that draws three faces in a window.
(b) Add and test amovemethod so that faces can be moved like other graphics objects.
(c) Add and test aflinchmethod that causes a face’s eyes to close. Also add and test anunflinch
to open them back up again.
(d) Write a complete program that draws a single face in a window and then animates it “bouncing around.” Start the face at a random location in the window and use a loop that moves it a small increment in the x and y directions. When the face hits the edge of the window, it should flinch and bounce off. You can do the bounce by simply reversing the sign of the x increment or the y increment depending on whether the face hit a side- or top-/bottom-edge respectively. Write the animation program to run for a certain (fixed) number of steps.
Note: You will need toflushthe graphics window at the bottom of the loop to achieve the effect of animation.
9. Create aTrackerclass that displays a circle in a graphics window to show the current location of an object. Here is a quick specification of the class:
class Tracker:
def __init__(self, window, objToTrack):
# window is a graphWin and objToTrack is an object whose
# position is to be shown in the window. objToTrack must be
# an object that has getX() and getY() methods that report its
# current position.
# Creates a Tracker object and draws a circle in window at the
# current position of objToTrack.
def update():
# Moves the circle in the window to the current position of the
# object being tracked.
Use your new Trackerclass in conjunction with the Projectileclass to write a program that graphically depicts the flight of a cannonball.
10. Add a Targetclass to the cannonball program from the previous problem. A target should be a rectangle placed at a random position in the window. Allow the user to keep firing until they hit the target.
11. Redo the regression problem from Chapter 8 using aRegressionclass. Your new class will keep track of the various quantities that are needed to compute a line of regresion (the running sums of x, y,
x2and xy)The regression class should have the following methods.
init Creates a new regression object to which points can be added.
addPoint Adds a point to the regression object.
predict Accepts a value of x as a parameter, and returns the value of the corresponding y on the line
of best fit.
Note: Your class might also use some internal helper methods to do such things as compute the slope of the regression line.
12. Implement a card class that represents a playing card. Use your class to write a program that “deals” a random hand of cards and displays them in a graphics window. You should be able to find a freely available set of card images to display your cards by searching on the Internet.
Data Collections
As you saw in the last chapter, classes are one mechanism for structuring the data in our programs. Classes alone, however, are not enough to satisfy all of our data-handling needs.
If you think about the kinds of data that most real-world programs manipulate, you will quickly realize that many programs deal with large collections of similar information. A few examples of the collections that you might find in a modern program include
Words in a document. Students in a course. Data from an experiment. Customers of a business.
Graphics objects drawn on the screen. Cards in a deck.
In this chapter, you will learn techniques for writing programs that manipulate collections like these.
11.1
Example Problem: Simple Statistics
Back in Chapter 8, we wrote a simple but useful program to compute the mean (average) of a set of numbers entered by the user. Just to refresh your memory (as if you could forget it), here is the program again:
# average4.py def main():
sum = 0.0 count = 0
xStr = raw_input("Enter a number (<Enter> to quit) >> ") while xStr != "":
x = eval(xStr) sum = sum + x count = count + 1
xStr = raw_input("Enter a number (<Enter> to quit) >> ") print "\nThe average of the numbers is", sum / count
main()
This program allows the user to enter a sequence of numbers, but the program itself does not keep track of what numbers were entered. Instead, it just keeps a summary of the numbers in the form of a running sum. That’s all that’s needed to compute the mean.
Suppose we want to extend this program so that it computes not only the mean, but also the median and
standard deviation of the data. You are probably familiar with the concept of a median. This is the value that
splits the data set into equal-sized parts. For the data 2, 4, 6, 9, 13, the median value is 6, since there are two values greater than 6 and two that are smaller. To calculate the median, we need to store all the numbers and put them in order so that we can identify the middle value.
The standard deviation is a measure of how spread out the data is relative to the mean. If the data is tightly clustered around the mean, then the standard deviation is small. When the data is more spread out, the standard deviation is larger. The standard deviation provides a yardstick for determining how exceptional a value is. For example, some teachers define an “A” as any score that is at least two standard deviations above the mean.
The standard deviation, s, is defined as
s ∑ ¯x x
i 2
n 1
In this formula ¯x is the mean, xirepresents the ith data value and n is the number of data values. The formula looks complicated, but it is not hard to compute. The expression ¯x x
i
2is the square of the “deviation” of an individual item from the mean. The numerator of the fraction is the sum of the deviations (squared) across all the data values.
Let’s take a simple example. If we again use the values: 2, 4, 6, 9, and 13, the mean of this data ( ¯x) is 6.8.
So the numerator of the fraction is computed as 6 8 2 2 6 8 4 2 6 8 6 2 6 8 9 2 6 8 13 2 149 6 Finishing out the calculation gives us
s 149 6
5 1
37 4 6 11
The standard deviation is about 6.1. You can see how the first step of this calculation uses both the mean (which can’t be computed until all of the numbers have been entered) and each individual value as well. Again, this requires some method to remember all of the individual values that were entered.