LINUX.ORG.RU

Не могу вызвать слот при использовании в параметрах QString&

 , , ,


0

1

Есть сигнал у объекта класса MainWindow:

class MainWindow : public QMainWindow
{
    Q_OBJECT

signals:
    void selectPointsFile(QString &fileName);


Эмитится так:

// Отправка сигнала что выбран новый файл бинарных координат для точек
emit selectPointsFile(fileName);


Есть слот у объекта класса PointsLoader:
class PointsLoader : public QObject
{
    Q_OBJECT

public slots:
    void setPointsFile(QString &fileName);


Реализация слота:

void PointsLoader::setPointsFile(QString &fileName)
{
    qDebug() << "Set new points file";
    mFileName=fileName;
}


Они соеденены в «новом» синтаксисе. Объект MainWindow доступен по указателю, а PointsLoader в явном виде, поэтому у него адрес берется:

    // Главное окно оповещает считыватель точек, что задан новый файл координат
    connect(mMainWindow, &MainWindow::selectPointsFile,
            &mPointsLoader, &PointsLoader::setPointsFile);


Компиляция проходит без ошибок. Но когда выбираешь новый файл и эмитится сигнал selectPointsFile(fileName), выдается ошибка:

QObject::connect: Cannot queue arguments of type 'QString&' 
(Make sure 'QString&' is registered using qRegisterMetaType().)


Если переделать параметр с типа QString& на QString, то ошибка исчезает. Но мне нужно сделать именно по ссылке, так как дальше я буду делать похожий сигнал/слот, который будет передавать большой блок данных, и хотел бы его уметь передавать по ссылке.

Примечание: объект класса PointsLoader находится в отдельном потоке, возможно из-за это проблема? Но как ее можно обойти чтобы не гонять в явном виде данные, а только ссылку на них?

★★★★★

Последнее исправление: Xintrea (всего исправлений: 1)

Примечание: объект класса PointsLoader находится в отдельном потоке, возможно из-за это проблема?

Да.

Но как ее можно обойти чтобы не гонять в явном виде данные, а только ссылку на них?

Умные указатели?

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Умные указатели?

Имеешь в виду, надо сделать умный указатель, у просто передать его в качестве параметра? Но у меня объект имени файла на стеке а не в куче. Это ничего страшного?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Silerus

Попробуйте так
signals:
void selectPointsFile(const QString &fileName);

Хм, а так компиляция проходит без ошибок, при выборе файла ошибок нет, вот только слот не срабатывает. А ведь в отладочном выводе должна появиться строка:

Set new points file

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

UPD: Я не доглядел. Слот не срабатывает и при простом типе QString без всяких модификаций. Никаких ошибок ни при компиляции ни в рантайме нет. Не пойму что еще надо докрутить.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

Кароч я застрял. Даже при простом типе QString все компилится без ошибок, в рантайме нет ошибок, но слот не вызывается.

Я думал, что может важно в какой последовательноси происходит помещение в отдельный тред, и сделал так:

    // Считыватель координат точек переносится в тред
    mPointsLoader.moveToThread(&mPointsLoaderThread);

    // Главное окно оповещает считыватель точек, что задан новый файл координат
    connect(mMainWindow, &MainWindow::selectPointsFile,
            &mPointsLoader, &PointsLoader::setPointsFile);

    // Объект в треде запускается
    mPointsLoaderThread.start();


То есть, коннект делается после того как объект помещен в тред. Но и это не помогает, слот не вызывается.

Xintrea ★★★★★
() автор топика

Если переделать параметр с типа QString& на QString, то ошибка исчезает. Но мне нужно сделать именно по ссылке, так как дальше я буду делать похожий сигнал/слот, который будет передавать большой блок данных, и хотел бы его уметь передавать по ссылке.

Документация говорит, что QString в Qt использует copy-on-write, поэтому копирование QString дешёвое. Так что если не нужно менять строку так, чтобы вызывающая сторона увидела изменения, можно передавать по значению.

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

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Не работает вызов слота даже по значению, блин.

Ошибок нет.

Перед emit есть отладочный вывод - он печатается.

В слоте есть отладочный вывод - он не печатается.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Очисти проект, сделай заново qmake и пересобери. Если связывание не произошло в консоле должен появится варнинг

Silerus ★★★★
()
Ответ на: комментарий от i-rinat

Похоже, нащупал. Похоже, что в треде не работает свой отдельный основной цикл обработки событий. Поэтому и слот не отрабатывает.

И поэтому у меня тред не закрывается, mPointsLoaderThread.wait() в конце программы ждется бесконечно долго.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от i-rinat

Да, дело было в основном цикле. Добавил в периодический код треда вызов qApp()->processEvents(); и все заработало. Оказывается, такой вызов в треде вызывает обработчик цикла событий в самом треде, а не цикла событий приложения.

После того как заработало, я попробовал сделать вызов слота с типом параметра:

const QString &fileName
...как советовал Silerus. И такой вариант тоже заработал.

* * *

После чего я попробовал сделать новое соединение новых сигнала/слота, в котором используется похожий тип параметра:
const QVector<QPointF> &pointsBuffer

Компилится без ошибок, но в момент вызова слота в рантайме появляется опять такая же ошибка как в топике:
QObject::connect: Cannot queue arguments of type 'QVector<QPointF>'
(Make sure 'QVector<QPointF>' is registered using qRegisterMetaType().)


И вот я не могу понять, почему для QString константная ссылка работает, а для QVector<QPointF> нет. Ошибок компиляции ведь нет, коннект сделан в «новом» стиле, неужели действительно надо регистрировать тип через qRegisterMetaType(), или ошибка в чем-то другом?

Xintrea ★★★★★
() автор топика
Последнее исправление: Xintrea (всего исправлений: 2)

QObject::connect: Cannot queue arguments of type 'QString&'
(Make sure 'QString&' is registered using qRegisterMetaType().)

Ну русским по белому же написано, что нужно зарегистрировать тип. Если используешь соединение через Qt::QueuedConnection, то тип должен быть зарегистрирован. Перед использованием сигнала:

qRegisterMetaType<QString&>("QString&");

ox55ff ★★★★★
()
Ответ на: комментарий от Xintrea

Тут такое дело, не все объекты могут быть переданы через систему сигнал/слотов, такие объекты должны быть специально сконструированы http://scrutator.me/post/2011/12/08/Qt-pitfalls.aspx https://doc.qt.io/qt-5/qmetatype.html. Но никто не отменял передачу указателей на объекты в функцию, старый добрый метод

Silerus ★★★★
()
Ответ на: комментарий от ox55ff

Ну русским по белому же написано, что нужно зарегистрировать тип.

Почему же тогда работает const QString& без регистрации?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

потому что он зарегистрован системой по умолчанию, если посмотрите в документацию, то почти всегда в сигналах передающих строки увидите const QString &

Silerus ★★★★
()
Ответ на: комментарий от Silerus

Я попробовал сделать так:

    typedef QVector<QPointF>& QVectorOfQPointFReference;
    qRegisterMetaType<QVectorOfQPointFReference>("QVectorOfQPointFReference");

и так:
    typedef QVector<QPointF>& QVectorOfQPointFReference;
    qRegisterMetaType<QVectorOfQPointFReference>("QVector<QPointF>&");

В обоих случаях компиляция затыкается с ошибкой:

../src/core/Core.cpp: In member function ‘void Core::init(QString, MainWindow*)’:
../src/core/Core.cpp:34:69: error: no matching function for call to ‘qRegisterMetaType(const char [18])’
     qRegisterMetaType<QVectorOfQPointFReference>("QVector<QPointF>&");
                                                                     ^
In file included from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qobject.h:54:0,
                 from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qiodevice.h:45,
                 from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qtextstream.h:43,
                 from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qdebug.h:49,
                 from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/QDebug:1,
                 from ../src/core/Core.cpp:2:
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h:1700:5: note: candidate: template<class T> int qRegisterMetaType(const char*, T*, typename QtPrivate::MetaTypeDefinedHelper<T, (QMetaTypeId2<T>::Defined && (! QMetaTypeId2<T>::IsBuiltIn))>::DefinedType)
 int qRegisterMetaType(const char *typeName
     ^~~~~~~~~~~~~~~~~
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h:1700:5: note:   template argument deduction/substitution failed:
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h: In substitution of ‘template<class T> int qRegisterMetaType(const char*, T*, typename QtPrivate::MetaTypeDefinedHelper<T, (QMetaTypeId2<T>::Defined && (! QMetaTypeId2<T>::IsBuiltIn))>::DefinedType) [with T = QVector<QPointF>&]’:
../src/core/Core.cpp:34:69:   required from here
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h:1666:80: error: ‘IsBuiltIn’ is not a member of ‘QMetaTypeId2<QVector<QPointF>&>’
     , typename QtPrivate::MetaTypeDefinedHelper<T, QMetaTypeId2<T>::Defined && !QMetaTypeId2<T>::IsBuiltIn>::DefinedType defined = QtPrivate::MetaTypeDefinedHelper<T, QMetaTypeId2<T>::Defined && !QMetaTypeId2<T>::IsBuiltIn>::Defined
                                                                                ^~~~~~~~~~~~~~~~
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h:1737:29: note: candidate: template<class T> constexpr int qRegisterMetaType()
 inline Q_DECL_CONSTEXPR int qRegisterMetaType()
                             ^~~~~~~~~~~~~~~~~
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h:1737:29: note:   template argument deduction/substitution failed:
../src/core/Core.cpp:34:69: note:   candidate expects 0 arguments, 1 provided
     qRegisterMetaType<QVectorOfQPointFReference>("QVector<QPointF>&");
                                                                     ^
In file included from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qobject.h:54:0,
                 from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qiodevice.h:45,
                 from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qtextstream.h:43,
                 from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qdebug.h:49,
                 from /opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/QDebug:1,
                 from ../src/core/Core.cpp:2:
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h:1749:43: note: candidate: template<class T> constexpr int qRegisterMetaType(T*)
 QT_DEPRECATED inline Q_DECL_CONSTEXPR int qRegisterMetaType(T *)
                                           ^~~~~~~~~~~~~~~~~
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h:1749:43: note:   template argument deduction/substitution failed:
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h: In substitution of ‘template<class T> constexpr int qRegisterMetaType(T*) [with T = QVector<QPointF>&]’:
../src/core/Core.cpp:34:69:   required from here
/opt/qt_5_9_6/5.9.6/gcc_64/include/QtCore/qmetatype.h:1749:43: error: forming pointer to reference type ‘QVector<QPointF>&’
Makefile:1794: recipe for target 'Core.o' failed
make: *** [Core.o] Error 1


Заголовок QMetaType, естественно, подключен.

Xintrea ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.