【问题标题】:Get signal from qml into c++ class in Qt从 qml 获取信号到 Qt 中的 c++ 类
【发布时间】:2026-02-12 07:05:02
【问题描述】:

我试图弄清楚如何从 QML 代码中获取信号并将其连接到位于 C++ 类中的插槽。

我从这个answer 和屏幕上显示的控件获取代码,但我无法获得信号。

以下是相关代码:

test.qml:

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1

Switch{
    id: swt;
    checked:true;
    onCheckedChanged: swt.qmlSignal();
}

menu.cpp:

Menu::Menu(QWidget *parent) :
QWidget(parent),
        ui(new Ui::Menu)
{
    ui->setupUi(this);
    QQuickView *view = new QQuickView();
    QWidget *container = QWidget::createWindowContainer(view, this);

    container->setMaximumSize(50, 20);

    QObject::connect(container, SIGNAL(qmlSignal()),
                     this, SLOT(receiveFromQml()));

    view->setSource(QUrl("qrc:/test.qml")); 
    ui->verticalLayout->addWidget(container);



}
void Menu::receiveFromQml() 
{
    qDebug() << "Called the C++ slot with message:" ;
}

我查看了示例 here,但无法使其正常工作。

这是我得到的错误:

qrc:/test.qml:10: TypeError: 对象 Switch_QMLTYPE_4(0x291ac70) 的属性 'qmlSignal' 不是函数

【问题讨论】:

  • 在 qmlSignal 之后删除 () - 您正在尝试分配属性,而不是调用函数
  • @Hcorg 我删除了它,现在我没有收到错误但仍然没有触发插槽。连接语句正确吗?
  • 这能回答你的问题吗? How to bind C++ property to QML property?

标签: c++ qt qml qt-quick


【解决方案1】:

简单,标记为槽

好像你有一个C++对象,你需要调用一个槽,所以把函数标记为slot,然后它会自动暴露。在 C++ 中不需要奇怪的 connect()。

// Seems Switch is a QML Item
Switch{
 id: swt;
 checked:true;
 onCheckedChanged: myCppObj.slot(); // calls the object's slot 
} 

还有其他方法可以做到这一点,但这似乎涵盖了您的情况。如果没有,请详细说明,我们会完善它。

【讨论】:

    【解决方案2】:

    我希望您的示例已经足够,并且该开关项也是根项。这通常是这样解决的:

    Switch {
        id: swt
    
        signal gmlSignal
    
        checked:true;
        onCheckedChanged: { 
            qmlSignal();
        }
    }
    

    Qt article 的详细信息。您还可以在需要时使用限定符 swt,以便在该文件中的其他上下文中使用 swt.qmlSignal()

    另外,C++ 部分也需要修复:

    Menu::Menu(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Menu)
    {
       ui->setupUi(this);
       QQuickView *view = new QQuickView();
       QWidget *container = QWidget::createWindowContainer(view, this);
    
       container->setMaximumSize(50, 20);
    
       view->setSource(QUrl("qrc:/test.qml")); 
       ui->verticalLayout->addWidget(container);
    
       QObject::connect((QObject*)view->rootObject(), SIGNAL(qmlSignal()),
                         this, SLOT(receiveFromQml()));
    }
    

    我通过查看发出类似信号的自己的代码进行了更改,但实际上并没有尝试,但您需要连接到从 QML viewrootObject 暴露的信号,而不是它的小部件容器。

    【讨论】:

    • 我试过这个并得到一个错误:没有匹配函数调用 'Menu::connect(QQuickItem*, const char*, Menu* const, const char*)' ,你错过了什么吗?
    • 我用我自己的类似于你的代码进行了简单的检查,根对象的访问方式类似于 (QObject*)view->rootObject() 并在我的回答中修复了它。
    • 仍然有一些问题:我现在(在运行时)得到以下错误:QObject::connect: No such signal Switch_QMLTYPE_4_QML_6::qmlSignal() in ../bwStimulator/menu.cpp:44。当我点击开关时,我收到错误:qrc:/test.qml:13: ReferenceError: qmlSignal is not defined
    • 也许找到 Qt 示例与 QML 将信号传递给 C++ 是有意义的。我能想到为什么它对你不起作用:要么 Switch 不是根项目,所以我们处理 C++ 错误的信号,或者项目中有一些混乱,所以附加了错误的 QML 文件。
    • 这是我仅有的一个 qml 文件,我不认为这在项目中是一团糟。我在哪里可以找到一个简单的教程来添加 qt 快速控制到 qt 小部件项目并从 gim 信号中获取?
    【解决方案3】:

    你不能在 qml 中创建额外的信号,而是使用对象的标准属性。

    import QtQuick 2.4
    import QtQuick.Controls 1.3
    import QtQuick.Layouts 1.1
    
    Switch{
        id: swt;
        checked:true;
    }
    

    C++:

    class MyHandler : public QObject
    {
        Q_OBJECT
        public:
            MyHandler( const QObject& object, const QQmlProperty& qmlProperty );
        private slots:
            void handleNotify();
        private:
            const QQmlProperty& m_qmlProperty;
    };
    
    MyHandler::MyHandler( const QObject& object, const QQmlProperty& qmlProperty ) :
        QObject(),
        m_qmlProperty( qmlProperty )
    {
        static const QMetaMethod metaSlot = this->metaObject()->method( this->metaObject()->indexOfSlot( "handleNotify" ) );
        if( metaSlot.isValid() )
        {
            const QMetaMethod metaQmlPropSignal = qmlProperty.property().notifySignal();
            if( metaQmlPropSignal.isValid() )
                QObject::connect( &object, metaQmlPropSignal, this, metaSlot );
        }
    }
    
    MyHandler::handleNotify()
    {
        if( m_qmlProperty.isValid() )
        {
            const int qmlPropertyValue = m_qmlProperty.read().value<bool>();
            ...
        }
    }
    

    使用:

    QQuickView* view = ...;
    QObject* quickItem = view->rootObject();
    const QQmlProperty* qmlProperty = new QQmlProperty( quickItem, "checked" );
    MyHandler* myHandler = new MyHandler( *quickItem, *qmlProperty );
    

    因此,当checked 属性发生变化时(如果对象quickItem 存在),方法MyHandler::handleNotify 将被调用。

    P.S.您还可以将 qml 属性与信号连接。

    class QmlPropertyWrapper : public QObject
    {
        Q_OBJECT
        public:
            QmlPropertyWrapper( const QObject& object, const QQmlProperty& qmlProperty );
        signals:
            void triggered();
    };
    
    QmlPropertyWrapper::QmlPropertyWrapper( const QObject& object, const QQmlProperty& qmlProperty ) :
        QObject()
    {
        static const QMetaMethod metaSignal = QMetaMethod::fromSignal( &QmlPropertyWrapper::triggered );
        if( metaSignal.isValid() )
        {
            const QMetaMethod metaQmlPropSignal = qmlProperty.property().notifySignal();
            if( metaQmlPropSignal.isValid() )
                QObject::connect( &object, metaQmlPropSignal, this, metaSignal );
        }
    }
    

    使用

    QQuickView* view = ...;
    QObject* quickItem = view->rootObject();
    const QQmlProperty* qmlProperty = new QQmlProperty( quickItem, "checked" );
    QmlPropertyWrapper* qmlPropertyWrapper = new QmlPropertyWrapper( *quickItem, *qmlProperty );
    QObject::connect( qmlPropertyWrapper, &QmlPropertyWrapper::triggered, [ qmlProperty ]()
    {
        if( m_qmlProperty->isValid() )
        {
            const int qmlPropertyValue = m_qmlProperty->read().value<bool>();
            ...
        }
    } );
    

    【讨论】: