Как использовать модели с QML?

У меня есть GUI, написанный в qml и С++. Есть 2 comboboxes (qt control 5.1). Второе поле со списком должно обновляться во время выполнения, когда изменяется значение первого.

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

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

Это две модели, которые я даю qml из С++.

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

    }

Это первое поле со списком. Как вы видите, С++-модель второй combobox обновляется каждый раз, когда изменяется значение первого (mainwin.unitGenerator(typebox.currentText)). Но, похоже, он не обновляет модель combobox.

Как обновить модель qml во время выполнения?

Ответ 1

Чтобы даже начать решать вашу проблему, нам нужно посмотреть, что делает метод unitGenerator. Если вы используете пользовательскую модель, она почти уверена, что вы неправильно выполняете уведомления. Моей ставкой на данный момент было бы то, что вы не сигнализируете модель reset.

Ниже приведен полный пример кода, который показывает, как вы можете связать QStringListModel с редактируемыми ListView и ComboBox es. Вторая модель 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"

Ответ 2

На самом деле это больше ответ/комментарий к ответу @KubaOber.

Я обнаружил, что на самом деле нет необходимости делать какие-либо специальные трюки, используя несколько ролей, если вы привязываетесь к правильному событию:

onAccepted: model.edit = text

работает просто отлично и не создает никакого цикла обновления (поскольку он вызван только для "человеческой" /модификации ввода).