简体   繁体   中英

QML Binding integer property - changes in c++ not sent to QML

I am new to QtQuick controls, I would like to create a simple GUI and I have encountered the following problem with binding certain c++ variables to qml widgets properties: In the simplified example below, I have a single combobox for which I would like to assign a list of items and also be able to set which item is selected from within the c++ side of the application.

For the test_list/model pair everything is working exactly as I would expect, but for the test_index/currentIndex it is not. After the test_index is set to 2 and notification is called, nothing happens - I expect that the READ method (getIndex) should be called, but it is not (it is for the test_list though). Changing the index from the GUI works, ie the value is sent from the qml to the c++ side due to the onCurrentIndexChanged, but it does not work the other way around. So after calling the modifyValues(), there are four items in the combobox - 0, 1, 2, 3 - but the currentIndex is still 0, so the "0" item is selected (meanwhile, test_index is equal to 2).

Odd thing is that the getIndex does get called the first time the widget is created. I have read that changing a property in the qml will terminate all the declarative bindings, but I do not see why this should happen here, especially when it works for the 'model' property (or other that I tried, eg currentText). Nevertheless, I have tried to put myCombo.currentIndex = testClassInstance.test_index to the onCurrentIndexChanged as well, but that does not help (and it does not seem right...).

My questions: Is there something special about the currentIndex property? How to achieve what I want without resorting to explicitly defining signal connections in the qml (I thought that this is basically what the Q_PROPERTY macro is for)? I think I could figure out some workarounds to get to my goal, so that is not what I am looking for here - instead, I would prefer if somebody could explain to me why this particular approach is not working the way I expect.

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

Lets break in steps:

1) You create combobox object and bind it's properties (index and model) to class' properties

2) You call modifyValues and a) change list, this triggers model changing b) change index, this triggers... nothing

The problem is on step 2a. ComboBox inside has following code:

onModelChanged: {
    ...
    popup.__selectedIndex = 0
}

This call breaks your binding for index, so when you go to step 2b, binding don't exist anymore.

And here is solution to your problem, you should updage binding after model changed:

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

As a quick reply, you can use Connections ,

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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