Reputation: 619
I have created a turtle drawing program that draws any letter on the turtle canvas that the user presses on the keyboard. I have already implemented an undo function to undo the last drawing the user calls (shown below), but now I am looking at how to implement a redo function. Can anybody give me any tips or tricks on the most pythonic way to do this, possibly based on my current undo function? I have googled a lot about this to no avail, so any help regarding this issue is much appreciated.
My undo function:
def Clear():
clear()
speed(0)
tracer(0,0)
def undoHandler():
if len(function) > 0:
undoHandler.handling = True
if not hasattr(undoHandler, "counter"):
undoHandler.counter = 0
undoHandler.counter += 1
Clear()
function.pop()
penup()
try:
goto(o,p)
print("Gone to")
except:
goto(-200, 100)
pendown()
# "function" is a deque I created with the letter functions appended to it
# Items created based on a Points class that also stores all the attributes including the width, height, color, etc. of each letter.
# Items from a queue I created for the letter functions. The following executes each item from the deque.
try:
for i in function:
k = i.getXY()
penup()
goto(k)
pendown()
hk = i.getletterheight()
global letter_height
letter_height = hk
rk = i.getletterwidth()
global letter_width
letter_width = rk
hw = i.getwidth()
width(hw)
op = i.getcolor()
try:
color(op)
except:
for g in colors:
cp = g.getcolor2()
colormode(255)
color(cp)
j = i.getfunction()
j()
except:
pass
update()
EDIT: Just to avoid confusion, what I want the "redo" to do is to clear the canvas, then redraw everything with one function past the undone point each time a button calling "redo" is pressed. For example, if the user draws "HELLO" on the canvas, and the user undoes up until the letter "H", when redo is pressed once, the turtle should redraw "H(new letter user chose)L", if redo is called a second time, the turtle should draw "H(new letter user chose)LL", so on so forth. It should also be able to change the undone letter to a letter the user replaced it with (hence the "redo"). For example, if the user undoes up to, for example, "H" in "HELLO", and the user replaces "E" with "A", then when redo is called, it should draw "HAL".
Upvotes: 0
Views: 264
Reputation: 5304
A simple method to handle undo/redo is to use two stacks.
If you think of it like a web browser, One stack is for going backwards
and the other stack is for going forwards
.
New Action with Overwrite: Each new user action is done, then pushed onto the backwards
stack. Finally, the next redo action is overwritten by having an action popped and discarded from the forwards
stack (if it is not empty).
Undo: When the user wants to go backwards (undo
), an action is popped from the backwards
stack, the action is undone, then the action is pushed to the forwards
stack.
Redo All: When the user wants to go forwards (redo
), an action is popped from the forwards
stack, the action is redone, then the action is pushed to the backwards
stack. In this case, redo
is actually redo all
, so redo
should be repeated until the forwards
stack is empty.
Warning: Ensure that each action is defined such that it is self-contained, otherwise you may run into issues when actions are overwritten.
Upvotes: 2