web-dev-qa-db-ja.com

QMLでモデルを使用する方法

Qmlとc ++で書かれたGUIがあります。 2つのコンボボックスがあります(qtコントロール5.1)。 2番目のコンボボックスは、最初のコンボボックスの値が変更されるたびに、実行時に更新する必要があります。

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

    }

これは最初のコンボボックスです。ご覧のとおり、2番目のコンボボックスのc ++モデルは、最初のコンボボックスの値が変更されるたびに更新されます(mainwin.unitGenerator(typebox.currentText))。しかし、それはコンボボックスのモデルを更新していないようです。

実行時にqmlのモデルを更新するにはどうすればよいですか?

13
khajvah

問題への対処を開始するには、unitGeneratorメソッドの機能を確認する必要があります。カスタムモデルを使用している場合、通知を正しく実装していないことはほぼ確実です。現時点での私の賭けは、モデルのリセットを通知していないことです。

以下は、QStringListModelを編集可能なListViewおよびComboBoxesに関連付ける方法を示す完全なコード例です。 2番目のComboBoxのモデルは、最初のモデルからの選択に基づいて再生成されます。これはおそらく望ましい機能に近いものです。

QStringListModelによって行われるロールの特定の処理に注意してください。モデルは表示と編集の役割をほぼ同じに扱います。どちらもリスト内の文字列値にマップされます。ただし、特定のロールのデータを更新すると、dataChangedシグナルは変更したロールをのみ伝えます。これは、モデルエディターアイテム(TextInput)に存在する可能性のあるバインディングループを解除するために使用できます。カスタムモデルを使用する場合、同様の機能を実装する必要がある場合があります。

displayロールは、コンボボックスをモデルにバインドするために使用されます。 editロールは、エディターオブジェクトを事前入力するために使用されます。エディターのonTextChangedシグナルハンドラーがdisplayロールを更新しています。これにより、バインディングループ自体が発生することはありません。ハンドラがeditロールを更新している場合、textプロパティを介してバインディングループが発生します。

QMLのモデルについて

QMLにはさまざまな種類の「モデル」があります。内部的には、QMLはモデルのほとんどすべてのものをラップします。内部的にはQObjectではないものの、まだモデルである可能性があります(たとえば、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"
        }
    }
}

main.cpp

#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"
32

これは実際には@KubaOberの回答に対する回答/コメントの多くです。

正しいイベントにバインドする場合、複数のロールを使用して特別なトリックを行う必要は実際にはないことがわかりました。

onAccepted: model.edit = text

正常に動作し、更新ループは作成されません(「人間」/入力の変更でのみ呼び出されるため)。

4