QML 绑定整数属性 - c++ 中的更改未发送到 QML

Posted

技术标签:

【中文标题】QML 绑定整数属性 - c++ 中的更改未发送到 QML【英文标题】:QML Binding integer property - changes in c++ not sent to QML 【发布时间】:2016-02-26 01:41:28 【问题描述】:

我是 QtQuick 控件的新手,我想创建一个简单的 GUI,但在将某些 c++ 变量绑定到 qml 小部件属性时遇到了以下问题: 在下面的简化示例中,我有一个组合框,我想为其分配一个项目列表,并且还能够设置从应用程序的 c++ 端选择的项目。

对于 test_list/model 对,一切都完全按照我的预期工作,但对于 test_index/currentIndex 却不是。在 test_index 设置为 2 并调用通知后,什么也没有发生 - 我希望应该调用 READ 方法(getIndex),但它不是(虽然它是用于 test_list)。从 GUI 更改索引是可行的,即由于 onCurrentIndexChanged 将值从 qml 发送到 c++ 端,但反过来就不行。所以在调用 modifyValues() 之后,组合框中有四个项目 - 0, 1, 2, 3 - 但 currentIndex 仍然是 0,所以选择了“0”项目(同时,test_index 等于 2)。

奇怪的是 getIndex 确实在第一次创建小部件时被调用。我已经读过更改 qml 中的属性将终止所有声明性绑定,但我不明白为什么会在这里发生这种情况,尤其是当它适用于“模型”属性(或我尝试过的其他属性,例如 currentText)时。尽管如此,我也尝试将 myCombo.currentIndex = testClassInstance.test_index 放入 onCurrentIndexChanged 中,但这并没有帮助(而且看起来不正确......)。

我的问题:currentIndex 属性有什么特别之处吗?如何在不诉诸于在 qml 中显式定义信号连接的情况下实现我想要的(我认为这基本上是 Q_PROPERTY 宏的用途)?我想我可以找到一些解决方法来实现我的目标,所以这不是我在这里寻找的 - 相反,如果有人可以向我解释为什么这种特殊方法没有按我预期的方式工作,我更愿意。

main.qml

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Dialogs 1.2

ApplicationWindow 
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")


    MainForm 
        objectName: "mainForm"
        anchors.fill: parent
    

MainForm.qml

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

Item 
   width: 640
   height: 480


   Rectangle 
      anchors.centerIn: parent

      ComboBox 
            id: myCombo
            model: testClassInstance.test_list
            currentIndex: testClassInstance.test_index
            visible: true
            onCurrentIndexChanged: 
                testClassInstance.test_index = myCombo.currentIndex
            
       
   

testClass.h

#ifndef TESTCLASS_H
#define TESTCLASS_H

#include <QObject>

class testClass : public QObject

    Q_OBJECT
    Q_PROPERTY(int test_index READ getIndex WRITE setIndex NOTIFY notification)
    Q_PROPERTY(QStringList test_list READ getList WRITE setList NOTIFY notification)

public:
    testClass(QObject *parent) : QObject(parent) 
    ~testClass() 

    void modifyValues()
    
        test_list << "1" << "2" << "3";
        notification();
        test_index = 2;
        notification();
    

    int getIndex()  return test_index;  
    QStringList getList()  return test_list;   

    void setIndex(int val)  test_index = val;  
    void setList(QStringList val)  test_list = val; 

signals:
    void notification();

private:
    int test_index = 0;
    QStringList test_list = QStringList("0");

;

#endif

main.cpp

int main(int argc, char *argv[])

    QApplication app(argc, argv);
    QQmlApplicationEngine engine;

    testClass test(&engine);
    engine.rootContext()->setContextProperty("testClassInstance", &test);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    test.modifyValues();

    return app.exec();

【问题讨论】:

【参考方案1】:

让我们分步进行:

1) 您创建组合框对象并将其属性(索引和模型)绑定到类的属性

2) 你调用 modifyValues 和 a) 更改列表,这会触发模型更改 b)更改索引,这会触发......什么都没有

问题出在第 2a 步。 ComboBox里面有如下代码:

onModelChanged: 
    ...
    popup.__selectedIndex = 0

此调用会破坏您对索引的绑定,因此当您转到第 2b 步时,绑定不再存在。

这是解决您的问题的方法,您应该在模型更改后更新绑定:

onModelChanged: 
     currentIndex = Qt.binding( function()return testClassInstance.testIndex )

【讨论】:

哦……这确实有道理。我不得不说,即使我自己意识到了这一点,我想我也不会想到我必须使用Qt.binding(...) 语法而不是像currentIndex: test_index 这样的语法,这似乎很不直观,我在我遇到的任何教程中都没有看到它......不过,非常感谢您的解释和解决方案。 每当你编写信号处理代码时,它都是javascript,简单的赋值不能作为绑定,所以你必须显式地创建绑定。【参考方案2】:

作为快速回复,您可以使用Connections

   ComboBox 
       id: myCombo
       model: testClassInstance.test_list
       visible: true
       onCurrentIndexChanged: 
           testClassInstance.test_index = myCombo.currentIndex
       
       Connections 
           target: testClassInstance
           onNotification: myCombo.currentIndex = test_index;
       
   

【讨论】:

嗨,谢谢,这个解决方案是有效的(我之前确实试过,只是不知道我必须在那里指定“onNotification:”)。无论如何,我仍然想知道为什么我必须为 currentIndex 执行此操作,但我不需要为“model”、“currentText”或一堆其他属性执行此操作;这让我很困惑。如果有人(或者你有时间的话)可以解释,我会等几天,如果没有人解释,我会将你的答案标记为已接受。 您好,您可以随时查阅Qt文档。他们有据可查。这里已经描述过了,doc.qt.io/qt-5/qml-qtquick-controls-combobox.html#model-prop 我之前读过,但问题是“初始化后更改模型会将 currentIndex 重置为 0。”在我看来,这并不意味着与另一个变量的绑定被破坏了。根据这句话,我希望 currentIndex 将设置为 0,并且绑定到它的变量也将设置为 0。到目前为止,我已经了解到这不是它的工作方式(当需要更多时它实际上是有意义的是时候考虑一​​下了),但我之前没有意识到这一点。对于“经验不足”的人来说,也许值得明确提及“破坏绑定”问题。 是的,你实际上是对的,qml 上的属性绑定在第一次体验时有点棘手,赋值会破坏绑定,但人们期望的是赋值但不会破坏绑定。干杯。

以上是关于QML 绑定整数属性 - c++ 中的更改未发送到 QML的主要内容,如果未能解决你的问题,请参考以下文章

从高度动态的 C++ 数据模型更新 QML:计时器与属性绑定

如何有条件地禁用 QML 绑定到 C++ 后端?

如何从 C++ 中删除属性上的 QML 绑定?

QML ListView:检测到属性“高度”的绑定循环

将对象从 c++ 发送到 qml。空闲内存呢?

在 QML 中更新对 var 属性的绑定