QML与C++:基于ListView调用外部模型进行增删改查(附自定义组件)

news2025/5/18 18:18:20

目录

    • 引言
    • 相关阅读
    • 项目结构
      • 文件组织
    • 核心技术实现
      • 1. 数据模型设计
        • 联系人项目类 (datamodel.h)
        • 数据模型类 (datamodel.h)
        • 数据模型实现 (datamodel.cpp)
      • 2. 主程序入口点 (main.cpp)
      • 3. 主界面设计 (Main.qml)
      • 4. 联系人对话框 (ContactDialog.qml)
      • 5. 自定义组件
        • CustomTextField.qml
        • CustomButton.qml
        • IconButton.qml
    • 运行效果
    • 总结
    • 下载链接

引言

在上一篇中介绍了ListView的数据交互与样式定制后,本文上一点强度,将通过一个联系人管理的案例,详细介绍如何使用QML与C++进行混合开发,充分展示QML的界面设计优势和C++的数据处理能力。该应用基于ListView & Model实现了联系人的增删改查等基本功能,并通过自定义组件提升了用户体验。由于篇幅有限,会省略部分代码,完整代码请看本文最后的下载链接。

下一篇与ListView有关的文章,我将会进一步优化Model的性能。到时候在相关阅读中补上链接。

相关阅读

  • 接上篇 —— QML ListView:列表视图的数据交互与样式定制
  • 下篇 —— QML与C++:基于ListView调用外部模型进行增删改查(性能优化版)

项目结构

以下是本项目的核心结构图:

main.cpp
DataModel类
QML引擎
Main.qml
ContactDialog.qml
自定义组件
CustomTextField.qml
CustomButton.qml
IconButton.qml

文件组织

qml_listview_cpp/
├── CMakeLists.txt           # CMake构建配置
├── main.cpp                 # C++主函数
├── datamodel.h              # 数据模型头文件
├── datamodel.cpp            # 数据模型实现
├── Main.qml                 # 主界面QML
├── ContactDialog.qml        # 联系人对话框QML
├── components/              # 自定义组件目录
│   ├── CustomTextField.qml  # 自定义文本输入框
│   ├── CustomButton.qml     # 自定义按钮
│   └── IconButton.qml       # 自定义图标按钮
├── icons/                   # 图标资源目录
│   ├── user.png             # 用户图标
│   ├── add.png              # 添加图标
│   ├── delete.png           # 删除图标
│   ├── edit.png             # 编辑图标
│   ├── find.png             # 搜索图标
│   ├── phone.png            # 电话图标
│   └── clear.png            # 清除图标
└── image.qrc                # Qt资源文件

核心技术实现

1. 数据模型设计

本项目采用了QAbstractListModel作为基类创建自定义数据模型,实现了联系人数据的管理。C++的数据模型为QML提供了高效的数据源。

联系人项目类 (datamodel.h)
class ContactItem {
public:
    ContactItem(const QString &name, const QString &phone)
        : m_name(name), m_phone(phone) {}
    QString name() const { return m_name; }
    QString phone() const { return m_phone; }
    QString firstLetter() const { return m_name.isEmpty() ? "?" : m_name.left(1).toUpper(); }

private:
    QString m_name;
    QString m_phone;
};

ContactItem类定义了联系人的基本属性:姓名和电话。它还提供了一个便利方法firstLetter()用于获取姓名的首字母,这将用于UI中的头像显示。

数据模型类 (datamodel.h)
class DataModel : public QAbstractListModel
{
    Q_OBJECT

public:
    enum Roles {
        NameRole = Qt::UserRole + 1,
        PhoneRole,
        FirstLetterRole
    };

    explicit DataModel(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;

    // 暴露给QML的方法
    Q_INVOKABLE bool addContact(const QString &name, const QString &phone);
    Q_INVOKABLE bool removeContact(int index);
    Q_INVOKABLE bool editContact(int index, const QString &name, const QString &phone);
    Q_INVOKABLE QVariantList searchContacts(const QString &keyword);
    Q_INVOKABLE void clearSearch();

private:
    QList<ContactItem> m_items;
    QList<ContactItem> m_originalItems; // 用于存储搜索前的原始数据
};

DataModel类继承自QAbstractListModel,实现了必要的虚函数:

  • rowCount(): 返回模型中的项目数量
  • data(): 根据索引和角色返回项目数据
  • roleNames(): 定义了模型中可用的角色名称,这些名称将在QML中使用

此外,还通过Q_INVOKABLE宏定义了几个可以从QML中直接调用的方法:

  • addContact(): 添加联系人
  • removeContact(): 删除联系人
  • editContact(): 编辑联系人
  • searchContacts(): 搜索联系人
  • clearSearch(): 清除搜索,恢复原始列表
数据模型实现 (datamodel.cpp)

数据模型的核心实现如下:

DataModel::DataModel(QObject *parent)
    : QAbstractListModel(parent)
{
    // 添加一些示例联系人数据
    m_items.append(ContactItem("张三", "13800138000"));
    m_items.append(ContactItem("李四", "13900139000"));
    m_items.append(ContactItem("王五", "13700137000"));
    
    // 保存原始数据
    m_originalItems = m_items;
}

QVariant DataModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= m_items.count())
        return QVariant();

    const ContactItem &item = m_items.at(index.row());

    switch (role) {
    case NameRole:
        return item.name();
    case PhoneRole:
        return item.phone();
    case FirstLetterRole:
        return item.firstLetter();
    default:
        return QVariant();
    }
}

// 搜索联系人实现
QVariantList DataModel::searchContacts(const QString &keyword)
{
    if (keyword.isEmpty()) {
        beginResetModel();
        m_items = m_originalItems;
        endResetModel();
        return QVariantList();
    }

    QVariantList results;
    beginResetModel();
    m_items.clear();
    
    for (const ContactItem &item : m_originalItems) {
        if (item.name().contains(keyword, Qt::CaseInsensitive) ||
            item.phone().contains(keyword, Qt::CaseInsensitive)) {
            m_items.append(item);
        }
    }
    endResetModel();
    return results;
}

2. 主程序入口点 (main.cpp)

主函数设置了QML引擎并将C++数据模型暴露给QML:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "datamodel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    QObject::connect(
        &engine,
        &QQmlApplicationEngine::objectCreationFailed,
        &app,
        []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);

    // 创建数据模型实例
    DataModel *model = new DataModel(&engine);
    
    // 将模型暴露给QML
    engine.rootContext()->setContextProperty("dataModel", model);

    engine.loadFromModule("qml_listview_cpp", "Main");

    return app.exec();
}

通过setContextProperty方法,将C++数据模型注册为QML上下文属性,这样在QML代码中就可以直接访问dataModel对象了。

3. 主界面设计 (Main.qml)

主界面采用了QML编写,实现了联系人的列表显示和搜索功能:

import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import "./components"  // 导入自定义组件

Window {
    width: 640
    height: 480
    visible: true
    title: "联系人列表"

    // ... 省略部分代码 ...

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10
        spacing: 10

        // 顶部工具栏
        Rectangle {
            Layout.fillWidth: true
            height: 50
            color: "#f0f0f0"
            radius: 5

            RowLayout {
                anchors.fill: parent
                anchors.margins: 5
                spacing: 10

                CustomTextField {
                    id: searchField
                    Layout.fillWidth: true
                    placeholderText: "搜索联系人..."
                    leftIcon: "qrc:/icons/find.png"
                    onTextChanged: dataModel.searchContacts(text)
                    onRightIconClicked: {
                        text = ""
                        dataModel.clearSearch()
                    }
                }

                IconButton {
                    text: "添加联系人"
                    iconSource: "qrc:/icons/add.png"
                    showBackground: true
                    backgroundColor: "#BBDEFB"
                    onClicked: addContactDialog.open()
                }
            }
        }

        // 联系人列表
        ListView {
            Layout.fillWidth: true
            Layout.fillHeight: true
            model: dataModel
            spacing: 10
            clip: true

            delegate: Rectangle {
                width: ListView.view.width
                height: 80
                color: "#f0f0f0"
                radius: 5

                // ... 省略部分代码 ...

                RowLayout {
                    anchors.fill: parent
                    anchors.margins: 10
                    spacing: 15

                    // 首字母头像
                    Rectangle {
                        width: 60
                        height: 60
                        radius: width / 2
                        color: {
                            const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5", "#9B59B6"]
                            return colors[firstLetter.charCodeAt(0) % colors.length]
                        }

                        Text {
                            anchors.centerIn: parent
                            text: firstLetter
                            color: "white"
                            font.pixelSize: 24
                            font.bold: true
                        }
                    }

                    // 联系人信息
                    ColumnLayout {
                        Layout.fillWidth: true
                        spacing: 5

                        Text {
                            text: name
                            font.bold: true
                            font.pixelSize: 16
                            Layout.fillWidth: true
                        }

                        Text {
                            text: phone
                            color: "#666666"
                            font.pixelSize: 14
                            Layout.fillWidth: true
                        }
                    }

                    // 操作按钮
                    RowLayout {
                        spacing: 10

                        // 编辑按钮
                        IconButton {
                            iconSource: "qrc:/icons/edit.png"
                            onClicked: {
                                currentEditIndex = index
                                editContactDialog.currentName = name
                                editContactDialog.currentPhone = phone
                                editContactDialog.open()
                            }
                        }

                        // 删除按钮
                        IconButton {
                            iconSource: "qrc:/icons/delete.png"
                            onClicked: dataModel.removeContact(index)
                        }
                    }
                }
            }
        }
    }
}

主界面的核心部分是一个ListView,它使用C++提供的dataModel作为数据源。每个联系人项目都显示为一个带有圆形首字母头像、姓名、电话号码以及编辑和删除按钮的矩形卡片。

主界面效果图:

主界面效果图


4. 联系人对话框 (ContactDialog.qml)

为了添加和编辑联系人,项目实现了一个模态对话框:

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "./components"

Dialog {
    id: root
    width: 400
    modal: true

    // 属性
    property bool isEdit: false  // 用于区分是编辑还是添加模式
    property string currentName: ""
    property string currentPhone: ""

    // 标题根据模式动态设置
    title: isEdit ? "修改联系人" : "添加新联系人"

    closePolicy: Dialog.NoAutoClose

    // 信号
    signal contactConfirmed(string name, string phone)

    // ... 省略部分代码 ...

    contentItem: ColumnLayout {
        spacing: 20
        anchors.margins: 10

        CustomTextField {
            id: nameField
            Layout.fillWidth: true
            placeholderText: "姓名"
            leftIcon: "qrc:/icons/user.png"
        }

        CustomTextField {
            id: phoneField
            Layout.fillWidth: true
            placeholderText: "电话"
            leftIcon: "qrc:/icons/phone.png"
            validator: RegularExpressionValidator {
                regularExpression: /^[0-9\+\-\s]*$/
            }
        }

        // 按钮区域
        RowLayout {
            Layout.alignment: Qt.AlignRight | Qt.AlignBottom
            spacing: 10

            CustomButton {
                id: confirmButton
                text: isEdit ? qsTr("保存") : qsTr("确定")
                enabled: nameField.text.length > 0 && phoneField.text.length > 0
                onClicked: root.accept()
            }

            CustomButton {
                id: cancelButton
                text: qsTr("取消")
                bgColor: "#f5f5f5"
                textColor: "#333333"
                onClicked: root.reject()
            }
        }
    }
} 

这个对话框可以在两种模式下工作:添加新联系人和编辑现有联系人。它包含两个自定义文本输入字段用于输入姓名和电话号码,以及确认和取消按钮。

对话框效果图:

添加联系人

5. 自定义组件

为了提升UI的美观度和复用性,项目定义了几个自定义组件:

CustomTextField.qml

此处代码省略…

这个自定义文本输入框增强了标准的TextField,添加了左侧图标、右侧图标或清除按钮等功能。如果所示:

自定义TextField


CustomButton.qml

此处代码省略…

自定义按钮组件提供了更灵活的外观定制,包括背景色、文本色以及悬停效果。

主要在对话窗中使用了CustomButton:

自定义Button


IconButton.qml

此处代码省略…

图标按钮组件实现了一个可以显示图标和文本的自定义按钮,提供了丰富的自定义选项,如图标大小、边框、背景色等。

在列表中使用了IconButton:
IconButton1
在添加联系人按钮上使用了IconButton:
IconButton2

只需要设置背景色和文字即可实现不同的样式效果。


运行效果

查找联系人:

搜索

修改联系人:

请添加图片描述
新增/删除联系人:

新增和删除


总结

本文介绍了一个基于Qt/QML与C++混合开发的联系人管理应用。通过这个示例,我们展示了:

  1. QML与C++协同工作的模式:QML负责直观高效的UI设计,C++处理数据逻辑和模型。
  2. 自定义QML组件的实现:通过组件化设计提高代码复用性和可维护性。
  3. QAbstractListModel的使用:通过继承QAbstractListModel创建自定义数据模型。
  4. 信号与槽机制:利用Qt的信号与槽机制实现UI与数据模型的解耦。

下载链接

您可以通过以下链接获取完整的源代码:GitCode -> QML -> ListView & Model

QML ListView & Model

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2337707.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

postman莫名奇妙报错,可能是注释引起的。postman 过滤请求体中的注释。

postman莫名奇妙报错&#xff0c;可能是注释引起的。postman 过滤请求体中的注释。 1、问题描述2、问题分析3、解决方法 1、问题描述 postman http请求测试时&#xff0c;如果在请求体中添加了注释&#xff0c;那么这个注释会被带到服务端执行&#xff0c;导致服务端接口返回报…

扩增子分析|基于R语言microeco包进行微生物群落网络分析(network网络、Zi-Pi关键物种和subnet子网络图)

一、引言 microeco包是福建农林大学姚敏杰教授团队开发的扩增子测序集成分析。该包综合了扩增子测序下游分析的多种功能包括群落组成、多样性、网络分析、零模型等等。通过简单的几行代码可实现复杂的分析。因此&#xff0c;microeco包发表以来被学界广泛关注&#xff0c;截止2…

中间件--ClickHouse-4--向量化执行(什么是向量?为什么向量化执行的更快?)

1、向量&#xff08;Vector&#xff09;的概念 &#xff08;1&#xff09;、向量的定义 向量&#xff1a;在计算机科学中&#xff0c;向量是一组同类型数据的有序集合&#xff0c;例如一个包含多个数值的数组。在数据库中&#xff0c;向量通常指批量数据&#xff08;如一列数…

【SpringBoot+Vue自学笔记】001

跟着这位老师学习的&#xff1a;https://www.bilibili.com/video/BV1nV4y1s7ZN?vd_sourceaf46ae3e8740f44ad87ced5536fc1a45 前后端开发技术的全栈课程&#xff1a; Java EE企业级框架&#xff1a;SpringBootMyBatisPlus Web前端核心框架&#xff1a;VueElement UI 公共云…

第十节:性能优化-如何排查组件不必要的重复渲染?

工具&#xff1a;React DevTools Profiler 方法&#xff1a;memo、shouldComponentUpdate深度对比 React 组件性能优化&#xff1a;排查与解决重复渲染问题指南 一、定位性能问题&#xff1a;React DevTools 高级用法 使用 React Developer Tools Profiler 精准定位问题组件&…

MATLAB项目实战(一)

题目&#xff1a; 某公司有6个建筑工地要开工&#xff0c;每个工地的位置&#xff08;用平面坐标系a&#xff0c;b表示&#xff0c;距离单位&#xff1a;km&#xff09;及水泥日用量d(t)由下表给出&#xff0e;目前有两个临时料场位于A(5,1)&#xff0c;B(2,7)&#xff0c;日储…

spring boot 文件下载

1.添加文件下载工具依赖 Commons IO is a library of utilities to assist with developing IO functionality. <dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version> </depe…

微服务链路追踪:SleuthZipkin

文章目录 Sleuth & Zipkin一、Sleuth\&Zipkin介绍二、搭建环境三、Sleuth入门操作四、Zipkin搭建及操作五、RabbitMQ方式发送信息六、Elasticsearch持久化 SpringBootAdmin一、Actuator介绍二、Actuator快速入门三、SpringBootAdmin介绍四、SpringBootAdmin快速入门4.1…

java面试篇 4.9(mybatis+微服务+线程安全+线程池)

目录 mybatis&#xff1a; 1、mybatis的执行流程 2、mybatis是否支持延迟加载&#xff1f; 当我们需要去开启全局的懒加载时&#xff1a; 3、mybatis的一级和二级缓存 微服务 1、springcloud五大组件有哪些 2、服务注册和发现是什么意思&#xff1f;springcloud如何实现…

基于电子等排体的3D分子生成模型 ShEPhERD - 评测

一、背景介绍 ShEPhERD 是一个由 MIT 开发的一个 3D 相互作用感知的 ligand-based的分子生成模型&#xff0c;以 arXiv 预印本的形式发表于 2024 年&#xff0c;被ICLR2025 会议接收。文章链接&#xff1a;https://openreview.net/pdf?idKSLkFYHlYg ShEPhERD 是一种基于去噪扩…

GR00T N1:面向通用类人机器人的开放基础模型

摘要 通用型机器人需要具备多功能的身体和智能的大脑。近年来&#xff0c;类人机器人的发展在构建人类世界中的通用自主性硬件平台方面展现出巨大潜力。一个经过大量多样化数据源训练的机器人基础模型&#xff0c;对于使机器人能够推理新情况、稳健处理现实世界的多变性以及快…

QT简单实例

QT简单实例 QT简单实例一&#xff1a;通过拖动创建1.创建工程2.拖动控件实现响应3.文件目录3.1 TestQDialog.pro3.2 main.cpp3.3 dialog.h3.4 dialog.cpp 二&#xff1a;通过动态创建1.创建工程2.文件目录2.1 TestQDialogSelf.pro2.2 main.cpp2.3 dialog.h2.4 dialog.cpp QT简单…

Linux:初学者的简单指令

文章目录 pwd&#xff08;Print working directory&#xff09;whoamilsmkdir ~~cd ~~touch ~~rm ~~ 充当后端服务,我们用xshell工具来进行操作 其中Linux文件是/目录/目录/目录或文件/来表示的&#xff08;其中目录可以看作是windows操作系统的文件夹&#xff0c;只是Linux中…

端侧大模型综述On-Device Language Models: A Comprehensive Review

此为机器翻译&#xff0c;仅做个人学习使用 设备端语言模型&#xff1a;全面回顾 DOI&#xff1a;10.48550/arXiv.2409.00088 1 摘要 大型语言模型 &#xff08;LLM&#xff09; 的出现彻底改变了自然语言处理应用程序&#xff0c;由于减少延迟、数据本地化和个性化用户体验…

python实现音视频下载器

一、环境准备 确保当前系统已安装了wxPython 、 yt-dlp 和FFmpeg。当前主要支持下载youtube音视频 1、安装wxPython pip install wxPython2、安装yt-dp pip install wxPython yt-dlp3、安装FFmpeg 在Windows 10上通过命令行安装FFmpeg&#xff0c;最简便的方式是使用包管理…

三、小白如何用Pygame制作一款跑酷类游戏(按键图片和距离的计算)

三、小白如何用Pygame制作一款跑酷类游戏&#xff08;实现移动距离的计算&#xff0c;以及按键指引的添加&#xff09; 文章目录 三、小白如何用Pygame制作一款跑酷类游戏&#xff08;实现移动距离的计算&#xff0c;以及按键指引的添加&#xff09;前言一、创建字体文件夹1.可…

H5:实现安卓和苹果点击下载App自动跳转到对应的应用市场

一、需求场景 手机扫描下载App&#xff0c;需要根据不同手机自动跳转到对应的应用市场&#xff08;商店&#xff09;里&#xff0c;苹果手机直接打开App Store里指定的app页面&#xff0c;安卓手机如果是海外用户则打开GooglePlay 商店里指定的app页面&#xff0c;国内直接下载…

【Linux】文件传输归档与压缩

目录 配置实验环境 文件传输方法--scp&#xff0c;rsync scp rsync 归档与压缩--tar&#xff0c;gz&#xff0c;bz2&#xff0c;xz&#xff0c;zip 归档---tar 压缩 zip gzip bzip2 xz 归档并压缩 gz bz2 xz 拓展du 配置实验环境 在多个linux系统进行系统传输…

3D人脸扫描技术如何让真人“进入“虚拟,虚拟数字人反向“激活“现实?

随着虚拟人技术的飞速发展&#xff0c;超写实数字人已经成为数字娱乐、广告营销和虚拟互动领域的核心趋势。无论是企业家、知名主持人还是明星&#xff0c;数字分身正在以高度还原的形象替代真人参与各类活动&#xff0c;甚至成为品牌代言、直播互动的新宠。 3D人脸扫描&#…

Git标签的认识

Git标签完全指南&#xff1a;从基础到企业级发布策略 前言 在软件发布领域&#xff0c;Git标签是版本管理的基石。根据2023年GitHub年度报告显示&#xff0c;85%的开源项目使用标签进行版本控制。然而&#xff0c;许多开发者仅停留在git tag的基础使用层面&#xff0c;未能充分…