QUndoCommand subclass problem



  • I have to admit defeat and ask for help using the Qt undo/redo system. The problem is that I cannot seem to access the the subclassed QUndoCommand object pushed onto the QUndoStack. I can access the base class but not any of the subclass methods or properties. This may be a C++ misunderstanding on my part; if so hopefully someone can point me in the right direction.

    The problem:

    In order to use the QUndoStack class one has to subclass QUndoCommand and push a subclass object onto the QUndoStack object. Define a subclass MyUndoCommand as

    @
    #include <QUndoCommand>
    class MyUndoCommand : public QUndoCommand
    {
    public:
    explicit MyUndoCommand(int idx=0,QUndoCommand parent = 0);
    void undo(){/
    some undo code /}
    void redo(){/
    some redo code */}
    int getMyInt(){return myint;}
    protected:
    int myint;
    };
    @

    where the constructor is simply

    @
    MyUndoCommand::MyUndoCommand(int idx,QUndoCommand *parent) :
    QUndoCommand(parent)
    {myint=idx;}
    @

    I can create an QUndoStack object as

    @QUndoStack *m_undoStack=new QundoStack(this);@

    and then create my command objects and successfully push them onto m_undoStack as follows

    @MyUndoCommand cmd = new MyUndoCommand(idx / idx defined previously */);
    m_undoStack->push(cmd);@

    The problem occurs when I try to use the method getMyInt. According to the documentation there is a QUndoStack method called command defined as

    @const QUndoCommand *command ( int index ) const@

    What I want to do access the command at index and get the value of myint but I can't seem to get the code to compile. The basic idea is

    @MyUndoCommand *tmpcmd = m_undoStack->command(index);
    int k=tmpcmd->getMyInt();@

    but I cannot seem to get these two lines of code to compile. I have tried static_cast, const_cast, and reinterpret_cast casting of m_undoStack->command(index) without success (I even tried a dynamic_cast). Probably I don't understand const fully but I have tried every combination of the const qualifier I could think of with the above casts but nothing works.

    Or perhaps it's something else preventing me from using getMyInt. I would be grateful for any help in understanding how to achieve my goal.



  • I think you may be misusing the undo framework.

    The objects are not meant to be used by you, that's what the "documentation":http://qt-project.org/doc/qt-5.1/qtwidgets/qundostack.html#push says (hope I am not misinterpreting the Qt-bible). You have to modify your point of view: The QUndoCommand does not need any public functions, aside from the ones you reimplement.

    An example of something that works for me:
    @class cmd : public QUndoCommand
    {
    public:
    cmd(QStringList SomeDataToWorkWith, QUndoCommand *parent = 0);
    void undo();
    void redo();

    bool mergeWith(const QUndoCommand *command);//only if you plan to merge comands
    int id() const;//only if you plan to merge comands
    private:
    QStringList someDataToWorkWith;
    };

    cmd::cmd(QStringList SomeDataToWorkWith, QUndoCommand *parent)
    : QUndoCommand(parent), someDataToWorkWith(SomeDataToWorkWith)
    {
    this->setText("descriptive text");
    }

    void cmd::redo()
    {
    //do stuff and make this object remember what you did
    }

    void cmd::undo()
    {
    //trace back your steps to undo stuff
    }@

    Then you could use the thing like this:
    @QUndoCommand *u = new cmd(someList);
    stack.push(u);@

    You can roll things back by using
    @stack.undo();@

    Think about which objects have to be inserted to the same stack, make yourself free of any desire to access an undocommand yourself, and hopefully you'll have success.


  • Lifetime Qt Champion

    Hi,

    It should something like be:

    @const MyUndoCommand *command = dynamic_cast<const MyUndoCommand *>(stack.command(index));@



  • Two interesting replies:

    Reply to SGast; thanks for the suggestion. I was under the impression that dynamic_cast could only be relied upon when casting from a subclass to a base class. In any event, for the sake of completeness, I have already tried that cast. The cast line of code compiles (MinGW) but the line of code that tries to use the getMyInt() function
    @int k=tmpcmd->getMyInt();@

    produces the following fatal compile error:

    error: passing 'const MyUndoCommand' as 'this' argument of 'int MyUndoCommand::getIndex()' discards qualifiers

    I haven't been able to get past this problem when adding a function to MyUndoCommand. If I eliminate the getMyInt() function and make myint a public variable then I can get the code to compile but, as yet, I haven't tested that it actually works. Nonetheless I'm still interestred in how to add a working function to MyUndoCommand.

    Reply to theEClaw: I got into this conundrum by attempting to use an existing QUndoStack in QWebPage. For my application Qt's WebKit has the problem that so much of the internals are inaccessible. In particular, two annoying inaccessible items are text nodes and the QWebPage undoStack. In particular whenever an iframe node is cut then the undo works okay but a redo clears the undoStack meaning that you lose all the previous undo operations.

    There is no easy way around this problem since it is impractical to completely replace the WekKit undo stack because that stack is automatically used during normal editing operations. This means that I have to create a separate undo stack that accounts for the above (and other) problems.

    In order to have a reliable undo system I also need to coordinate the two undo stacks which means I have to keep track of the current index in each stack. This leave me with two alternatives:

    • Modify the Qt's existing undo system.
    • Create (from scratch) my own undo system

    I prefer the former which is what caused the original post but may be forced to implement the latter.


  • Lifetime Qt Champion

    The problem is that getMyInt() is not constant, change that and you'll be fine.



  • A BIG thanks SGaist. I replaced

    @
    int getMyInt(){return myint;}
    @

    with

    @
    int getMyInt() const {return myint;}
    @

    and it compiles without errors. Clearly I don't fully understand dynamic_cast or const qualifiers and need to do some more research.


  • Lifetime Qt Champion

    You can have a good explanation "here":http://www.cplusplus.com/doc/tutorial/typecasting/

    As for the const qualifier, it implies that the code in the function doesn't modify the object, if not, the compiler will throw an error.



  • One note: please be aware that a dynamic cast (or a qoject_cast, for that matter) can fail and return a 0 pointer. Always check the result of a cast before dereferencing it!



  • SGaist and Andre - thanks for the pointers. There are still some nuances I don't understand but there are C++ forums I can use to ask those questions. Once again thanks.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.