简体   繁体   English

从C ++(Qt5)向QML项目发送信号

[英]Sending a signal to a QML item from C++ (Qt5)

I have a QML file containing this: 我有一个包含以下内容的QML文件:

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

The catch is this taskClicked signal. 抓住的是这个taskClicked信号。 It is emitted by another widget (C++) and needs to be relayed to QML. 它是由另一个小部件(C ++)发出的,需要中继到QML。

This is similar to this SO question , except that the solution posted there doesn't work (why is written below). 这类似于该SO问题 ,除了在那里发布的解决方案不起作用(为什么在下面编写)。

The C++ code: C ++代码:

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): m_view是一个QListView子类,它发出taskClicked(HolidayTask* task)信号(来自.h文件):

Q_SIGNALS:
    void taskClicked(HolidayTask* task);

color and m_model are registered in QML and are used elsewhere. colorm_model在QML中注册,并在其他地方使用。 The object from the signal is already registered in QML. 信号中的对象已在QML中注册。 view is my QQuickView . view是我的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). 但是, myElement始终为null(并且我收到有关不存在的插槽的运行时警告)。

If I try to set the view (the QListView) pointer as a context property of the QML view, it still doesn't work. 如果我尝试将视图(QListView)指针设置为QML视图的上下文属性,它仍然不起作用。

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) 编辑以阐明一些细节: HolidayTask是一个自定义QObject子类,信号taskClicked在C ++中定义(在QListView子类中)

EDIT2: We're getting close, but no cigar: EDIT2:我们越来越近了,但是没有雪茄:

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 更多详细信息:我的自定义QObject子类HolidayTask,在代码中注册为

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

Minimal QML with the data: 带有数据的最小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: The final, working code is (see answers on why) EDIT3:最终的工作代码是(请参阅有关原因的答案)

 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. 否则,签名将在QML中不匹配。

However, myElement is always null (and I get a runtime warning about a non existent slot). 但是, myElement始终为null(并且我收到有关不存在的插槽的运行时警告)。

You are trying to find the child based on id, whereas it is based on the objectName property. 您试图基于id查找子项,而它是基于objectName属性的。 You would need to set the objectName property to the desired to actually find it. 您需要将objectName属性设置为所需的值才能实际找到它。

Also, you do not seem to declare the signal in your QML Text item. 另外,您似乎没有在QML Text项目中声明信号。 I am not sure if it is a custom C++ item or a built-in one. 我不确定这是自定义C ++项还是内置项。 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: 您需要确保将HolidayTask确保已注册到QML,并且还需要如下更改main.cpp中的连接语法:

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 . 简而言之,您需要以这种方式而不是通过SLOT来触发QML信号处理程序。

Also, note that your connect syntax is broken as it is missing the closing brackets at the end. 另外,请注意您的connect语法已损坏,因为它缺少结尾的右括号。 That needs to be fixed. 这需要解决。

I would even consider removing the pointer assignment and use value or reference based passing for this. 我什至会考虑删除指针分配,并为此使用基于值或基于引用的传递。

You can connect a signal from C++ to QML by: 您可以通过以下方式将信号从C ++连接到QML:

 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 . 当您的信号命名为taskClicked ,QML中的插槽应为onTaskClicked

Also in QML you should name the object testData by: 同样在QML中,您应该通过以下方式将对象命名为testData

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 ! 该错误告诉您Text项没有信号taskClickedonTaskClicked属性! 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*)) . 但这也不起作用,因为您创建了SLOT( onTaskClicked(QVariant) )而不是SLOT(taskClicked(HolidayTask*)) In order to exchange data with QML you need to change your signal to SIGNAL(taskClicked(QVariant)) : 为了与QML交换数据,您需要将信号更改为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 . 请记住,为了能够使用HolidayTask它必须是一个QObject是与注册qmlRegisterType

You can also simply call that qml function . 您也可以简单地调用qml函数

If you are not able to use QVariant you can declare a signal inside you Text object: 如果您不能使用QVariant,则可以在Text对象中声明一个信号:

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: 然后从C ++信号连接到qml信号:

auto root = view->rootObject(); 
auto myElement = root->findChild<QObject*>(QLatin1Literal("testData");
connect(m_view, SIGNAL(taskClicked(HolidayTask*), myElement,
    SIGNAL(taskClicked(HolidayTask*));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM