如何根据 QML 中不同 QAbstractListModel 的元素实现列表?

How to implement a list depending on the elements of a different QAbstractListModel in QML?

在 QML 中,我想指定一个选项(字符串)列表,用户可以选择将其插入到我的后端。但我不希望列表包含后端 already 的字符串。如果后端更新,那么选项列表也应该更新。

首先,我编写了一个子类 QAbstractListModel,它为我的后端公开了一个 QString 接口列表。它遵循文档中的指南,并公开了两个自定义函数 push_backindexOf.

class MyModel: public QAbstractListModel {
    Q_OBJECT
public:
    MyModel() { ... } // connect to the backend

    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override {
        Q_UNUSED(role);
        if (!hasIndex(index.row(), index.column())) {
            return {};
        }
        return ... // retreive backend object at index.row() and convert to QString
    }


    Q_INVOKABLE void push_back(const QString& item) {
        beginInsertRows(QModelIndex(), int(_data->size()), int(_data->size()));

        ... // convert QString to backend data type and insert into backend

        endInsertRows();
    }

    Q_INVOKABLE int indexOf(const QString& item) {
        return ... // convert QString to backend type, and search backend for this object. return -1 if not found
    }


private:
    // pointer to backend object
};

我在我的 QML 中考虑过这样的事情。首先查看后端中已有的字符串。这部分工作正常。

ListView {
  anchors.fill: parent
  spacing: 5
  model: backend // an instance MyModel interface, passed in from main.cpp

  delegate: Rectangle {
    height: 25
    width: 200
    Text { text: model.display}
  }
}

然后是可以添加到后端的选项列表,这些选项还不是后端的一部分。这部分不行。

ListView {
  anchors.fill: parent
  spacing: 5
  model: ["option1", "option2", "option3"]

  delegate: Rectangle {
    visible: backend.indexOf(model.modelData) >= 0
    height: 25
    width: 200
    MouseArea {
        id: mouseArea1
        anchors.fill: parent
        onClicked: {
            backend.push_back(model.modelData)
        }
    }
    Text { text: model.modelData }
  }
}

问题是,当后台添加字符串时,这个列表没有刷新。我不认为 Qt 理解 backend.indexOf 需要在修改时重新计算。

你对这个问题的看法是正确的。没有会重新调用 indexOf 的绑定。修复它的一种方法是您可以添加一个 Connections 对象,这样您就可以侦听特定信号,然后手动更新可见的 属性:

  delegate: Rectangle {
    visible: backend.indexOf(model.modelData) >= 0
    height: 25
    width: 200
    MouseArea {
        id: mouseArea1
        anchors.fill: parent
        onClicked: {
            backend.push_back(model.modelData)
        }
    }
    Text { text: model.modelData }

    Connections {
      target: backend
      onCountChanged: visible = (backend.indexOf(model.modelData) >= 0)
    }
  }