QML ListView 与 C++ 模型交互

在 Qt 中,QML 的 ListView 可以与 C++ 模型进行交互,这是实现复杂数据展示和业务逻辑的常见方式。以下是几种主要的交互方法:

1. 使用 QAbstractItemModel 派生类

这是最强大和灵活的方式,适合复杂数据结构。

C++ 端实现

cpp

复制代码
// mymodel.h
#include <QAbstractListModel>

class MyModel : public QAbstractListModel
{
    Q_OBJECT
public:
    enum Roles {
        NameRole = Qt::UserRole + 1,
        ColorRole
    };
    
    explicit MyModel(QObject *parent = nullptr);
    
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QHash<int, QByteArray> roleNames() const override;
    
private:
    QList<QPair<QString, QColor>> m_data;
};

// mymodel.cpp
MyModel::MyModel(QObject *parent) : QAbstractListModel(parent)
{
    m_data << qMakePair(QString("Item 1"), QColor("red"))
           << qMakePair(QString("Item 2"), QColor("green"))
           << qMakePair(QString("Item 3"), QColor("blue"));
}

int MyModel::rowCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : m_data.size();
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() >= m_data.size())
        return QVariant();
    
    const auto &item = m_data.at(index.row());
    
    switch (role) {
    case NameRole: return item.first;
    case ColorRole: return item.second;
    default: return QVariant();
    }
}

QHash<int, QByteArray> MyModel::roleNames() const
{
    return {
        {NameRole, "name"},
        {ColorRole, "color"}
    };
}

QML 端使用

qml

复制代码
ListView {
    width: 200; height: 300
    model: myModel  // 从C++暴露的模型
    
    delegate: Rectangle {
        width: ListView.view.width
        height: 40
        color: model.color  // 对应ColorRole
        
        Text {
            text: model.name  // 对应NameRole
            anchors.centerIn: parent
        }
    }
}

注册和暴露模型

cpp

复制代码
// main.cpp
qmlRegisterType<MyModel>("com.example", 1, 0, "MyModel");

// 或者直接设置上下文属性
MyModel model;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("myModel", &model);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

2. 使用 QStringListModel 或 QStandardItemModel

对于简单数据,可以使用 Qt 提供的现成模型:

cpp

复制代码
QStringListModel *model = new QStringListModel(this);
model->setStringList(QStringList() << "Item 1" << "Item 2" << "Item 3");

engine.rootContext()->setContextProperty("myListModel", model);

qml

复制代码
ListView {
    model: myListModel
    delegate: Text { text: model.display }  // 使用默认的display角色
}

3. 使用 QVariantList 或 QList<QObject*>

方法1:QVariantList

cpp

复制代码
QVariantList dataList;
for (int i = 0; i < 5; ++i) {
    QVariantMap item;
    item["name"] = QString("Item %1").arg(i);
    item["value"] = i * 10;
    dataList.append(item);
}

engine.rootContext()->setContextProperty("myData", dataList);

qml

复制代码
ListView {
    model: myData
    delegate: Text { text: model.modelData.name + ": " + model.modelData.value }
}

方法2:QList<QObject*>

cpp

复制代码
class ListItem : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
    // ... 实现getter/setter和信号
};

QList<QObject*> items;
for (int i = 0; i < 5; ++i) {
    items.append(new ListItem(QString("Item %1").arg(i), i*10, this));
}

engine.rootContext()->setContextProperty("myItems", QVariant::fromValue(items));

qml

复制代码
ListView {
    model: myItems
    delegate: Text { text: name + ": " + value }  // 直接访问属性
}

4. 双向交互

从 QML 调用 C++ 方法

cpp

复制代码
// 在模型类中添加方法
Q_INVOKABLE void addItem(const QString &name, const QColor &color) {
    beginInsertRows(QModelIndex(), m_data.size(), m_data.size());
    m_data.append(qMakePair(name, color));
    endInsertRows();
}

qml

复制代码
Button {
    text: "Add Item"
    onClicked: myModel.addItem("New Item", Qt.rgba(Math.random(), Math.random(), Math.random(), 1))
}

从 C++ 触发 QML 更新

cpp

复制代码
// 当数据变化时发出信号
emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, 0));

5. 深入理解 QAbstractItemModel 中的 roleNames() 方法

roleNames()QAbstractItemModel 中的一个关键方法,它在 QML 与 C++ 模型交互中扮演着重要角色。这个方法定义了模型中可用的数据角色及其对应的名称。

5.1 基本用法

cpp

复制代码
QHash<int, QByteArray> MyModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[ColorRole] = "color";
    roles[SizeRole] = "size";
    return roles;
}

5.2 为什么需要 roleNames()

  1. QML 访问数据:QML 通过角色名称而不是数字来访问模型数据

  2. 角色映射:将 C++ 中的枚举角色转换为 QML 可识别的字符串

  3. 数据绑定:使 QML 的属性绑定系统能够工作

5.3 详细解释

1. 角色枚举定义

通常在模型头文件中定义角色枚举:

cpp

复制代码
class MyModel : public QAbstractListModel
{
    Q_OBJECT
public:
    enum CustomRoles {
        NameRole = Qt::UserRole + 1,  // 从UserRole开始避免与内置角色冲突
        ColorRole,
        SizeRole,
        // 添加更多角色...
    };
    // ...
};

2. 实现 roleNames()

cpp

复制代码
QHash<int, QByteArray> MyModel::roleNames() const
{
    // 首先获取基类的角色(可选)
    QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
    
    // 添加自定义角色
    roles[NameRole] = "name";
    roles[ColorRole] = "color";
    roles[SizeRole] = "size";
    
    return roles;
}

3. 在 QML 中使用

qml

复制代码
ListView {
    model: myModel
    delegate: Text {
        text: model.name + " (" + model.color + ")"  // 使用roleNames中定义的名称
        font.pixelSize: model.size
    }
}

6. 性能优化技巧

  1. 对于大型数据集,实现 fetchMore()canFetchMore() 进行懒加载

  2. 使用 QQmlListProperty 替代 QList<QObject*> 可以获得更好的性能

  3. 在模型中实现 roleNames() 时,确保角色值是连续的

  4. 考虑使用 QIdentityProxyModelQSortFilterProxyModel 进行数据转换和过滤

7. 常见问题解决

问题1:模型更新但视图不刷新

  • 确保正确实现了 dataChanged() 信号

  • 检查是否调用了 beginInsertRows()/endInsertRows()

问题2:QML 中无法访问角色

  • 确保 roleNames() 正确实现

  • 检查 QML 中使用的角色名是否匹配

问题3:性能低下

  • 避免在 data() 中进行复杂计算

  • 考虑使用 QAbstractTableModel 替代 QAbstractListModel 对于表格数据

相关推荐
火山上的企鹅11 小时前
异形遮罩之QML中的 `OpacityMask` 实战
开发语言·qml·opacitymask
byxdaz5 天前
QML Item 元素
qml
苏克贝塔5 天前
QML面试笔记--UI设计篇05容器控件
qml
机器视觉知识推荐、就业指导11 天前
QML 批量创建模块 【Repeater】 组件详解
前端·c++·qml
m0_5557629013 天前
qml 基本元素
qt·qml
钱彬 (Qian Bin)14 天前
QT Quick(C++)跨平台应用程序项目实战教程 5 — 界面设计
c++·qt·教程·音乐播放器·qml·qt quick
__ocean25 天前
qml中ComboBox组件onCurrentIndexChanged与onActivated的使用
qt·qml
钱彬 (Qian Bin)1 个月前
QT Quick(C++)跨平台应用程序项目实战教程 3 — 项目基本设置(窗体尺寸、中文标题、窗体图标、可执行程序图标)
c++·人工智能·音乐播放器·qml·界面设计·qt quick
梦起丶1 个月前
「 DelegateUI 」Ant-d 风格的 Qt Qml UI 套件
qt·ui·qml·ant-d·ui-kit