Undo!
Anytime you draw, you ought be be able to undo what you drew because we all make mistakes. To undo a mistake in a drawing program, you need to remember all the actions you took to draw the picture, discard the last one, and redraw the picture from the beginning without the last action.
To remember the actions you took in a program, we are going to remember all the points in the program along with all their attributes. Our attributes we need to remember are the coordinate, color, width, and whether or not the pen was up when we drew the next point. To help us with this, we'll define a class in Python. A class is a way of discribing a collection of data and the messages we send to that data. Here is the Point class.
class Point: def __init__(self,x,y,color,width,penup): self.x = x self.y = y self.color = color self.width = width self.penup = penup def draw(self,turtle): if self.penup: turtle.penup() else: turtle.pendown() turtle.color(self.color) turtle.width(self.width) turtle.goto(self.x,self.y) def getXY(self): return (self.x, self.y)
The code above is used to define the collection of data that make up a point and all the messages that can be sent to a point. The def __init__ above describes what is called the constructor. The constructor makes it possible to create a Point object by writing something like
p = Point(100,200,"red",2,False)
The line above does not get copied into your program. It is an example of creating a point object that has an X coordinate of 100, a Y coordinate of 200, with a red line leading to it, a width of 2 on the line, and the pen is down when the line is drawn. If you look carefully, the def __init__ above has all the same parameters as this example except for one additional parameter called self. The self parameter is supplied by Python and always points to the current object. By Python providing the self parameter, we can write code that will work for any point object that we create.
The other def statements above define the messages that can be sent to a point object. For instance, we can write
p.draw(t)
Where t is a Turtle to draw the point on the screen. We can also write
x,y = p.getXY()
to get the X and Y coordinates from a point. Neither of these lines of code belong in your program by themselves, but we'll use them when implementing the undo function in our program.
To continue implementing the undo function, we need to remember the points that we have drawn. To begin, we'll create a list of points that remember all the points we will draw. We put this code in our main function somewhere before the call to mainloop at the end of the program.
data["points"] = [Point(0,0,"black",1,False)]
In the timerHandler we can add these lines of code
p = Point(x,y,c,w,data["penup"]) p.draw(t) data["points"].append(p)
to replace these lines of code that currently draws the point.
t.width(w) t.color(c) t.goto(x,y)
Appending a new point to the end of the list of points allows us to keep track of all the points we have drawn so far (and the lines between them).
Be sure to do the same thing in the clickHandler code to append a new point to the data["points"] list.
Finally, to undo our last action, we'll add an Undo button.
def clear():
t.clear()
t.speed(1000)
screen.tracer(10000) def undoHandler(): if len(data["points"]) > 0: x,y = data["points"][0].getXY() data["points"].pop() clear() t.penup()
t.goto(x,y) t.pendown()
for i in range(len(data["points"])): data["points"][i].draw(t)
screen.update() undoButton = tkinter.Button(frame, \ text="Undo",command=undoHandler) undoButton.pack()
If you get stuck, ask for help!