0

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".

R. Kap
  • 599
  • 1
  • 10
  • 33

1 Answers1

2

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.

Nuclearman
  • 5,029
  • 1
  • 19
  • 35
  • Please look at my edit. I really do want to save a record of "all" the actions and its okay if things get a bit complicated. Using your way, it will redraw everything but what is NOT redone. For example, using your way, if someone draws "HELLO" and undoes to "H", pressing redo will redraw "ELLO" leaving out the "H". – R. Kap Jan 07 '16 at 02:14
  • Seems like you only need two changes to get what you want. 1) Don't clear the `forwards` stack. Though this *may* cause you issues down the road. 2) What you are calling "redo" is seems actually closer to a "redo all" function in practice. This means your "redo" function means repeatedly executing my redo function until the `forwards` stack is empty. ||| That being said, I've got no clue how you can get the last bit to work unless you actually mean "HALLO". Which could be done by overwriting the old actions, so that each new action results in a pop and discard from the `forwards` stack. – Nuclearman Jan 07 '16 at 02:27
  • Just one more question. What kind of stack do you think I should use? Right now I am using a `deque`, but if you know about anything better for this process, please let me know. – R. Kap Jan 07 '16 at 03:01
  • For Python, you can just use a [list as a stack](https://docs.python.org/2/tutorial/datastructures.html#using-lists-as-stacks). Also [this](http://stackoverflow.com/a/4688885/1608226) answer on stacks in Python may be of benefit to you if you want a stack class. – Nuclearman Jan 07 '16 at 03:41