当前位置: 首页 > news >正文

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. 考虑使用 QIdentityProxyModel 或 QSortFilterProxyModel 进行数据转换和过滤

7. 常见问题解决

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

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

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

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

  • 确保 roleNames() 正确实现

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

问题3:性能低下

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

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

相关文章:

  • 微信小程序实战案例 - 餐馆点餐系统 阶段 0 - 环境就绪
  • 玩转Docker | 使用Docker部署MicroBin粘贴板
  • Java新手村第二站:泛型、集合与IO流初探
  • k8s的配置文件总结
  • Go学习路线指南
  • springboot框架集成websocket依赖实现物联网设备、前端网页实时通信!
  • MySQL——MVCC(多版本并发控制)
  • 免费在线文档工具,在线PDF添加空白页,免费在任意位置插入空白页,多样化的文件处理
  • 【AI论文】MM-IFEngine:迈向多模态指令遵循
  • Magnet Pro Macbook窗口分屏管理软件【提高效率工具】
  • 从零训练LLM-1.训练BPE
  • 猫咪如厕检测与分类识别系统系列【五】信息存储数据库改进+添加猫咪页面制作+猫咪躯体匹配算法架构更新
  • SQL 解析 with as
  • C++ 入门六:多态 —— 同一接口的多种实现之道
  • Java 8中的Lambda 和 Stream (from Effective Java 第三版)
  • 【模态分解】EEMD-集合经验模态分解
  • CSI-PVController-volumeWorker
  • 【家政平台开发(40)】功能测试全解析:从执行到报告撰写
  • 应急响应靶机-Linux(2)
  • Qwen2.5-VL Technical Report 论文翻译和理解
  • 图集|俄罗斯举行纪念苏联伟大卫国战争胜利80周年阅兵式
  • 梅花奖在上海|第六代“杨子荣”是怎样炼成的?
  • 如此城市|上海老邬:《爱情神话》就是我生活的一部分
  • 南通市委常委、市委秘书长童剑跨市调任常州市委常委、组织部部长
  • 中国以“大幅开放市场”回应贸易保护主义
  • 夜读丨母亲的手擀面