0

Qt::QUndoCommand 问题和可能的解决方案。

我们正在开发 Qt 中的 3D 编辑应用程序。

我们需要实现一个“操作堆栈”,允许用户在他的操作上调用 undo-redo。

我们将 QUndoStack 与 QUndoCommand Qt 类一起使用。

该应用程序基于 MVC 模式,因此 View (QGLWidget) 知道如何绘制场景结构。

我们有滑块(QSlider)来平移/旋转/缩放 3D 对象(网格),我们需要实时显示变换的效果。

例如,如果我选择了一个对象并且我正在移动“X 平移滑块”,我希望在拖动滑块时看到该对象沿 X 轴移动。问题是让实时编辑与操作堆栈一起工作。事实上,应该被压入堆栈的“可撤消”操作是整个滑块移动(从按下到释放滑块)。

我们发现了两种方法:

  1. 当我拖动滑块时(在每个 valueChanged 信号处),转换将应用于模型,并且 QGLWidget 在每个滑块滴答后立即更新。在释放滑块时,必须将命令推入堆栈。当一个命令被推送时,QUndoStack 会自动调用 QUndoCommand::redo() 动作。为防止操作执行两次(第一次授予实时效果,第二次在 QUndoStack::push() 调用时),在调用 QUndoStack::push() 之前应用逆变换对象(从总滑块移动中获得),然后我将命令推送到堆栈。

  2. 当调用 QUndoCommand::Id() 得到相同的结果时,QUndoStack 会尝试合并命令。当我拖动滑块时(在每个 valueChanged 信号处)生成一个 QUndoCommand 并立即推送到堆栈上,如果它们具有相同的 Id(),堆栈会将其与顶部的命令合并,然后堆栈调用 redo( ) 对于正在插入的命令,则 QGLWidget 更新并获得实时效果。

对于第二个,每个滑块刻度都会生成一个“命令”类的实例,而对于第一个,实时操作会被还原,只是为了推送一个命令并保持一致的状态。

就“良好的编程”而言,哪种解决方案更好?在性能方面哪种解决方案更好?

4

2 回答 2

3

我想你正在寻找QUndoStack::beginMacro()QUndoStack::endMacro()

可用于合并撤消堆栈中的一系列命令,以便它们作为原子操作完成/撤消。

于 2013-07-04T18:10:08.870 回答
1

克里斯是正确的,您可以使用宏以及您提出的解决方案。但我不认为他对“过于复杂”的批评是公平的:Qt mergeWith AND 宏机制是为您的目的而设计的。

在您发现性能存在问题之前,我不会担心任何解决方案的性能。让它工作,然后测试性能,然后修复任何性能问题。在您尝试之前,您无法知道是否生成过多的命令(然后合并)或您的逆变换是性能问题。

另一种解决方案:在第一次滴答时,推送一个命令并保留对它的引用。在随后的滴答声中,更新命令并执行增量转换?

(还有一个相关的问题,就是一个命令被推了,用户又取消了操作。比如用户拖动滑块,鼠标在滑块外松开怎么办?滑块取消了吗?搜索“事务性撤消命令”。它重新实现了重做,以便在命令堆叠时第一次调用它时什么都不做,并且有一个稍后调用的 commit() 或 rollback() 方法。)

(我从你的帖子中学到了一些东西:当 self.mergeWith(other) 被调用时,Qt 会调用 other 的 redo 方法。这在文档中并不清楚。)

于 2013-08-08T16:19:56.880 回答