[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