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


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

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

捕获的是这个任务点击信号。它由另一个小部件(C ++)发出,需要中继到QML。

这类似于 这个问题,除了那里发布的解决方案不起作用(为什么写在下面)。

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文件):

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 始终为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")
        }
   }

}

EDIT3:最终的工作代码是(查看原因的答案)

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

这很有效 只要 通过使用具有完整名称空间的对象。否则签名在QML中将不匹配。


1723
2018-04-09 05:48


起源



答案:


然而, myElement 总是为null(我得到一个运行时警告   一个不存在的插槽)。

您正在尝试基于id查找子项,而它基于objectName属性。您需要将objectName属性设置为实际找到它的所需。

此外,您似乎没有在QML文本项中声明信号。我不确定它是自定义C ++项目还是内置项目。遗憾的是,您还没有分享足够的代码来理解这一点。无论哪种方式,宣布你的信号 根据文件

因此,请尝试以下代码:

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

另请注意,您的连接语法已损坏,因为它缺少最后的右括号。这需要修复。

我甚至会考虑删除指针赋值并使用值或基于引用的传递。


6
2018-04-09 05:57



信号来自C ++,项目是自定义的。我会更新帖子。 - Einar
@Einar:好的,但我建议不要打电话 Text 因为有这样一个默认的qml元素。我建议把它称为更符合你目的的东西。在这种情况下,您也可以用C ++声明信号,但是恕我直言,qml信号声明仍然要简单得多。 :) - lpapp
对不起,我错了 - 我的意思是“项目” HolidayTask。 Text 是内置的。对困惑感到抱歉! - Einar
@Einar:啊,好的,这简化了事情。我认为在QML中声明信号是最简单的,恕我直言。 - lpapp
我想到了!签名需要具有完整的命名空间(everythign位于命名空间下)。我正在做没有附加命名空间的“HolidayTask”,显然这对QML来说并不是好兆头。我会在问题中发布修改后的代码以跟踪其他人。 - Einar


答案:


然而, myElement 总是为null(我得到一个运行时警告   一个不存在的插槽)。

您正在尝试基于id查找子项,而它基于objectName属性。您需要将objectName属性设置为实际找到它的所需。

此外,您似乎没有在QML文本项中声明信号。我不确定它是自定义C ++项目还是内置项目。遗憾的是,您还没有分享足够的代码来理解这一点。无论哪种方式,宣布你的信号 根据文件

因此,请尝试以下代码:

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

另请注意,您的连接语法已损坏,因为它缺少最后的右括号。这需要修复。

我甚至会考虑删除指针赋值并使用值或基于引用的传递。


6
2018-04-09 05:57



信号来自C ++,项目是自定义的。我会更新帖子。 - Einar
@Einar:好的,但我建议不要打电话 Text 因为有这样一个默认的qml元素。我建议把它称为更符合你目的的东西。在这种情况下,您也可以用C ++声明信号,但是恕我直言,qml信号声明仍然要简单得多。 :) - lpapp
对不起,我错了 - 我的意思是“项目” HolidayTask。 Text 是内置的。对困惑感到抱歉! - Einar
@Einar:啊,好的,这简化了事情。我认为在QML中声明信号是最简单的,恕我直言。 - lpapp
我想到了!签名需要具有完整的命名空间(everythign位于命名空间下)。我正在做没有附加命名空间的“HolidayTask”,显然这对QML来说并不是好兆头。我会在问题中发布修改后的代码以跟踪其他人。 - Einar


您可以通过以下方式将信号从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"

3
2018-04-09 07:18





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;
   }
}

然后从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*));

1
2018-04-09 11:38



正如我上面所写,这会触发一个不存在的信号运行时错误。 - Einar
@Einar应该是exaclty SIGNAL(taskClicked(HolidayTask*)) 没有“任务”参数名称。 - Arpegius