I am developing a generic undo/redo frame work that can used along
with GUI based application. Overall undo/redo actions are divided into
3 types:
1. Undo/Redo attribute/member value. (Store value and it's holder such
as list or tuple)
2. Undo/Redo by function (Series of actions and their equal and
apposite actions for redoing)
3. Undo/Redo delete/restore instances/node. (Store instance before it
gets deleted some where and get it back while undoing. Serialization
or something else.)
When implementing above in a gui based application, gui state should
be changed while executing undo/redo actions. For example, changing a
slider's value, changes the value of a member, value is stored in
stack before and after changing. When undoing this action, you have to
change the state of gui control also. This is simple if you control is
static but if you are generating controls at run time then this is a
bit complicated.
Here is a detail of such scenario:
1. You have a node with certain properties.(float, int, list, etc)
2. Select Node "A", alter some properties.
3.Select Node "B", alter some properties
4. Select Node "C".
5. Undo(Node "C" deselected and Node "B" is selected.
6. Property editor is showing members of Node "B".
7. Undo (The last property that was changed of Node "B" goes back to
previous state.
8. As mention above storing control's instance along with value in
stack won't do the job the here because, the controls are created just
now as we have selected Node "B". Internally the value of member is
changed but it will not update the control that's being shown in
property editor.
How do I solve this problem?
Are there any other/better ways to implement undo/redo frame work in
gui based applications?
I think you got the basics right. There's on important piece missing: You need to separate your model (data) from the view. The undo/redo actions will just change the data itself. The model then sends an event. If there's a view registered for the model, the view can update itself.
How does this apply to your problem?
Say you have Node A and B, both have a "colour" property. First you change A.colour, then B.colour. Now you hit undo. B.colour will be reverted and the UI for it is updated. You are still looking a the UI of node B. Now if you press undo again A.colour is reverted to its initial value. Since there is no view that watches for "change" events of Node A nothing happens.
Does this make sense?
I am using this system with success in my own application (with dynamically generated UI from the node's properties).
-Matthias
···
Am 15.06.2010, 10:54 Uhr, schrieb King <animator333@gmail.com>:
How do I solve this problem?
Are there any other/better ways to implement undo/redo frame work in
gui based applications?
Thanks for the suggestions. There is one thing that's is slightly
different in my implementation.
Select/DeSelect is also part of the stack. After changing B.colour,
pressing undo will de-select "B" and selects "A".
As soon as "A" is selected, property editor is showing it's property.
Now again if you press "UnDo", A.colour is changed and at the same
time control should be updated because it's visible.
I am thinking to store couple of other things along with member and
it's value. For example it's name("colour" and name of it's node("B"),
later check if the current node in prop-editor is "B" then find the
control which is connected to "colour" and update it's value. This
will work but couple of additional changes are required.
I've dealt with situations like this in a couple different ways in the past. Each has their advantages and disadvantages, and each will be a better fit in different applications depending on the needs. In a nutshell they were:
* Assign each of the views/widgets a name and use the name in the undo stack. Then when you need to undo/redo you look up the view by name and if it still exists then you update it (or tell it to update itself) as needed.
* Make the creation and deletion of the views/widgets part of the undoable commands that are stored in the undo stack. IOW, when a view is destroyed make that be an action that can be undone (by recreating the view and populating it with data) etc.
The suggestion made by Nitro is another good alternative, which I'll include in this list just for completeness.
* Essentially you make the undo stack deal only with the data values in the application and totally ignore the views. When the user changes data values, or when an undo/redo is done, then a simple "data changed" message is sent (perhaps with something like pubsub) and it will be up to the views to listen for that message and update themselves. They don't really need to care if the message comes because of an undo, a redo, or from edit actions by the user.
···
On 6/15/10 1:54 AM, King wrote:
How do I solve this problem?
Are there any other/better ways to implement undo/redo frame work in
gui based applications?
I am doing this in a faily large commercial grade application,
following the well known OO patterns: MVC, command, observer. It is
working and scaling very well.
1. First of all you separate the model from the view.
2. Then you have command objects to modify the model. Never modify the
model directly, always use commands.
3. You can make lists of done/undone commands and move through the
list with undo/redo
4. Use the observer pattern to keep the views updated when you do/undo
Fabio
···
On 16 Giu, 03:43, Robin Dunn <ro...@alldunn.com> wrote:
On 6/15/10 1:54 AM, King wrote:
> How do I solve this problem?
> Are there any other/better ways to implement undo/redo frame work in
> gui based applications?
I've dealt with situations like this in a couple different ways in the
past. Each has their advantages and disadvantages, and each will be a
better fit in different applications depending on the needs. In a
nutshell they were:
* Assign each of the views/widgets a name and use the name in the undo
stack. Then when you need to undo/redo you look up the view by name and
if it still exists then you update it (or tell it to update itself) as
needed.
* Make the creation and deletion of the views/widgets part of the
undoable commands that are stored in the undo stack. IOW, when a view
is destroyed make that be an action that can be undone (by recreating
the view and populating it with data) etc.
The suggestion made by Nitro is another good alternative, which I'll
include in this list just for completeness.
* Essentially you make the undo stack deal only with the data values in
the application and totally ignore the views. When the user changes
data values, or when an undo/redo is done, then a simple "data changed"
message is sent (perhaps with something like pubsub) and it will be up
to the views to listen for that message and update themselves. They
don't really need to care if the message comes because of an undo, a
redo, or from edit actions by the user.
--
Robin Dunn
Software Craftsmanhttp://wxPython.org