Qt快速入门(MV架构之TableView + QStandardItemModel + 自定义代理小案例)

Qt快速入门(MV架构之TableView + QStandardItemModel + 自定义代理小案例)

关于MV架构的简单介绍

在Qt框架中,代理(Delegate)、模型(Model)和视图(View)之间的关系构成了MVVM(Model-View-ViewModel)架构的一部分,尽管Qt通常使用Model-View架构。这三者之间的关系可以这样理解:

1. Model(模型)

Model是数据的核心代表,它负责存储和管理应用程序的数据。Model提供了数据的接口,允许View查询和修改数据。Model与View的交互是通过信号和槽机制来完成的,当Model中的数据发生变化时,它会发出信号通知View进行更新。

2. View(视图)

View是Model数据的展示层,它负责将数据以用户友好的形式展示出来,并接收用户的交互操作。在Qt中,View通常是通过一些控件来实现的,比如QListView、QTableView、QTreeView等。View不处理数据的逻辑,它只是简单地展示Model提供的数据。

3. Delegate(代理)

Delegate位于Model和View之间,充当了一个中介的角色。它允许开发者为View中的每个项创建自定义的编辑器或显示组件。代理的作用是处理View中的项的创建、显示和编辑。当用户与View交互时,代理负责将用户的输入转换为对Model的修改,同时也负责将Model的数据转换为View中的显示形式。

代理、模型和视图之间的关系

Model与View:Model和View之间通过数据接口进行交互。Model提供数据,View展示数据。Model通过信号通知View数据的变化,View通过槽来响应这些信号并更新显示。

Model与Delegate:Model提供了数据的接口,而Delegate负责将这些数据以特定方式显示在View中。Delegate从Model获取数据,并将其转换为用户可以理解的形式。

View与Delegate:View使用Delegate来创建和管理每个项的显示和编辑。Delegate为View中的项提供自定义的外观和行为,使得View可以展示复杂的数据项。

Delegate作为中介:Delegate作为Model和View之间的中介,它处理用户的输入并将这些输入转换为对Model的操作。同时,它也负责将Model的数据格式化并展示在View中。

需要注意的是,MV架构中,默认的代理使用的是单行文本框!

在这里插入图片描述

UI设计

本博客需要的资源链接。提取码:14ml。

  1. 创建项目,选择QMainWindow作为主窗口的基类。主窗口名为:TableWindow。

  2. 为项目添加一个资源文件,将项目用到的图标引用到资源文件中。

  3. 双击ui文件,移除菜单栏,添加工具栏,保留状态栏。添加数个QAction,如下图:

    在这里插入图片描述

  4. 界面设计:

    控件名bjectnameframeShapeframeShadowreadOnly
    QTableViewm_tablewinpanelsunken\
    QPlainTextEditm_editwinpanelsunken
  5. 系统信号和系统槽的连接:

    在这里插入图片描述

  6. 系统信号和自定义槽的连接(鼠标右击创建的QAction,选择转到槽):

    QAction要连接的信号
    加粗triggered(bool)
    其余的QAction(除退出外)triggered()

整体效果如下:

在这里插入图片描述

功能实现

添加一个自定义代理类,类名为ComboBoxDelegate,基类为:QStyledItemDelegate

class ComboBoxDelegate : public QStyledItemDelegate
{
public:ComboBoxDelegate();protected:// 创建一个编辑框QWidget * createEditor(QWidget *parent,const QStyleOptionViewItem &option,const QModelIndex &index) const override;// 将模型中指定索引位置的数据设置到编辑框组件中void setEditorData(QWidget *editor,const QModelIndex &index) const override;// 将编辑框中的数据放回到模型中void setModelData(QWidget *editor,QAbstractItemModel *model,const QModelIndex &index) const override;// 根据视图的样式调整编辑组件的几何形状void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,const QModelIndex &index) const override;
};ComboBoxDelegate::ComboBoxDelegate()
{}/**  1.创建一个编辑框*  参数:*      参数1 - 新创建的编辑器的父部件*      参数2 - 渲染视图的样式选项*      参数3 - 要编辑位置的模型索引
*/
QWidget * ComboBoxDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem &option,const QModelIndex &index) const
{Q_UNUSED(option);Q_UNUSED(index);QComboBox* editor = new QComboBox(parent );editor->addItem("男");editor->addItem("女");return editor;
}/**  1.将模型中的数据放到编辑框中*      参数1:编辑组件*      参数2:需要设置数据的模型索引**  创建的组件是QComboBox - 给的是QWidget*/
void ComboBoxDelegate::setEditorData(QWidget *editor,const QModelIndex &index) const
{static_cast<QComboBox*>(editor)->setCurrentText(index.model()->data(index, Qt::EditRole).toString());
}/*1.将编辑框中的数据给到模型
*/
void ComboBoxDelegate::setModelData(QWidget *editor,QAbstractItemModel *model,const QModelIndex &index) const
{// index - 将数据放到模型的哪个位置model->setData(index,static_cast<QComboBox*>(editor)->currentText(),Qt::EditRole);
}void ComboBoxDelegate::updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,const QModelIndex &index) const
{Q_UNUSED(index);editor->setGeometry(option.rect);
}

在TableWindow类中,添加Model、和标签,此外手动添加一个m_selection成员的currentChanged信号的槽函数。定义如下:

class TableWindow : public QMainWindow
{Q_OBJECTprivate slots:
/*...
*/// item选择发生了改变void on_m_selection_currentChanged(const QModelIndex &current,const QModelIndex &previous);
private:void  initModel(QStringList const& strings);Ui::TableWindow *ui;QLabel* m_labCurFile;   // 状态栏显示信息QLabel* m_labCellPos;   // 状态栏显示信息QLabel* m_labCellText;  // 状态栏显示信息QStandardItemModel* m_model;QItemSelectionModel* m_selection;};

TableWindow的构造函数实现如下:

TableWindow::TableWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::TableWindow), m_labCurFile(new QLabel("当前文件: ")), m_labCellPos(new QLabel("单元格位置: ")), m_labCellText(new QLabel("单元格内容: ")), m_model(new QStandardItemModel(this)), m_selection(new QItemSelectionModel(m_model))
{ui->setupUi(this);m_labCurFile->setMinimumWidth(420);ui->statusBar->addWidget(m_labCurFile);m_labCellPos->setMinimumWidth(190);ui->statusBar->addWidget(m_labCellPos);m_labCellText->setMinimumWidth(190);ui->statusBar->addWidget(m_labCellText);ui->m_table->setModel(m_model); // view和model的绑定ui->m_table->setSelectionModel(m_selection);connect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),this, SLOT(on_m_selection_currentChanged(QModelIndex,QModelIndex)));// 默认代理是一个编辑框。 // 添加一个自定义代理// 选择对于某一列的数据添加自定义代理ui->m_table->setItemDelegateForColumn(3, new ComboBoxDelegate);
}

加粗的实现:

// 加粗Action的槽函数
void TableWindow::on_m_actBold_triggered(bool checked)
{// selectedIndexes - 获取所有的选中的项的索引for(QModelIndex const& index :m_selection->selectedIndexes()){QStandardItem* item = m_model->itemFromIndex(index);QFont font = item->font();// 获取该item的字体font.setBold(checked);item->setFont(font);}
}

打开文件的实现:

// 打开Action的槽函数
void TableWindow::on_m_actOpen_triggered()
{// 1.获取文件的路径QString path = QFileDialog::getOpenFileName(this, "打开",QCoreApplication::applicationDirPath(),"逗号分隔符文件(*.csv);;所有文件(*.*)");if(path.isEmpty())return;//2.打开文件// 构造一个QFile对象表示的就是path所指向的文件QFile file(path);// 打开file指向的文件 - 以只读和文本模式打开该文件if(!file.open(QIODevice::ReadOnly | QIODevice::Text))return;// 3.读取文件QTextStream stream(&file);// 构建文本流对象, 关联已经打开的文件//  stream.setCodec("utf-8");QStringList strings;// readLine - 读一行  atEnd - 文件末尾// 将数据读出来后 - 放到strings列表中while(!stream.atEnd())strings.append(stream.readLine());file.close();initModel(strings);m_labCurFile->setText("当前文件: " + path);
}void  TableWindow::initModel(QStringList const& strings)
{m_model->clear();// 1.strings.at(0)   获取的是strings字符串列表中的第一个字符串// 设置列的标题 - 参数1:使用逗号将数据分割, 参数2:忽略空字符串// 将分割后的字符串作为列的标题m_model->setHorizontalHeaderLabels(strings.at(0).split(",", Qt::SkipEmptyParts));// 2.填充数据 - 填充n-1次// values = 1001 张飞 1996-09-12 男 13512345678// 2.3.根据列数获取要循环放入数据的次数int rowCount = strings.count() - 1;for(int row = 0; row < rowCount; row++){QStringList values = strings.at(row + 1).split(",",Qt::SkipEmptyParts);int columnCount = values.size();for(int column = 0; column < columnCount; column++){m_model->setItem(row, column,new QStandardItem(values.at(column)));}}m_selection->setCurrentIndex(m_model->index(0,0), QItemSelectionModel::Select);
}

保存的实现:

//  保存Action对应的槽函数
void TableWindow::on_m_actSave_triggered()
{// 1.获取保存文件路径QString path = QFileDialog::getSaveFileName(this, "保存",QCoreApplication::applicationDirPath(),"逗号分隔符文件(*.csv);;所有文件(*.*)");if(path.isEmpty())return;// 2.打开文件// 构造一个QFile对象表示的就是path所指向的文件QFile file(path);// 打开file指向的文件 - 以可读可写和文本模式  + 清空原文件打开该文件if(!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))return;// 3.写入文件QTextStream stream(&file);// 构建文本流对象, 关联已经打开的文件//  stream.setCodec("utf-8");// 4.写入列标题// 学号  姓名  出生日期  性别  电话// 循环遍历模型的列 逐列写入标题到文件中int columnCount = m_model->columnCount();for(int col = 0; col < columnCount; col++){stream << m_model->horizontalHeaderItem(col)->text() <<(col == columnCount - 1 ? "\n" : ",");}// 5.写入数据行int rowCount = m_model->rowCount();for(int row = 0; row < rowCount; row++){for(int col = 0; col < columnCount; col++){stream << m_model->item(row, col)->text() <<(col == columnCount - 1 ? "\n" : ",");}}// 6.关闭文件file.close();
}

预览的实现:

//预览Action对应的槽函数
void TableWindow::on_m_actPreview_triggered()
{// 1.清空QPlainTextEdit内容ui->m_edit->clear();// 2.显示列标题QString text;int columnCount = m_model->columnCount();for(int col = 0; col < columnCount; col++){text += m_model->horizontalHeaderItem(col)->text()+ (col == columnCount - 1 ? "" : ",");}ui->m_edit->appendPlainText(text);// 3.显示数据行int rowCount = m_model->rowCount();for(int row = 0; row < rowCount; row++){QString text;for(int col = 0; col < columnCount; col++){text += m_model->item(row, col)->text() +(col == columnCount - 1 ? "" : ",");}ui->m_edit->appendPlainText(text);}
}

添加的实现:

// 添加Action对应的槽函数
void TableWindow::on_m_actAppend_triggered()
{// 1.先获取有多少列int columnCount = m_model->columnCount();if(!columnCount)return;// 2.创建空数据项// 创建新的空数据行 - 需要columnCount个数据项QList<QStandardItem*> items;// 用于存储空的数据项for(int col = 0; col < columnCount; col++)items << new QStandardItem;// 3.插入新行m_model->insertRow(m_model->rowCount(), items);// 4.设置当前选择m_selection->clearSelection();m_selection->setCurrentIndex(m_model->index(m_model->rowCount() -1, 0), QItemSelectionModel::Select);
}

插入的实现:

// 插入Action对应的槽函数
void TableWindow::on_m_actInsert_triggered()
{// 1.先获取有多少列int columnCount = m_model->columnCount();if(!columnCount)return;// 2.创建空数据项// 创建新的空数据行 - 需要columnCount个数据项QList<QStandardItem*> items;// 用于存储空的数据项for(int col = 0; col < columnCount; col++)items << new QStandardItem;// 3.获取当前的模型索引QModelIndex current = m_selection->currentIndex();m_model->insertRow(current.row(), items);// 4.设置当前选择m_selection->clearSelection();m_selection->setCurrentIndex(current, QItemSelectionModel::Select);
}

删除的实现:

// 删除Action对应的槽函数
void TableWindow::on_m_actDelete_triggered()
{// 1.先获取当前位置的索引// 1.1.获取上一行,同一列的位置(上一行, 同一列)// 1.2.判断当前行是否是最后一行QModelIndex current = m_selection->currentIndex();QModelIndex above = m_model->index( current.row() - 1, current.column());bool last = current.row() == m_model->rowCount() - 1;// 2.移除选中行m_model->removeRow(current.row());m_selection->setCurrentIndex(last ? above : current,QItemSelectionModel::Select);
}

对齐的实现:

// 左对齐Action对应的槽函数
void TableWindow::on_m_actLeft_triggered()
{// 1.遍历选中的索引for(QModelIndex const& index : m_selection->selectedIndexes()){m_model->itemFromIndex(index)->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);}
}
// 中对齐Action对应的槽函数
void TableWindow::on_m_actCenter_triggered()
{// 1.遍历选中的索引for(QModelIndex const& index : m_selection->selectedIndexes()){m_model->itemFromIndex(index)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);}
}
// 右对齐Action对应的槽函数
void TableWindow::on_m_actRight_triggered()
{// 1.遍历选中的索引for(QModelIndex const& index : m_selection->selectedIndexes()){m_model->itemFromIndex(index)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);}
}

状态栏文本动态变化的实现:

// 选择项改变对应的槽函数
void TableWindow::on_m_selection_currentChanged(const QModelIndex &current,const QModelIndex &previous){Q_UNUSED(previous);if(!current.isValid())return;// 添加单元格位置m_labCellPos->setText(QString(" 单元格位置:第%1行, 第%2列 ").arg(current.row() + 1).arg(current.column() + 1));// 添加单元格内容QStandardItem* item = m_model->itemFromIndex(current);m_labCellText->setText(" 单元格内容: " + item->text());// 检查并更新粗体显示状态// item->font().bold() - 判断选中的item的字体是否为 粗体ui->m_actBold->setChecked(item->font().bold());
}

运行效果如下:

在这里插入图片描述


本章完结

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

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

相关文章

51单片机实验03-单片机定时/计数器实验

目录 一、实验目的 二、实验说明 1、51单片机有两个16位内部计数器/定时器&#xff08;C/T&#xff0c; Counter/Timer&#xff09;。 2、模式寄存器TMOD 1) M1M0工作模式控制位&#xff1b; 2) C/T定时器或计数器选择位&#xff1a; 3&#xff09;GATE定时器/计数器运行…

Python零基础从小白打怪升级中~~~~~~~模块+异常+Pycharm的debug调试

第十节&#xff1a;模块异常Debug 一、Python模块 Python 模块(Module)&#xff0c;是一个 Python 文件&#xff0c;以 .py 结尾&#xff0c;模块能定义函数&#xff0c;类和变量&#xff0c;模块里也能包含可执行的代码。 1、导入模块 导入模块的5中方式 import 模块名fr…

C++ 之 【类与对象】从入门到精通一条龙服务 最终篇(static成员、友元、匿名对象。。。)

&#x1f4b4;到用时方恨早&#xff0c;白首方悔挣的少 车到山前没有路&#xff0c;悬崖勒马勒不住 一、再谈构造函数 1.构造函数体赋值 2.初始化列表 3.explicit关键字 二、Static成员 1.概念 2.特性 三、友元 1.友元函数 2.友元类 四、内部类 五、匿名对象 六、…

结合 tensorflow.js 、opencv.js 与 Ant Design 创建美观且高性能的人脸动捕组件并发布到InsCode

系列文章目录 如何在前端项目中使用opencv.js | opencv.js入门如何使用tensorflow.js实现面部特征点检测tensorflow.js 如何从 public 路径加载人脸特征点检测模型tensorflow.js 如何使用opencv.js通过面部特征点估算脸部姿态并绘制示意图tensorflow.js 使用 opencv.js 将人脸…

模型预测控制MPC(1)—— 基础概念

文章目录 1. 历史沿革1.1 控制论1.2 最优控制与强化学习 2. 模型预测控制 1. 历史沿革 我本科做机器人的时候接触过一点控制&#xff0c;主要做了大量 PID 在嵌入式控制系统的应用&#xff1b;硕士期间研究方向是强化学习。在我调研 MPC 的过程中&#xff0c;发现它同时出现在…

云笔记小程序的实现

1.前言 云笔记, 是基于HotApp小程序统计云后台提供的api接口开发的一个微信小程序。 2.功能 离线保存笔记 云端数据同步, 更换了设备也可以找到以前的笔记 接入了好推二维码提供的数据统计工具, 可以到平台上查看用户分析、留存分析、事件分析。 3.界面效果 ***HotApp云笔…

Redis Pipelining 底层原理分析及实践

作者&#xff1a;vivo 互联网服务器团队-Wang Fei Redis是一种基于客户端-服务端模型以及请求/响应的TCP服务。在遇到批处理命令执行时&#xff0c;Redis提供了Pipelining(管道)来提升批处理性能。本文结合实践分析了Spring Boot框架下Redis的Lettuce客户端和Redisson客户端对…

行云堡垒国密算法应用与信创支持

一、 国密算法和信创的介绍 1.1 什么是国密算法 国密算法是国家密码管理局制定颁布的一系列的密码标准&#xff0c;即已经被国家密码局认定的国产密码算法&#xff0c;又称商用密码&#xff08;是指能够实现商用密码算法的加密&#xff0c;解密和认证等功能的技术&#xff09;…

excel里如何的科学计数法的数字转换成数值?

比如下图&#xff0c;要想把它们转换成3250跟1780&#xff0c;有什么快捷的办法吗&#xff1f; 科学计数法在excel里的格式&#xff0c;与我们常规在数学上写的有差异。这个转换可以这样做&#xff1a; 1.转换后的效果&#xff1a; 2.问题分析 题目中所附截图&#xff0c;单元…

Day96:云上攻防-云原生篇Docker安全系统内核版本漏洞CDK自动利用容器逃逸

目录 云原生-Docker安全-容器逃逸&系统内核漏洞 云原生-Docker安全-容器逃逸&docker版本漏洞 CVE-2019-5736 runC容器逃逸(需要管理员配合触发) CVE-2020-15257 containerd逃逸(启动容器时有前提参数) 云原生-Docker安全-容器逃逸&CDK自动化 知识点&#xff1…

Redis入门到通关之Set命令

文章目录 ⛄ 概述⛄ Set类型的常见命令⛄RedisTemplate API❄️❄️ 添加Set缓存(值可以是一个&#xff0c;也可是多个)❄️❄️设置过期时间(单独设置)❄️❄️根据key获取Set中的所有值❄️❄️根据value从一个set中查询,是否存在❄️❄️获取Set缓存的长度❄️❄️移除指定的…

云服务器安装Mysql、MariaDB、Redis、tomcat

前置工作 进入根目录 cd / 进入/user/local文件夹 上传压缩包 rz 压缩包 Mysql 1.下载并安装MySQL官方的 Yum Repository wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm rpm -ivh mysql-community-release-el7-5.noarch.rpm yum install mysql-…

博途v17与winCC安装流程与踩坑记录

博途v17与winCC7.5安装教程与踩坑记录 安装教程博途安装流程第一步 注册表删除第二步 启用.NET Framework服务第三步 正式安装 winCC安装流程 博途v17与winCC安装记录win11家庭版 安装教程 首先说明&#xff0c;本文安装针对的是TIA portal V17 (64bit)与winCC 7.5SP2。安装在…

基于ollama搭建本地chatGPT

ollama帮助我们可以快速在本地运行一个大模型&#xff0c;再整合一个可视化页面就能构建一个chatGPT&#xff0c;可视化页面我选择了chat-ollama&#xff08;因为它还能支持知识库&#xff0c;可玩性更高&#xff09;&#xff0c;如果只是为了聊天更推荐chatbox 部署步骤 下载…

科研学习|科研软件——如何使用SmartPLS软件进行结构方程建模

SmartPLS是一种用于结构方程建模&#xff08;SEM&#xff09;的软件&#xff0c;它可以用于定量研究&#xff0c;尤其是在商业和社会科学领域中&#xff0c;如市场研究、管理研究、心理学研究等。 一、准备数据 在使用SmartPLS之前&#xff0c;您需要准备一个符合要求的数据集。…

1.MMD模型动作场景镜头的导入及视频导出

界面介绍 MIKUMIKUDANCE926版本 MMD的工具栏模型骨骼帧的窗口&#xff0c;在不同时间做不同动作&#xff0c;可以在这里打帧操作时间曲线操作窗口&#xff0c;控制模型两个动作之间的过渡模型操作窗口&#xff0c;导入模型选择模型相机操作&#xff0c;控制相机远近&#xf…

详解拷贝构造

拷贝构造的功能 写法&#xff1a; 拷贝构造函数的参数为什么是引用类型 系统自动生成的拷贝构造函数 拷贝构造的深拷贝与浅拷贝 概念 浅拷贝&#xff1a; 深拷贝 小结 拷贝构造的功能 拷贝构造函数可以把曾经实例化好的对象的数据拷贝给新创建的数据 &#xff0c;可见…

计算机网络常问面试题

一.HTTPS是如何保证安全传输的 https通过使⽤对称加密、⾮对称加密、数字证书等⽅式来保证数据的安全传输。 客户端向服务端发送数据之前&#xff0c;需要先建⽴TCP连接&#xff0c;所以需要先建⽴TCP连接&#xff0c;建⽴完TCP连接后&#xff0c;服务端会先给客户端发送公钥…

LabVIEW直流稳定电源自动化校准系统

LabVIEW直流稳定电源自动化校准系统 直流稳定电源正向着智能化、高精度、多通道、宽量程的方向发展。基于LabVIEW开发环境&#xff0c;设计并实现了一种直流稳定电源自动化校准系统&#xff0c;以提升校准过程的整体效能&#xff0c;实现自动化设备替代人工进行电源校准工作。…

MYSQL执行过程和顺序详解

一、前言 1.1、说明 就MySQL在执行过程、sql执行顺序&#xff0c;以及一些相关关键字的注意点方面的学习分享内容。 在参考文章的基础上&#xff0c;会增加自己的理解、看法&#xff0c;希望本文章能够在您的学习中提供帮助。 如有错误的地方&#xff0c;欢迎指出纠错&…