Отправка сигнала элементу QML из С++ (Qt5)
У меня есть файл QML, содержащий это:
Text {
id: testData
onTaskClicked:{
testData.text = task.name
}
}
Захват - это сигнал, запрограммированный для задачи. Он выдается другим виджетами (С++) и должен быть передан в QML.
Это похоже на этот вопрос SO, за исключением того, что решение, размещенное там, не работает (почему написано ниже).
Код С++:
ctxt->setContextProperty(QLatin1Literal("holiday"), m_model);
ctxt->setContextProperty(QLatin1Literal("bgcolor"), color);
view->setResizeMode(QQuickView::SizeRootObjectToView);
auto mainPath = QStandardPaths::locate(QStandardPaths::DataLocation,
QLatin1Literal("taskview.qml"));
view->setSource(QUrl::fromLocalFile(mainPath));
ctxt->setContextProperty(QLatin1Literal("viewer"), m_view);
m_view
- это подкласс QListView
, который испускает сигнал taskClicked(HolidayTask* task)
(из файла .h):
Q_SIGNALS:
void taskClicked(HolidayTask* task);
color
и m_model
зарегистрированы в QML и используются в другом месте. Объект из сигнала уже зарегистрирован в QML. view
- мой QQuickView
.
Сначала я попробовал решение, представленное в вопросе выше:
auto root = view->rootObject();
auto myElement = root->findChild<QObject*>(QLatin1Literal("testData");
connect(m_view, SIGNAL(taskClicked(HolidayTask* task), myElement,
SLOT(taskClicked(HolidayTask* task);
Тем не менее, myElement
всегда является нулевым (и я получаю предупреждение о времени выполнения для несуществующего слота).
Если я попытаюсь установить указатель вида (QListView) как свойство контекста представления QML, он все равно не работает.
Во всех случаях я получаю также:
QML Connections: Cannot assign to non-existent property "onTaskClicked"
Что я могу сделать здесь неправильно?
EDIT, чтобы уточнить некоторые детали: HolidayTask
- это пользовательский подкласс QObject, а сигнал taskClicked
определен в С++ (в подклассе QListView
)
EDIT2: мы приближаемся, но нет сигары:
auto root = quickView->rootObject();
auto myElement = root->findChild<QObject*>(QLatin1Literal("testData"));
connect(m_view, SIGNAL(taskClicked(HolidayTask*)),
myElement, SIGNAL(taskClicked(HolidayTask* task)));
и
Text {
id: testData
objectName: "testData"
signal taskClicked(HolidayTask task)
onTaskClicked: {
testData.text = task.name
console.log("CLICk!")
}
}
дает
QObject::connect: No such signal QQuickText_QML_0::taskClicked(HolidayTask* task) in /home/lb/Coding/cpp/holiday-planner/src/mainwindow.cpp:178
QObject::connect: (receiver name: 'testData')
Подробнее: HolidayTask, мой пользовательский подкласс QObject, зарегистрирован в коде как
qmlRegisterType<HolidayTask>("HolidayPlanner", 1, 0, "HolidayTask");
Минимальный QML с данными:
import QtQuick 2.0
import QtQml 2.2
import HolidayPlanner 1.0
Rectangle {
id: container
objectName: "container"
color: bgcolor
Text {
id: testData
objectName: "testData"
signal taskClicked(HolidayTask task)
onTaskClicked: {
testData.text = task.name
console.log("CLICK")
}
}
}
EDIT3: окончательный рабочий код (см. ответы на вопрос почему)
connect(m_view, SIGNAL(taskClicked(HolidayPlanner::HolidayTask*)),
myElement, SIGNAL(taskClicked(HolidayPlanner::HolidayTask*)));
Это работало только, используя объекты с полными пространствами имен. В противном случае подпись не будет соответствовать в QML.
Ответы
Ответ 1
Однако myElement
всегда является нулевым (и я получаю предупреждение о времени выполнения несуществующий слот).
Вы пытаетесь найти ребенка на основе id, тогда как оно основано на свойстве objectName. Вам нужно будет установить свойство objectName в желаемое, чтобы его действительно найти.
Кроме того, вы, похоже, не объявляете сигнал в своем элементе QML Text. Я не уверен, что это настраиваемый элемент С++ или встроенный. К сожалению, у вас недостаточно общего кода, чтобы понять этот бит. В любом случае, объявите свой сигнал согласно документации.
Поэтому попробуйте этот код:
Text {
id: testData
objectName: "testData"
// ^^^^^^^^^^^^^^^^^^^
signal taskClicked (HolidayTask task)
// ^^^^^^^^^^^^^^^^^^^
onTaskClicked:{
testData.text = task.name
}
}
Как только это будет сделано, вы почти готовы. Вам обязательно нужно зарегистрировать свой HolidayTask в QML, и вам также необходимо изменить синтаксис подключения в main.cpp следующим образом:
connect(m_view, SIGNAL(taskClicked(HolidayTask* task), myElement, SIGNAL(taskClicked(HolidayTask* task)));
Короче говоря, вам нужно запускать обработчик сигнала QML таким образом, а не через SLOT
.
Кроме того, обратите внимание, что ваш синтаксис соединения сломан, так как в конце отсутствуют закрывающие скобки. Это необходимо исправить.
Я бы даже подумал об удалении назначения указателя и использования значения или ссылки, основанной на этом.
Ответ 2
Вы можете подключить сигнал от С++ к QML:
view->rootContext()->setContextProperty("testData",this);
QObject::connect(this,SIGNAL(taskClicked(HolidayTask* task)),(QObject *)view->rootObject(),SLOT(onTaskClicked(HolidayTask* task)));
Когда ваш сигнал имеет название taskClicked
, слот в QML должен быть onTaskClicked
.
Также в QML вы должны называть объект testData
:
objectName: "testData"
Ответ 3
QML Connections: Cannot assign to non-existent property "onTaskClicked"
Ошибка говорит вам, что ваш элемент Text
не имеет сигнала taskClicked
или свойства onTaskClicked
!
Вам нужно объявить слот внутри вашего текстового элемента. Для этого вы просто объявляете функцию:
Text {
id: testData
objectName: "testData" // as Laszlo said
function onTaskClicked( task ) {
testData.text = task.name;
}
}
Но это также не сработает, потому что вы создаете SLOT( onTaskClicked(QVariant) )
вместо SLOT(taskClicked(HolidayTask*))
. Чтобы обмениваться данными с QML, вам нужно изменить свой сигнал на SIGNAL(taskClicked(QVariant))
:
Q_SIGNALS:
void taskClicked(QVariant task);
И испустите его с помощью:
emit taskClicked( QVariant::fromValue( task ) );
Помните, что для использования HolidayTask
он должен быть QObject
, который зарегистрирован в qmlRegisterType
.
Вы также можете просто вызвать эту функцию qml.
Если вы не можете использовать QVariant, вы можете объявить сигнал внутри вас. Текстовый объект:
Text {
id: testData
objectName: "testData" // as Laszlo said
signal taskClicked ( HolidayTask task )
onTaskClicked: {
testData.text = task.name;
}
}
И затем подключитесь с С++ SIGNAL к qml SIGNAL:
auto root = view->rootObject();
auto myElement = root->findChild<QObject*>(QLatin1Literal("testData");
connect(m_view, SIGNAL(taskClicked(HolidayTask*), myElement,
SIGNAL(taskClicked(HolidayTask*));