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

14

我有一个包含以下内容的QML文件:

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

问题在于 taskClicked 信号。它是由另一个小部件(C++)发出的,需要转发到 QML。

这类似于这个 SO 问题,但是那里发布的解决方案不起作用(原因在下面说明)。

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

m_view是一个QListView的子类,它会发出taskClicked(HolidayTask* task)信号(来自.h文件):

Q_SIGNALS:
    void taskClicked(HolidayTask* task);

colorm_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 总是 null(我会收到一个有关不存在插槽的运行时警告)。

如果我尝试将视图(即 QListView)指针设置为 QML 视图的上下文属性,它仍然无法工作。

在所有情况下,我还会收到:

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

我在这里可能做错了什么?

编辑以澄清一些细节:HolidayTask是一个自定义的QObject子类,信号taskClicked在C ++中定义(在QListView子类中)

编辑2:我们接近了,但还没有成功:

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")
        }
   }

}

编辑3:最终、可行的代码是(参见答案为什么)

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

只有通过使用具有完整命名空间的对象才能使其正常工作,否则在QML中签名将无法匹配。

3个回答

6
然而,myElement 总是 null (并且我收到了关于不存在插槽的运行时警告)。
你试图通过 id 查找子项,但它是基于 objectName 属性的。你需要将 objectName 属性设置为所需的名称才能找到它。
另外,您似乎没有在 QML Text 项中声明信号。我不确定它是自定义 C++ 项还是内置项。不幸的是,您没有共享足够的代码来理解这一点。无论哪种方式,按照文档 声明您的信号
因此,请尝试此代码:
Text {
    id: testData
    objectName: "testData"
    // ^^^^^^^^^^^^^^^^^^^
    signal taskClicked (HolidayTask task)
    // ^^^^^^^^^^^^^^^^^^^
    onTaskClicked:{
        testData.text = task.name
    }
}

一旦完成上述步骤,您就快要准备好了。您肯定需要将您的HolidayTask注册到QML中,并且您还需要更改main.cpp中的connect语法,如下所示:

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

简单来说,你需要以这种方式触发你的QML信号处理程序,而不是通过SLOT。此外,请注意你的连接语法有误,因为它缺少结尾处的括号,需要进行修复。我甚至会考虑删除指针赋值,并使用基于值或引用的传递来代替。

信号来自C++,项目是定制的。我会更新帖子。 - Einar
@Einar: 好的,但我建议不要把它命名为'Text',因为已经有这样一个默认的QML元素了。我建议为您的目的选择更具体的名称。在这种情况下,您也可以在C++中声明信号,但在我看来,QML信号声明仍然简单得多。 :) - László Papp
哦,抱歉,我搞错了 - 我指的是“HolidayTask”,而不是“item”。 “Text”是内置的。对于造成的混淆,我很抱歉! - Einar
@Einar:啊,好的,这简化了事情。我认为在QML中声明信号是最简单的方法,在我看来。 - László Papp
1
我想通了!签名需要完整的命名空间(所有内容都在命名空间下)。我之前只写了“HolidayTask”,没有附加命名空间,显然这对QML不太友好。我会在问题中发布修改后的代码,以便其他人跟踪。 - Einar
显示剩余2条评论

4

您可以通过以下方式将C++中的信号连接到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"

2
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 ,它必须是一个已经通过 qmlRegisterType 注册的 QObject
你也可以 直接调用 QML 函数
如果您无法使用 QVariant ,您可以在 Text 对象内部声明一个信号:
Text {
   id: testData
   objectName: "testData" // as Laszlo said

   signal taskClicked ( HolidayTask task )

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

然后从C++ SIGNAL连接到qml SIGNAL:

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

正如我上面所写的那样,这会触发不存在的信号运行时错误。 - Einar
@Einar,它应该是SIGNAL(taskClicked(HolidayTask*)),不要有“task”参数名。 - Arpegius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接