Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

making undo in python

first of all .. sorry if my english was bad. its my 3rd language
im working on a paint software that draw over images and save them again ( for commenting propose )
i use pile and wxpython. but im still having problems with some features ..
what is the ideal way to make the undo option ?
another question .. when the user scale the picture ( by enlarging the drawing frame) the lines doesn't scale . how do i make that happen .

i got rid of all those problems by saving temporary images to the hard disk whenever the user finishes a line and assign that new picture ( the old one with a line on it) to the frame . undo and redo will is done by switching between those images ... so when user scale image the line will scale too . but that is bad since it takes a lot of HDD space ( when you draw 1000 lines ) and its slow because it assigns a new image every time a user draw a line

hope my idea is clear

does anyone have a better solution ?

like image 414
Moayyad Yaghi Avatar asked Nov 29 '22 06:11

Moayyad Yaghi


2 Answers

The canonical strategy is to use the Command pattern. You'll represent the things you can do as Command objects, and each object is placed on a stack. The state of the application is then defined by an initial state plus everything that the stack has. Thus, the "undo" operation is then just popping the top stack item and reapplying the remaining items to the initial state.

In practice, sometimes it's expensive to keep applying those operations to the initial state to generate the current state. For example, this might be true with something like a complex series of image adjustments, like you might find in Photoshop. In cases like that, it's common to keep an alternating state-stack series in memory:

+---------+
| state_0 |
+---------+       +---------+
| next   -------> | stack_0 |
+---------+       +---------+
                  | data    |       +---------+
                  | next   -------> | state_1 |
                  +---------+       +---------+
                                    | next   ----- ...
                                    +---------+

Each stack_i holds commands until it exceeds some pre-set complexity (e.g., the commands exceed computational cost X) or ordinal (e.g. the stack holds X or more commands) limit. At that point, a new intermediate state object state_i+1 is created to encapsulate the state, and a new, empty stack stack_i+1 is created to hold new commands.

In this way, you only have to apply a small sequence of commands to the last snapshotted state to get the current state. This comes at the expense of memorizing entire states, which may not be feasible for very large applications, but you can choose to snapshot only a set of the state to optimize.

like image 174
John Feminella Avatar answered Dec 04 '22 11:12

John Feminella


Also, keep in mind that Python's functions are first-class objects, which can make implementing the Command pattern very smooth:

actions = []

def do_action1(arg1, arg2):
    # .. do the action ..

    # remember we did the action:
    actions.append(do_action1, (arg1, arg2))

def do_action2(arg1, arg2):
    # .. do the action ..
    actions.append(do_action2, (arg1, arg2))

def replay_actions():
    for fn, args in actions:
        fn(*args)
like image 30
Ned Batchelder Avatar answered Dec 04 '22 12:12

Ned Batchelder