Is it possible to batch text changes for undo/redo?

  • I am developing an editor based on QPlainTextEdit. I have implemented find/replace including replace-all. The replace-all operation loops on find, collecting textCursors of matching strings. It queries the user, "ok to replace nnn instances of xxx with yyy?" Upon Ok, it loops over the list of cursors replacing the text selected by each.

    This all works; however each replacement is a single command for undo purposes. I would like to be able to batch all these changes as a single Undo step so the user could Undo the replace-all with a single ^z instead of many. Is this possible? I have searched the Assistant but I didn't see anything relevant.

  • Look into QUndoStack::beginMacro() and ::endMacro(). If you wrap those around your search/replace action, all the individual replace actions will be grouped in a single action on the undo stack.

  • Thank you, that was just what I was looking for!

  • I have looked at the doc for QUndoStack, QTextEdit, and QTextDocument and I cannot find how to get a reference to the undostack for a particular document.

    I am using PyQt4, and when I code simply,


    I get the error, TypeError: QUndoStack.beginMacro(QString): first argument of unbound method must have type 'QUndoStack' This tells me I need to call the beginMacro() method of a particular object, but I do not see, for example, a "getUndoStack" method on a TextDocument that I could use for this.

    p.s. I see in the UndoRedo example code, they create their own UndoStack object. However I am working with a QPlainTextEdit which does this internally, I know the editor's undo stack is working as it properly supports ^z undo already. The issue is getting a reference to that stack.

  • Further to reply to my own question, I see that QWebPage has an undoStack() method that returns a reference to its undo stack. No other class seems to have this.

  • Mod notes:

    • Please do not add "not solved" to the thread title. It's obvious by a missing "solved".
    • Please do not reply to yourself. You can edit your own posts by clicking on the edit link to the right of your comment (just below avatar and user name), in case you have something to add to your previos post.

    Regarding your actual problem:
    If you operate on a single [[Doc:QTextCursor]], you can call "beginEditBlock() ": and "endEditBlock() ": in order to group your edit operations into one single undo/redo step.

  • Apologies for not following the conventions. Rules noted.

    As I described in the original post, I collect a list of textCursors, one for each matching string in the document, mostly in order to count them and tell the user "going to change nnn... OK?" Having the list of cursors, it is easy to go down the list and make the substitution on each.

    If there is no other way -- ?? -- then I can either re-execute all the matches with a single cursor (I hate to waste the cycles)... or I can use a single work cursor and make it take on the selection of each matched cursor in turn. I think it would be something like this:

    for matchingCursor in listOfMatches:

    Edit: Please wrap code in @ tags; mlong

  • The working cursor approach looks reasonable to me.

    BUT: be aware that if you start from the beginning you will change the document and the found and cached cursors are not updated and might point to false positions!

Log in to reply