13

我有一个用 qml 和 c++ 编写的 GUI。有 2 个组合框(qt control 5.1)。每当第一个组合框的值更改时,第二个组合框必须在运行时更新。

maincontext->setContextProperty("typemodel", QVariant::fromValue(m_typemodel));

maincontext->setContextProperty("unitmodel", QVariant::fromValue(m_unitmodel));

这些是我从 c++ 给 qml 的 2 个模型。

ComboBox {
    id: typebox

    anchors.left: text1.right
    anchors.leftMargin: 5
    signal changed(string newtext)

    width: 70
    height: 23
    anchors.top: parent.top
    anchors.topMargin: 37
    model: typemodel

    onCurrentTextChanged: {

        mainwin.unitGenerator(typebox.currentText);

    }

这是第一个组合框。如您所见,每次更改第一个组合框的值时,都会更新第二个组合框的 c++ 模型(mainwin.unitGenerator(typebox.currentText))。但它似乎并没有更新组合框的模型。

如何在运行时更新 qml 的模型?

4

2 回答 2

33

为了开始解决您的问题,我们需要查看该unitGenerator方法的作用。如果您使用的是自定义模型,则几乎可以肯定您没有正确实现通知。我目前的赌注是你没有发出模型重置的信号。

下面是一个完整的代码示例,展示了如何将 a 绑定QStringListModel到可编辑ListViewComboBoxes。第二个ComboBox模型是根据第一个模型的选择重新生成的。这大概接近您想要的功能。

请注意由QStringListModel. 该模型对显示和编辑角色的处理几乎相同:它们都映射到列表中的字符串值。然而,当您更新特定角色的数据时,信号dataChanged携带您已更改的角色。这可用于中断模型编辑器项 (TextInput) 中可能存在的绑定循环。当您使用自定义模型时,您可能需要实现类似的功能。

display角色用于将组合框绑定到模型。该edit角色用于预填充编辑器对象。编辑器的onTextChanged信号处理程序正在更新display角色,这不会导致自身的绑定循环。如果处理程序正在更新edit角色,它将通过text属性导致绑定循环。

QML 中的模型

QML 中有各种各样的“模型”。在内部,QML 将在模型中包装几乎“任何东西”。内部不是 QObject但仍然可以是模型(例如 a QVariant)的任何东西都不会通知任何人任何事情。

例如,基于QVariant包装的“模型”int不会发出通知,因为QVariant不是QObject可以发出变化的信号。

同样,如果您的“模型”与派生自 的类的属性值相关联QObject,但您未能收到emit属性更改通知信号,它也将不起作用。

如果不知道您的模型类型是什么,则无法分辨。

main.qml

import QtQuick 2.0
import QtQuick.Controls 1.0

ApplicationWindow {
    width: 300; height: 300
    ListView {
        id: view
        width: parent.width
        anchors.top: parent.top
        anchors.bottom: column.top
        model: model1
        spacing: 2
        delegate: Component {
            Rectangle {
                width: view.width
                implicitHeight: edit.implicitHeight + 10
                color: "transparent"
                border.color: "red"
                border.width: 2
                radius: 5
                TextInput {
                    id: edit
                    anchors.margins: 1.5 * parent.border.width
                    anchors.fill: parent
                    text: edit // "edit" role of the model, to break the binding loop
                    onTextChanged: model.display = text
                }
            }
        }
    }
    Column {
        id: column;
        anchors.bottom: parent.bottom
        Text { text: "Type";  }
        ComboBox {
            id: box1
            model: model1
            textRole: "display"
            onCurrentTextChanged: generator.generate(currentText)
        }
        Text { text: "Unit"; }
        ComboBox {
            id: box2
            model: model2
            textRole: "display"
        }
    }
}

主文件

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QStringListModel>
#include <QQmlContext>

class Generator : public QObject
{
    Q_OBJECT
    QStringListModel * m_model;
public:
    Generator(QStringListModel * model) : m_model(model) {}
    Q_INVOKABLE void generate(const QVariant & val) {
        QStringList list;
        for (int i = 1; i <= 3; ++i) {
            list << QString("%1:%2").arg(val.toString()).arg(i);
        }
        m_model->setStringList(list);
    }
};

int main(int argc, char *argv[])
{
    QStringListModel model1, model2;
    Generator generator(&model2);
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    QStringList list;
    list << "one" << "two" << "three" << "four";
    model1.setStringList(list);

    engine.rootContext()->setContextProperty("model1", &model1);
    engine.rootContext()->setContextProperty("model2", &model2);
    engine.rootContext()->setContextProperty("generator", &generator);

    engine.load(QUrl("qrc:/main.qml"));
    QObject *topLevel = engine.rootObjects().value(0);
    QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
    window->show();
    return app.exec();
}

#include "main.moc"
于 2013-09-04T19:42:42.850 回答
4

这实际上更像是对@KubaOber 答案的回答/评论。

我发现如果绑定到正确的事件,实际上不需要使用多个角色执行任何特殊技巧:

onAccepted: model.edit = text

工作得很好并且不会创建任何更新循环(因为它只在“人”/输入修改时调用)。

于 2015-07-29T13:24:26.903 回答