#c #qt #signals #signals-slots #qprocess
#c #qt #сигналы #сигналы-слоты #qprocess
Вопрос:
У меня есть команда, унаследованная от 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 amp;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
, amp;Importer::importFinished
, this
, amp;ImportEntityCommand::handleImportFinish
);
}
Сигнал, передаваемый процессом, обрабатывается следующим образом:
void ImportEntityCommand::handleImportFinish(const QString name)
{
m_importedEntityName = name;
}
Но я получаю такую ошибку при компиляции:
C:QtQt5.12.95.12.9msvc2017_64includeQtCoreqobject.h:250 : ошибка: C2664: ‘QMetaObject::Connection QObject::connectImpl(const QObject *,void **,const QObject *,void **,QtPrivate::QSlotObjectBase *,Qt::ConnectionType,const int *,const QMetaObject *)’: не удается преобразовать аргумент 3 из ‘constImportEntityCommand *’ в ‘const QObject *’
Точка ошибки заключается в том, что:
не удается преобразовать аргумент 3 из ‘const ImportEntityCommand *’ в ‘const QObject *’
Я использую свой ImportEntityCommand
класс, от QUndoCommand
которого, в свою очередь, наследуется QObject
, я полагаю.
Вопрос
Поэтому по какой-то причине Qt не позволяет мне обрабатывать сигналы внутри команды, унаследованной от QUndoCommand
.
- Является ли это ограничение преднамеренным?
- Какие у меня есть варианты обойти это ограничение? Какова стандартная процедура?
Ответ №1:
Я использую свой класс ImportEntityCommand из QUndoCommand, который, в свою очередь, наследуется от 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:
...
}
и вы можете подключиться к лямбде, например:
QObject::connect(m_importer
, amp;Importer::importFinished
[amp;]() { this->handleImportFinish() }
);
Комментарии:
1. Мой
Importer
класс уже наследует отQObject
. На самом деле, мойImportEntityCommand
класс является причиной проблемы, которая наследуется отQUndoCommand
, а неQObject
.2. Вы можете подключиться к лямбде, что означает
ImportEntityCommand
, что это не обязательно должен быть QObject, но тогда вам придется обрабатывать область действияImportEntityCommand
и при необходимости отключать соединение.3. Правильно, лямбда-подход разрешил ошибку компиляции. Однако мое приложение застревает во время работы, что, вероятно, связано с какой-то другой проблемой. Спасибо =)
Ответ №2:
Не могли бы вы просто использовать composition для размещения экземпляра производного класса 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,
amp;Importer::importFinished,
this->_signalReciver.data(),
amp;SignalReceiver::handleImportFinished);
Мы не можем обойти передачу необработанного указателя ImportEntityCommand
на SignalReceiver
, поскольку ImportEntityCommand
он должен находиться в стеке отмены. С другой стороны, использование a QScopedPointer
для получателя гарантирует, что он будет правильно очищен, когда стек отмены удалит ImportEntityCommand
экземпляр. Просто не забывайте всегда указывать QScopedPointerDeleteLater
при переносе QObject
производной, особенно той, которая улавливает сигналы.
Комментарии:
1. В конце концов, я использовал ваш подход, чтобы иметь возможность использовать сигналы / слоты с моими
QUndoCommand
экземплярами. Спасибо =)