Отправка сигнала элементу 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*));