在Python中实现撤销功能

4 投票
2 回答
8864 浏览
提问于 2025-04-15 17:39

首先,如果我的英语不好请原谅,这是我学的第三门语言。
我正在开发一个绘图软件,可以在图片上绘画并保存(用于评论)。
我使用的是pile和wxpython。
不过我在一些功能上还是遇到了一些问题……
我想知道,理想的撤销功能应该怎么做?
还有一个问题……当用户缩放图片(通过放大绘图框)时,线条并没有跟着缩放。我该怎么让它们一起缩放呢?

我通过在用户完成一条线时,将临时图片保存到硬盘,解决了这些问题。然后把这张新图片(就是在旧图片上加了一条线的那张)赋值给绘图框。
撤销和重做就是在这些图片之间切换……所以当用户缩放图片时,线条也会缩放。但这样做不好,因为它会占用很多硬盘空间(当你画了1000条线时),而且每次用户画一条线时都要重新赋值新图片,这样速度也慢。

希望我的想法能表达清楚。

有没有人有更好的解决方案呢?

2 个回答

10

另外,记住Python中的函数是“头等公民”,这意味着你可以很方便地使用它们来实现命令模式:

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)
13

一种常用的方法是使用命令模式。你可以把可以执行的操作看作是命令对象,然后把这些对象放在一个栈里。应用程序的状态就是初始状态加上栈里的所有内容。因此,“撤销”操作其实就是把栈顶的项目弹出,然后把剩下的项目应用到初始状态上。

不过在实际操作中,有时候不断地把这些操作应用到初始状态上以生成当前状态是很耗费资源的。比如说,处理复杂的图像调整时,就像在Photoshop里那样。在这种情况下,通常会在内存中保留一个交替的状态栈系列:

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

每个stack_i会保存命令,直到它超过某个预设的复杂度(比如说,命令的计算成本超过X)或者数量限制(比如说,栈里有X个或更多命令)。到那时,就会创建一个新的中间状态对象state_i+1来封装当前状态,并创建一个新的空栈stack_i+1来保存新的命令。

这样,你只需要对最后一个快照状态应用一小段命令,就能得到当前状态。这种方法的代价是需要记住整个状态,对于非常大的应用程序来说,这可能不可行,但你可以选择只快照一部分状态来进行优化。

撰写回答