Search code examples
c++qtsignalssignals-slotsqprocess

Using undo stack for a command which is running asynchronously and is emitting signals


I have a command inherited from QUndoCommand:

class ImportEntityCommand : public QUndoCommand
{
    // ...

private:
    QString m_importedEntityName;
    Importer *m_importer;
    // ...
}

The redo method kicks off a QProcess:

void ImportEntityCommand::redo()
{
    if (/* Import is already done before */) {
        // ...
    } else {
        // Import finish is handled by a slot
        m_importer->import(m_url);
    }
}

Signal-slot connection is composed in command constructor:

ImportEntityCommand::ImportEntityCommand(EditorSceneItemModel *sceneModel, const QUrl &url) :
  , m_importer(new Importer(/* ... */))
{

    // Importer would start a QProcess which runs asynchronously and emits a signal
    // that's why I have to handle the signal by a slot
    QObject::connect(m_importer
                     , &Importer::importFinished
                     , this
                     , &ImportEntityCommand::handleImportFinish
                     );
}

The signal emitted by the process is handled like this:

void ImportEntityCommand::handleImportFinish(const QString name)
{
    m_importedEntityName = name;
}

But I'm receiving such error while compiling:

C:\Qt\Qt5.12.9\5.12.9\msvc2017_64\include\QtCore\qobject.h:250: error: C2664: 'QMetaObject::Connection QObject::connectImpl(const QObject *,void **,const QObject *,void **,QtPrivate::QSlotObjectBase *,Qt::ConnectionType,const int *,const QMetaObject *)': cannot convert argument 3 from 'const ImportEntityCommand *' to 'const QObject *'

The error point is that:

cannot convert argument 3 from 'const ImportEntityCommand *' to 'const QObject *'

I'm inhering my ImportEntityCommand class from QUndoCommand which in turn inherits from QObject, I guess.

Question

Therefore, for some reason, Qt won't allow me to handle signals inside a command inherited from QUndoCommand.

  • Is this limitation intentional?
  • What are my options to work around this limitation? What is the standard procedure?

Solution

  • I'm inhering my ImportEntityCommand class from QUndoCommand which in turn inherits from QObject, I guess.

    QUndoCommand does not inherit from QObject see:

    https://doc.qt.io/qt-5/qundocommand.html

    Compared to a QWidget for example, that does inherit from QObject.

    https://doc.qt.io/qt-5/qwidget.html

    If you want your importer to handle signals and slots you can just inherit from QObject:

    class importer: public QObject
    {
        Q_OBJECT
    public:
        ...
    private:
        ...
    }
    

    and you can connect to a lambda e.g.:

    QObject::connect(m_importer
                     , &Importer::importFinished
                     [&]() { this->handleImportFinish() }
                     );