Pergunta

I have a QML file containing this:

Text {
    id: testData
    onTaskClicked:{
        testData.text = task.name
    }
}

The catch is this taskClicked signal. It is emitted by another widget (C++) and needs to be relayed to QML.

This is similar to this SO question, except that the solution posted there doesn't work (why is written below).

The C++ code:

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 is a QListView subclass that emits the taskClicked(HolidayTask* task) signal (from the .h file):

Q_SIGNALS:
    void taskClicked(HolidayTask* task);

color and m_model are registered in QML and are used elsewhere. The object from the signal is already registered in QML. view is my QQuickView.

First I tried the solution presented in the question above:

auto root = view->rootObject();
auto myElement = root->findChild<QObject*>(QLatin1Literal("testData");

connect(m_view, SIGNAL(taskClicked(HolidayTask* task), myElement,
        SLOT(taskClicked(HolidayTask* task);

However, myElement is always null (and I get a runtime warning about a non existent slot).

If I try to set the view (the QListView) pointer as a context property of the QML view, it still doesn't work.

In all cases, I get also:

QML Connections: Cannot assign to non-existent property "onTaskClicked"

What could I possibly be doing wrong here?

EDIT to clarify some details: HolidayTask is a custom QObject subclass, and the signal taskClicked is defined in C++ (in a QListView subclass)

EDIT2: We're getting close, but no cigar:

auto root = quickView->rootObject();
auto myElement = root->findChild<QObject*>(QLatin1Literal("testData"));

connect(m_view, SIGNAL(taskClicked(HolidayTask*)),
        myElement, SIGNAL(taskClicked(HolidayTask* task)));

and

Text {
    id: testData
    objectName: "testData"
    signal taskClicked(HolidayTask task)
    onTaskClicked: {
        testData.text = task.name
        console.log("CLICk!")
    }
}

yields

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')

More details: HolidayTask, my custom QObject subclass, is registered in the code as

qmlRegisterType<HolidayTask>("HolidayPlanner", 1, 0, "HolidayTask");

Minimal QML with the data:

 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: The final, working code is (see answers on why)

 connect(m_view, SIGNAL(taskClicked(HolidayPlanner::HolidayTask*)),
        myElement, SIGNAL(taskClicked(HolidayPlanner::HolidayTask*)));

This worked only through the use of objects with full namespaces. Otherwise the signature will not match in QML.

Foi útil?

Solução

However, myElement is always null (and I get a runtime warning about a non existent slot).

You are trying to find the child based on id, whereas it is based on the objectName property. You would need to set the objectName property to the desired to actually find it.

Also, you do not seem to declare the signal in your QML Text item. I am not sure if it is a custom C++ item or a built-in one. You have not shared enough code unfortunately to understand that bit. Either way, declare your signal as per documentation.

Therefore, try this code out:

Text {
    id: testData
    objectName: "testData"
    // ^^^^^^^^^^^^^^^^^^^
    signal taskClicked (HolidayTask task)
    // ^^^^^^^^^^^^^^^^^^^
    onTaskClicked:{
        testData.text = task.name
    }
}

Once that is done, you are almost ready. You need to have your HolidayTask registered to QML for sure, and you also need to change the connect syntax in your main.cpp as follows:

connect(m_view, SIGNAL(taskClicked(HolidayTask* task), myElement, SIGNAL(taskClicked(HolidayTask* task)));

In short, you need to trigger your QML signal handler this way, and not via SLOT.

Also, note that your connect syntax is broken as it is missing the closing brackets at the end. That needs to be fixed.

I would even consider removing the pointer assignment and use value or reference based passing for this.

Outras dicas

You can connect a signal from C++ to QML by:

 view->rootContext()->setContextProperty("testData",this);
 QObject::connect(this,SIGNAL(taskClicked(HolidayTask* task)),(QObject *)view->rootObject(),SLOT(onTaskClicked(HolidayTask* task)));

When your signal is named taskClicked, The slot in QML should be onTaskClicked.

Also in QML you should name the object testData by:

objectName: "testData"
QML Connections: Cannot assign to non-existent property "onTaskClicked"

The error tells you that your Text item do not have signal taskClicked or property onTaskClicked! You need to declare a slot inside your text item. To do that, you simply declare a function:

Text {
   id: testData
   objectName: "testData" // as Laszlo said

   function onTaskClicked( task ) {
       testData.text = task.name;
   }
}

But that also won't work because you create a SLOT( onTaskClicked(QVariant) ) instead of SLOT(taskClicked(HolidayTask*)). In order to exchange data with QML you need to change your signal to SIGNAL(taskClicked(QVariant)):

Q_SIGNALS:
    void taskClicked(QVariant task);

And emit it with:

emit taskClicked( QVariant::fromValue( task ) );

Remember that in order to be able to use HolidayTask it must be a QObject that is registered with qmlRegisterType.

You can also simply call that qml function.

If you are not able to use QVariant you can declare a signal inside you Text object:

Text {
   id: testData
   objectName: "testData" // as Laszlo said

   signal taskClicked ( HolidayTask task )

   onTaskClicked: {
       testData.text = task.name;
   }
}

And then connect from a C++ SIGNAL to qml SIGNAL:

auto root = view->rootObject(); 
auto myElement = root->findChild<QObject*>(QLatin1Literal("testData");
connect(m_view, SIGNAL(taskClicked(HolidayTask*), myElement,
    SIGNAL(taskClicked(HolidayTask*));
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top