【问题标题】:How can I access the QUndoStack of a QTextDocument?如何访问 QTextDocument 的 QUndoStack?
【发布时间】:2011-02-28 21:57:38
【问题描述】:

如何访问QTextDocumentQUndoStack

(例如,我希望能够将自定义QUndoCommand 对象添加到文档的撤消堆栈)

【问题讨论】:

    标签: c++ qt undo undo-redo qtextdocument


    【解决方案1】:

    我一直在阅读文档,但这似乎不是直接为小部件获取QUndoStack 的方法。

    可能唯一的方法是创建自己的QUndoStack 对象并手动添加更改,然后重新实现redo() / undo() 插槽。我会看一下源代码,您可能可以从那里获得大部分您需要的代码来存储QTextDocument 中的更改。

    【讨论】:

      【解决方案2】:

      没有办法:(

      我使用的方式是根据需要修改 QTextDocument 类,然后重新编译 Gui 模块。

      为此目的,静态链接是一个不错的选择。

      【讨论】:

        【解决方案3】:

        PySide2 解决方案:无需重新实现!


        背景和解释(代码和说明跳过下面):

        正如其他人所说,确实似乎没有办法直接访问 2020 年 5 月的撤消堆栈。例如This 2017 answer 用户 mrjj 在 Qt 论坛上说堆栈位于 qtextdocument_p.cpp 中,并且无法通过接口访问它

        相反,每个人都建议实现您自己的可撤消命令,这是小菜一碟,但我无法找到如此有意义的实现。此外,内置功能在 QTextDocument docs 中有详细记录,至少对我来说,重新实现看起来不太简单:

        可以使用 setUndoRedoEnabled() 函数控制对文档执行的操作的撤消/重做。撤消/重做系统可以由编辑器小部件通过 undo() 和 redo() 插槽控制;该文档还提供了 contentsChanged() 、 undoAvailable() 和 redoAvailable() 信号,这些信号通知连接的编辑器小部件有关撤消/重做系统的状态。以下是 QTextDocument 的撤消/重做操作:

        • 插入或删除字符。同一文本块中的一系列插入或删除操作被视为单个撤消/重做操作。
        • 插入或删除文本块。单个操作中的插入或删除序列(例如,通过选择然后删除文本)被视为单个撤消/重做操作。
        • 文本字符格式更改。
        • 文本块格式更改。
        • 文本块组格式更改。

        正如我们所见,它集成了许多不同类型的复杂事件,除此之外,它还具有命令压缩功能。我个人非常不喜欢重新实现它的想法。

        理想情况下,我们将通过 API 访问堆栈,然后我们就完成了!希望这在某些时候得到支持(如果是这种情况,请在 cmets 中告诉我)。 在这个答案中,我展示了一种以最小的努力集成内置 QTextDocument Undo 堆栈并保留其所有功能的方法。我尝试了很多不同的方法,我最喜欢这个。希望这会有所帮助!


        代码和说明

        此代码举例说明了QPlainTextEdit 的用法,但您可以使用其他小部件重现它。请参阅文档字符串以获取解释:

        from PySide2 import QtWidgets, QtGui, QtCore
        
        
        class TextDocumentUndoWrapperCommand(QtWidgets.QUndoCommand):
            """
            This command is a wrapper that simply uses the text document stack, but
            allows to register the action on a different stack for integration.
            """
        
            def __init__(self, txt_editor, parent=None):
                super().__init__("Text Document Action", parent)
                self.txt_editor = txt_editor
        
            def undo(self):
                self.txt_editor.document().undo()
        
            def redo(self):
                self.txt_editor.document().redo()
        
        
        class TextEditor(QtWidgets.QPlainTextEdit):
        """
        QTextDocument document has a really nice built-in undo stack, but
        unfortunately it cannot be accessed or integrated with other undo stacks.
        This class exemplifies such integration, as follows:
        
        1. Important: we do NOT disable undo/redo functionality. We keep it on!
        2. Every time that QTextDocument adds a Command to its own stack, we add
           a wrapper command to our own main stack
        3. Every time the user sends an undo/redo event, we intercept it and send
           it through our wrapper command. This way we have effectively integrated
           the built-in undo stack into our own main stack.
        """
        
        def __init__(self, parent=None, undo_stack=None):
            """
            """
            super().__init__(parent)
            self.setLineWrapMode(self.WidgetWidth)  # matter of taste
            if undo_stack is not None:
                # if we provide a stack, integrate internal stack with it
                self.installEventFilter(self)
                self.undo_stack = undo_stack
                self.document().undoCommandAdded.connect(self.handle_undo_added)
        
        def handle_undo_added(self, *args, **kwargs):
            """
            The key information is WHEN to create an undo command. Luckily,
            the QTextDocument provides us that information. That way, we can keep
            both undo stacks in perfect sync.
            """
            cmd = TextDocumentUndoWrapperCommand(self)
            self.undo_stack.push(cmd)
        
        def eventFilter(self, obj, evt):
            """
            We didn't deactivate the undo functionality. We simply want to
            re-route it through our stack, which is synched with the built-in
            one.
            """
            if evt.type() == QtCore.QEvent.KeyPress:
                if evt.matches(QtGui.QKeySequence.Undo):
                    self.undo_stack.undo()
                    return True
                if evt.matches(QtGui.QKeySequence.Redo):
                    self.undo_stack.redo()
                    return True
            return super().eventFilter(obj, evt)
        

        TextEditor 可以简单地用作常规小部件。如果我们不向构造函数提供堆栈,则将使用默认的内置隐藏堆栈。如果我们提供一个,包装器机制会将隐藏堆栈集成到提供的堆栈中。


        注意:我没有为“仅 QTextDocument”提供解决方案,因为我无法让 eventFilter 为它工作(我很高兴听到其他人的努力)。在任何情况下,QTextDocument 总是在任何类型的父窗口小部件/窗口内,然后这个逻辑应该同样适用。有很多论坛要求提供此功能,我认为这是发布此答案的最佳地点(否则请告诉我)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-05-09
          • 1970-01-01
          • 2020-04-12
          • 1970-01-01
          • 2011-05-08
          • 1970-01-01
          • 2021-10-18
          相关资源
          最近更新 更多