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

目录

    • 引言
    • 相关阅读
    • 项目结构
      • 文件组织
    • 核心技术实现
      • 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_OBJECTpublic: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);// 将模型暴露给QMLengine.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: 640height: 480visible: truetitle: "联系人列表"// ... 省略部分代码 ...ColumnLayout {anchors.fill: parentanchors.margins: 10spacing: 10// 顶部工具栏Rectangle {Layout.fillWidth: trueheight: 50color: "#f0f0f0"radius: 5RowLayout {anchors.fill: parentanchors.margins: 5spacing: 10CustomTextField {id: searchFieldLayout.fillWidth: trueplaceholderText: "搜索联系人..."leftIcon: "qrc:/icons/find.png"onTextChanged: dataModel.searchContacts(text)onRightIconClicked: {text = ""dataModel.clearSearch()}}IconButton {text: "添加联系人"iconSource: "qrc:/icons/add.png"showBackground: truebackgroundColor: "#BBDEFB"onClicked: addContactDialog.open()}}}// 联系人列表ListView {Layout.fillWidth: trueLayout.fillHeight: truemodel: dataModelspacing: 10clip: truedelegate: Rectangle {width: ListView.view.widthheight: 80color: "#f0f0f0"radius: 5// ... 省略部分代码 ...RowLayout {anchors.fill: parentanchors.margins: 10spacing: 15// 首字母头像Rectangle {width: 60height: 60radius: width / 2color: {const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5", "#9B59B6"]return colors[firstLetter.charCodeAt(0) % colors.length]}Text {anchors.centerIn: parenttext: firstLettercolor: "white"font.pixelSize: 24font.bold: true}}// 联系人信息ColumnLayout {Layout.fillWidth: truespacing: 5Text {text: namefont.bold: truefont.pixelSize: 16Layout.fillWidth: true}Text {text: phonecolor: "#666666"font.pixelSize: 14Layout.fillWidth: true}}// 操作按钮RowLayout {spacing: 10// 编辑按钮IconButton {iconSource: "qrc:/icons/edit.png"onClicked: {currentEditIndex = indexeditContactDialog.currentName = nameeditContactDialog.currentPhone = phoneeditContactDialog.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: rootwidth: 400modal: 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: 20anchors.margins: 10CustomTextField {id: nameFieldLayout.fillWidth: trueplaceholderText: "姓名"leftIcon: "qrc:/icons/user.png"}CustomTextField {id: phoneFieldLayout.fillWidth: trueplaceholderText: "电话"leftIcon: "qrc:/icons/phone.png"validator: RegularExpressionValidator {regularExpression: /^[0-9\+\-\s]*$/}}// 按钮区域RowLayout {Layout.alignment: Qt.AlignRight | Qt.AlignBottomspacing: 10CustomButton {id: confirmButtontext: isEdit ? qsTr("保存") : qsTr("确定")enabled: nameField.text.length > 0 && phoneField.text.length > 0onClicked: root.accept()}CustomButton {id: cancelButtontext: 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.mzph.cn/news/901869.shtml

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

相关文章

【MySQL】事务ACID理解记忆

事务的 ACID 特性详解 数据库中的 事务&#xff08;Transaction&#xff09; 是一组操作的集合&#xff0c;这些操作要么全部执行&#xff0c;要么全部不执行。为了保证事务可靠执行&#xff0c;必须满足 ACID 四大特性&#xff1a; 特性英文缩写简要说明原子性Atomicity事务…

MYSQL “Too Many Connections“ 错误解决

1.查询当前连接数 show status like "Threads_connected"; 2.查询数据库最大连接数 show variables like "max_connections" 3.查询所有活动连接 show processlist; 4.根据查询结果观察是否有长时间未被释放的连接 参数解释 : 字段说明id连接的唯一…

Python爬虫实战:基于 Scrapy 框架的微博数据爬取研究

一、引言 1.1 研究背景 在当今数字化时代,社交媒体已成为信息传播和公众交流的重要平台。微博作为国内极具影响力的社交媒体之一,每日产生海量的用户生成内容,涵盖新闻资讯、社交互动、娱乐八卦、热点话题讨论等多个领域。这些数据不仅反映了公众的兴趣偏好、情感态度和社…

猫咪如厕检测与分类识别系统系列【九】视频检测区域在线绘制+支持摄像头+网络摄像头+整体构建【上】

前情提要 家里养了三只猫咪&#xff0c;其中一只布偶猫经常出入厕所。但因为平时忙于学业&#xff0c;没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关&#xff0c;频繁如厕可能是泌尿问题&#xff0c;停留过久也可能是便秘或不适。为了更科学地了解牠的如…

【AI插件开发】Notepad++ AI插件开发实践:支持多平台多模型

引言 上篇文章我们的Notepad插件介绍到Dock窗口集成&#xff0c;本篇将继续完善插件功能&#xff0c;主要包括两个部分&#xff1a; 支持多平台、多模型支持多种授权验证、接口类型 一、多平台 原先的配置项很简单&#xff1a; // PluginConf.h class PlatformConf { publ…

【C#】Socket通信的使用

在C#中&#xff0c;Socket通信是一种用于实现网络通信的底层技术。通过Socket&#xff0c;程序可以在网络上与其他设备进行数据交换。以下是如何使用C#中的System.Net.Sockets命名空间来实现Socket通信的详细步骤。 1. Socket通信的基本概念 Socket: 一个Socket是网络通信的端…

2024年第九届团队程序设计天梯赛c++题解L1-L3-1(附PTA网址)

L1-1 编程解决一切 5分 L1-097 编程解决一切 - 团体程序设计天梯赛-练习集 (pintia.cn)https://pintia.cn/problem-sets/994805046380707840/exam/problems/type/7?problemSetProblemId1781658570803388416 #include<bits/stdc.h> #define int long long using namesp…

ICMAN防水触摸芯片 - 复杂环境下精准交互,提升触控体验

▍核心优势 ◆ 超强抗干扰能力 ◆ 工业级设计&#xff0c;一致性和稳定性好 ▍提供场景化解决方案 【智能厨电矩阵】抽油烟机档位调节 | 电磁炉火力触控 | 洗碗机模式切换 【卫浴设备方案】淋浴房雾化玻璃控制 | 智能马桶触控面板 | 浴缸水位感应 【工业控制应用】仪器仪…

Golang|抽奖相关

文章目录 抽奖核心算法生成抽奖大转盘抽奖接口实现 抽奖核心算法 我们可以根据 单商品库存量/总商品库存量 得到每个商品被抽中的概率&#xff0c;可以想象这样一条 0-1 的数轴&#xff0c;数轴上的每一段相当于一种商品&#xff0c;概率之和为1。 抽奖时&#xff0c;我们会生…

OpenCV 图形API(43)颜色空间转换-----将 BGR 图像转换为 LUV 色彩空间函数BGR2LUV()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将图像从BGR色彩空间转换为LUV色彩空间。 该函数将输入图像从BGR色彩空间转换为LUV。B、G和R通道值的传统范围是0到255。 输出图像必须是8位无符…

【Python】用Python写一个俄罗斯方块玩玩

【Python】用Python写一个俄罗斯方块玩玩 一、引言1.成品效果展示 二、思考准备1.思考设计2.代码设计2.1 游戏页面2.2 控件设计2.2.1 方块生成2.2.2 方块碰撞2.2.3 方块消融2.2.4 游戏主循环2.2.5 游戏窗口 三、游戏完整版 一、引言 今日看到侄子在玩游戏&#xff0c;凑近一看…

维港首秀!沃飞长空AE200亮相香港特别行政区

4月13日-16日&#xff0c;第三届香港国际创科展在香港会议展览中心盛大举办。 作为国内领先、国际一流的eVTOL主机厂&#xff0c;沃飞长空携旗下AE200批产构型登陆国际舞台&#xff0c;以前瞻性的创新技术与商业化应用潜力&#xff0c;吸引了来自全球17个国家及地区的行业领袖…

Openfein实现远程调用的方法(实操)

文章目录 环境准备一、URL中接收参数二、接收一个参数三、接收多个参数四、传递对象五、传递JSON格式数据 环境准备 下面的配置&#xff0c;服务调用方加入即可。 依赖导入&#xff1a; <!-- openfeign依赖--><dependency><groupId>org.springframe…

Bright+Data网页解锁器:旅游行业数据革命的“隐形引擎”

在数字经济浪潮中&#xff0c;旅游行业正经历前所未有的变革。当消费者指尖滑动间完成跨国酒店预订&#xff0c;当航空公司每秒调整万次舱位价格&#xff0c;背后是一场无声的数据战争。而在这场战争中&#xff0c;BrightData网页解锁器正成为旅游企业破局的关键武器——它像一…

OpenCV 图形API(38)图像滤波-----Sobel 算子操作函数Sobel()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::gapi::Sobel 函数是 OpenCV 的 G-API 模块中用于执行 Sobel 算子操作的一个函数&#xff0c;主要用于图像的边缘检测。Sobel 算子通过计算图…

CS5346 - Interactivity in Visualization 可视化中的交互

文章目录 Visualization representation interactionInteraction &#xff08;交互&#xff09;Benefits (好处)Typical Interaction Techniques&#xff08;交互技术&#xff09;SelectFilteringAbstract / Elaborate几何放缩&#xff08;Geometric zoom)语义放缩&#xff0…

第十六届蓝桥杯大赛软件赛省赛 C++ 大学 B 组 部分题解

赛时参加的是Python组&#xff0c;这是赛后写的题解&#xff0c;还有两题暂时还不会&#xff0c;待更新 题目链接题目列表 - 洛谷 | 计算机科学教育新生态 A 移动距离 答案&#xff1a;1576 C 可分解的正整数 Python3 import itertools from functools import cmp_to_ke…

Vue 解决 Error: please transfer a valid prop path to form item!

在 Vue.js 中使用表单验证库&#xff08;如 VeeValidate 或 Element UI 的表单组件时&#xff09;&#xff0c;遇到错误信息 "please transfer a valid prop path to form item!" 通常指的是在表单项的属性绑定中&#xff0c;路径&#xff08;prop path&#xff09;不…

在 Visual Studio Code 中安装通义灵码 - 智能编码助手

高效的编码工具对于提升开发效率和代码质量至关重要。 通义灵码作为一款智能编码助手&#xff0c;为开发者提供了全方位的支持。 本文将详细介绍如何在 Visual Studio Code&#xff08;简称 VSCode&#xff09;中安装通义灵码&#xff0c;以及如何进行相关配置以开启智能编码…

SQL 解析 with as dual sysdate level

目录 sql的运行顺序 with as EXTRACT ​编辑 dual sysdate level ​编辑 ​编辑 Oracle中的日期存储 核心部分 拆解字符串并计算最小值 关联子查询 NVL 函数 REGEXP_SUBSTR() sql的运行顺序 <select id="getTrendList" parameterType="java.uti…