【问题标题】:Using undo stack for a command which is running asynchronously and is emitting signals对异步运行并发出信号的命令使用撤消堆栈
【发布时间】:2021-04-05 02:18:46
【问题描述】:

我有一个从QUndoCommand继承的命令:

class ImportEntityCommand : public QUndoCommand
{
    // ...

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

redo 方法启动 QProcess:

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

信号槽连接由命令构造器组成:

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
                     );
}

进程发出的信号是这样处理的:

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

但是我在编译时收到这样的错误:

C:\Qt\Qt5.12.9\5.12.9\msvc2017_64\include\QtCore\qobject.h:250: 错误:C2664: 'QMetaObject::Connection QObject::connectImpl(const QObject *,void **, const QObject *,void **,QtPrivate::QSlotObjectBase *,Qt::ConnectionType,const int *,const QMetaObject *)':无法将参数 3 从 'const ImportEntityCommand *' 转换为 'const QObject *'

错误点在于:

无法将参数 3 从 'const ImportEntityCommand *' 转换为 'const QObject *'

我从QUndoCommand 继承我的ImportEntityCommand我猜它又从QObject 继承。

问题

因此,出于某种原因,Qt 不允许我处理从QUndoCommand 继承的命令中的信号。

  • 这个限制是故意的吗?
  • 我有哪些选项可以解决此限制?标准程序是什么?

【问题讨论】:

    标签: c++ qt signals signals-slots qprocess


    【解决方案1】:

    我从 QUndoCommand 继承了我的 ImportEntityCommand 类,其中 我猜是从 QObject 继承的。

    QUndoCommand 不继承自 QObject 参见:

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

    与 QWidget 相比,它确实继承自 QObject。

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

    如果你想让你的导入器处理信号和槽,你可以从 QObject 继承:

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

    您可以连接到 lambda,例如:

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

    【讨论】:

    • 我的 Importer 类已经继承自 QObject。实际上,我的 ImportEntityCommand 类是继承自 QUndoCommand 而不是 QObject 的问题原因。
    • 您可以连接到 lambda,这意味着 ImportEntityCommand 不必是 QObject,但是您必须处理 ImportEntityCommand 的范围并在需要时断开连接。
    • 对,lambda 方法解决了编译错误。但是,我的应用程序在运行时卡住了,这可能是由于其他一些问题。谢谢=)
    【解决方案2】:

    难道你不能只使用组合将 QObject 派生类实例放置在 ImportEntityCommand 中吗?然后,您可以将信号连接到 QObject 派生类,然后调用 ImportEntityCommand 来完成工作。

    class SignalReceiver : public QObject
    {
        Q_OBJECT
    public:
        SignalReciever(ImportEntityCommand *iec) : QObject()
        , _iec(iec)
        {}
    public slot:
        void handleImportFinished(QString url)
        {
            if (this->_iec) this->_iec->handleImportFinished(url);
        }
    private:
        ImportEntityCommand *_iec{nullptr};
    };
    

    然后在您的 ImportEntityCommand 中,您将创建一个 SignalReceiver 并将其连接到信号:

    class ImportEntityCommand : public QUndoCommand
    {
        // ...same as before, then
    private:
        QScopedPointer<SignalReceiver, QScopedPointerDeleteLater> _signalReceiver;
    };
    // in the ImportEntityCommand constructor
        this->_signalReceiver.reset(new SignalReceiver(this));
        QObject::connect(this->importer,
                         &Importer::importFinished,
                         this->_signalReciver.data(),
                         &SignalReceiver::handleImportFinished);
    

    我们无法绕过将ImportEntityCommand 的原始指针传递给SignalReceiver,因为ImportEntityCommand 必须存在于撤消堆栈中。另一方面,为接收器使用QScopedPointer 可确保在撤消堆栈删除ImportEntityCommand 实例时正确清理它。请记住始终在包装 QObject 衍生品时指定 QScopedPointerDeleteLater - 尤其是捕捉信号的衍生品。

    【讨论】:

    • 最后,我使用了您的方法,能够在我的 QUndoCommand 实例中使用信号/槽。谢谢=)
    猜你喜欢
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    • 2021-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多