[pygtk] Undo and Redo

Toon Verstraelen Toon.Verstraelen at UGent.be
Sun Sep 4 17:13:22 WST 2005


> 3. Yet another approach is used by gazpacho (google for link), where for
> every action there is a wrapper with a do and an undo function.

I have tried this approach for a molecular editing tool and it worked 
very well. Later on I've switched to a similar (but more complex) 
mechanism that was even easier to maintain:
- create an Action base class that has a pre_do, do and post_do method, 
no undo.
- When such action object is created, the tree methods are called in order
- in pre_do, the action assigns itself as current_action to the 
application model
- In the do method the application model is only modified through 
primitive action objects with very limited functionality. Both do and 
undo method's of the primitives are very simple. (I have only five of 
such different Primitive types for a molecular editing tool). In the 
initialization of the Primitive class, there are two lines like this:

context.application.current_model.current_action.primitives.append(self)
self.do()

- the post_do method, the current action is pushed on the undo stack and 
the redo stack is cleared

When undo is called:
- The last action is popped from the undo stack
- the undo methods of all primitives are called in reverse order
- the action is pushed on the redo stack
Similar things happen when redo is called

This approach has some strong advantages:
- The fact the the do&undo method pairs are limited, saves you a lot of 
time, because for all (non-primitive) actions you don't have to care 
about the fact that do and undo are each others inverses. This is also 
the main reason why I took this approach.
- If some primitive actions have 'consequenses', they are also tracked. 
In my case: when an atom is removed from a molecule, also the bonds 
connecting the atom to other atoms had to be removed from the model.
- Not all actions have to be undoable: saving files, changing the 
configuration of the program, opening a online help, ... Such actions 
have an empty primitives list when post_do is executed. Just leave these 
actions out of the undo stack.

It's also rather easy to extend the idea in several ways:
- for each class derived from Action you can also write a static method 
that tells when the action is applicable or not.
- Repeat functionality is also easily implemented. Just make sure that 
parameterized actions store their parameters in dedicated object.
- Interactive actions, like rotating a molecule with the mouse, also fit 
in this framework.


Yo see, there is a lot to tell about undo&redo. If you plan to write a 
program that will grow a very large set of features (actions), then you 
really have to consider to make your undo mechanism rock solid. It will 
certainly pay off in terms of implementation time.

good luck,

Toon


More information about the pygtk mailing list