微网站作用宁波seo搜索引擎优化公司
前言
此篇博客写的小项目与之前写的一篇《QTableWidget》类似,只是使用了不同的方法进行实现。但是推荐使用此篇播放的方法;因为高效,低耗能。
当然,项目中还涉及到使用委托添加控件,即在单元格中添加控件,使其双击后才显示出来。 具体操作请看此篇博客:使用委托QItemDelegate 于 QTableView单元格添加控件。
QAbstractTableModel的父类QABstractItemModel,他从父类中继承下来了大量方法,我们需要使用该类的话,也是需要继承与他(QAbstractTableModel),然后进行重写其里面的方法。
项目实现效果:
总体来说,继承于QAbstractTableModel实现起来还是蛮容易的,重写方法,根据自己的项目需求进行编写,对于数据的管理也是挺友好的。
相比于QTableWidget,QAbstractTableModel使用的内存更少,数据管理更方便。
定义
需要包含头文件:#include <QAbstractTableModel>
-
继承自QAbstractTableModel
class TableModel : public QAbstractTableModel { public:// ...private:// 16列,对应16个链表存储数据QStringList m_No;QStringList m_Reference;QStringList m_X;QStringList m_Y;QStringList m_Z;QStringList m_R;QStringList m_Part;QStringList m_Feeder;QStringList m_Nozzle;QStringList m_HD;QMap<int, Qt::CheckState> m_CS;QStringList m_CY;QMap<int, Qt::CheckState> m_SK;QStringList m_AR;QMap<int, Qt::CheckState> m_FID;QStringList m_BL;// 存储水平方向头数据QStringList m_HorizontalHead; }
这里我们需求是有16列,所以得定义16个数据变量来存储。
-
重写以下方法
// 返回行个数 virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; // 返回列个数 virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; // 返回头文本 virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; // 返回索引数据 virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; // 返回模型索引 virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; // 返回模型索引的编辑方式 virtual Qt::ItemFlags flags(const QModelIndex &index) const override; // 设置模型索引数据 virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; // 插入行 virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; // 删除行 virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
-
实现方法体
(1). rowCount
此方法的作用:见名之意,此方法是返回行的个数。// 返回行个数 int TableModel::rowCount(const QModelIndex &parent) const {// 某一列的数据个数即是行数return this->m_No.size(); }
(2). columnCount
此方法的作用:见名之意,此方法是返回列的个数。// 返回列个数 int TableModel::columnCount(const QModelIndex &parent) const { // 项目代码中,我们再头文件里写了一个对于列表头索引的枚举,所以这里直接使用枚举的个数来进行返回。return COLUMN_HEAD_INDEX::COLUMN; }
(3). headerData
此方法的作用:设置表头文本,并返回文本内容// 返回头文本 QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const {if (role != Qt::DisplayRole) // 返回一个默认数据的表头return QAbstractTableModel::headerData(section, orientation, role);// 仅仅只是返回水平方向的表头if (orientation == Qt::Orientation::Horizontal) {return this->m_HorizontalHead[section];}return QAbstractTableModel::headerData(section, orientation, role); }
根据参数role角色判定,再进行下一步操作。
(4). data
此方法的作用:返回索引的数据。// 返回索引数据 QVariant TableModel::data(const QModelIndex &index, int role) const {// 不存在则返回默认if (!index.isValid()) return QVariant();// 如果角色为显示和编辑模式if (role == Qt::DisplayRole || role == Qt::EditRole) {// 此处省略一万行代码...// 否则如果角色为选中模式(即多选框)} else if (role == Qt::CheckStateRole) {// 此处省略一万行代码...}return QVariant(); }
省略处的代码将在下方进行展示。每个参数都有其特殊意义。
(5). index
此方法的作用:返回行和列对应单元格的索引。// 返回模型索引 QModelIndex TableModel::index(int row, int column, const QModelIndex &parent) const {// 行和列的合法性检查if (row < 0 || row >= this->m_No.size() || column < 0 || column >= COLUMN_HEAD_INDEX::COLUMN) {return QModelIndex();}// 此处省略一万行代码...// 新建一个索引并绑定指针数据返回(指针数据可有可无)// return createIndex(row, column);return createIndex(row, column); }
省略处的代码将在下方进行展示。根据行和列新建一个索引并返回。
(6). flags
此方法的作用:根据索引,返回其的编辑模式。// 返回模型索引的编辑方式 Qt::ItemFlags TableModel::flags(const QModelIndex &index) const {if (!index.isValid()) {// 返回用户可以与界面进行交互return Qt::ItemIsEnabled | Qt::ItemIsSelectable;}// 此处省略一万行代码... // 返回用户可以进行编辑// return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;return Qt::ItemIsEnabled | Qt::ItemIsSelectable; }
省略处的代码将在下方进行展示。根据索引的列返回编辑方式。
(7). setData
此方法的作用:将参数value索引存入对应列的数据变量中。// 设置模型索引数据 bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role) {if (index.isValid() && role == Qt::EditRole) {// 此处省略一万行代码...// 设置完成后发射信号告诉视图数据有修改emit dataChanged(index, index); // 参数是左上角和右下角的模型索引(这里是相同的)return true;} else if (index.isValid() && role == Qt::CheckStateRole) {// 此处省略一万行代码...// 设置完成后发射信号告诉视图数据有修改emit dataChanged(index, index); // 参数是左上角和右下角的模型索引(这里是相同的)return true;}return false; }
省略处的代码将在下方进行展示。存储完后,得发射信号,告诉视图数据有修改。
(8). insertRows
此方法的作用:插入一行或多行数据。// 插入行 参数:插入的位置;插入的行数;父项的模型索引 bool TableModel::insertRows(int row, int count, const QModelIndex &parent) {// 如果插入零行,则返回false,表示插入失败if (count == 0) return false;// 没有父类if (!parent.isValid()) {// 从row开始插入行,直到row + count - 1处beginInsertRows(QModelIndex(), row, row + count - 1);// 有父类} else {// 从row开始插入行,直到row + count - 1处beginInsertRows(parent, row, row + count - 1);}// 按照位置在链表对应位置进行插入数据for (int addCount = 0; addCount < count; addCount++) {this->m_No.insert(row + addCount, "");this->m_Reference.insert(row + addCount, "");this->m_X.insert(row + addCount, "");this->m_Y.insert(row + addCount, "");this->m_Z.insert(row + addCount, "");this->m_R.insert(row + addCount, "");this->m_Part.insert(row + addCount, "");this->m_Feeder.insert(row + addCount, "");this->m_Nozzle.insert(row + addCount, "");this->m_HD.insert(row + addCount, "");this->m_CS.insert(row + addCount, Qt::CheckState::Unchecked);this->m_CY.insert(row + addCount, "");this->m_SK.insert(row + addCount, Qt::CheckState::Unchecked);this->m_AR.insert(row + addCount, "");this->m_FID.insert(row + addCount, Qt::CheckState::Unchecked);this->m_BL.insert(row + addCount, "");}// 结束插入行endInsertRows();return true; }
使用beginInsertRows()开始插入,endInsertRows()结束插入。
插入后,存储数据的链表也得对应插入空数据,或者插入默认数据;这才能保证数据的同步性。(9). removeRows
此方法的作用:删除一行或者多行数据。// 删除行 bool TableModel::removeRows(int row, int count, const QModelIndex &parent) {if (count == 0) return false;if (!parent.isValid()) {beginRemoveRows(QModelIndex(), row, row + count - 1);} else {beginInsertRows(parent, row, row + count - 1);}// 按照位置在链表对应位置进行移除数据for (int removeCount = 0; removeCount < count; removeCount++) {this->m_No.removeAt(row);this->m_Reference.removeAt(row);this->m_X.removeAt(row);this->m_Y.removeAt(row);this->m_Z.removeAt(row);this->m_R.removeAt(row);this->m_Part.removeAt(row);this->m_Feeder.removeAt(row);this->m_Nozzle.removeAt(row);this->m_HD.removeAt(row);this->m_CS.remove(row + removeCount);this->m_CY.removeAt(row);this->m_SK.remove(row + removeCount);this->m_AR.removeAt(row);this->m_FID.remove(row + removeCount);this->m_BL.removeAt(row);}endRemoveRows();return true; }
使用beginRemoveRows()开始删除,endRemoveRows()结束删除。
删除后,存储数据的链表也得删除对应的数据;这才能保证数据的同步性。
好了,到了这里,说明前期准备工作已经做得差不多了,接下来就可以去使用这个类了。
使用
另外新建类HzcTableEdit
ui文件:
再构造函数中new对象将其与TableView进行绑定即可。
TableModel *m_tableModel = new TableModel;
ui.tableView->setModel(this->m_tableModel);
至于插入、删除等功能,实现按钮的槽方法,再使用m_tableModel对象调用插入和删除的方法即可;具体实现按照具体项目需求即可。
QTableView 零碎知识点
设置水平头文本居中
ui.tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignHCenter);
设置隔行变色
ui.tableView->setAlternatingRowColors(true);
设置最后一列填满表格剩余空间
ui.tableView->horizontalHeader()->setStretchLastSection(true);
设置表格行高
ui.tableView->verticalHeader()->setDefaultSectionSize(30);
设置表格列宽
ui.tableView->setColumnWidth(column, 40);
设置用户可以拖动行
ui.tableView->verticalHeader()->setSectionsMovable(true);
设置固定行高
ui.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
设置表头样式表颜色
// 设置行表头背景颜色样式为浅黄色
ui.tableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:#ffff9b;}");
// 设置列表头背景颜色样式为浅蓝色
ui.tableView->verticalHeader()->setStyleSheet("QHeaderView::section{background:#a7fffa;}");
// 设置左上角两个表头相交的区域的颜色样式为浅灰色
ui.tableView->setStyleSheet("QTableCornerButton::section{background:#c2c2c2;}");
全部实现代码
TableModel.h
#pragma once
#include <QAbstractTableModel>#pragma execution_character_set("utf-8") // qt支持显示中文enum COLUMN_HEAD_INDEX {No = 0,Reference = 1,X = 2,Y = 3,Z = 4,R = 5,Part = 6,Feeder = 7,Nozzle = 8,HD = 9,CS = 10,CY = 11,SK = 12,AR = 13,FID = 14,BL = 15,COLUMN = 16
};class TableModel : public QAbstractTableModel {public:TableModel(QAbstractTableModel *parent = nullptr);~TableModel();// 返回行个数virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;// 返回列个数virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;// 返回头文本virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;// 返回索引数据virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;// 返回模型索引virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;// 返回模型索引的编辑方式virtual Qt::ItemFlags flags(const QModelIndex &index) const override;// 设置模型索引数据virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;// 插入行virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;// 删除行virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;private:// 16列,对应16个链表存储数据QStringList m_No;QStringList m_Reference;QStringList m_X;QStringList m_Y;QStringList m_Z;QStringList m_R;QStringList m_Part;QStringList m_Feeder;QStringList m_Nozzle;QStringList m_HD;QMap<int, Qt::CheckState> m_CS;QStringList m_CY;QMap<int, Qt::CheckState> m_SK;QStringList m_AR;QMap<int, Qt::CheckState> m_FID;QStringList m_BL;// 存储水平方向头数据QStringList m_HorizontalHead;
};
TableModel.cpp
#include "TableModel.h"TableModel::TableModel(QAbstractTableModel *parent) : QAbstractTableModel(parent) {this->m_HorizontalHead.append("No.");this->m_HorizontalHead.append("Reference");this->m_HorizontalHead.append("X");this->m_HorizontalHead.append("Y");this->m_HorizontalHead.append("Z");this->m_HorizontalHead.append("R");this->m_HorizontalHead.append("Part");this->m_HorizontalHead.append("Feeder");this->m_HorizontalHead.append("Nozzle");this->m_HorizontalHead.append("HD");this->m_HorizontalHead.append("CS");this->m_HorizontalHead.append("CY");this->m_HorizontalHead.append("SK");this->m_HorizontalHead.append("AR");this->m_HorizontalHead.append("FID");this->m_HorizontalHead.append("BL");
}TableModel::~TableModel() {}// 返回行个数
int TableModel::rowCount(const QModelIndex &parent) const {return this->m_No.size();
}// 返回列个数
int TableModel::columnCount(const QModelIndex &parent) const {return COLUMN_HEAD_INDEX::COLUMN;
}// 返回头文本
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const {if (role != Qt::DisplayRole) // 返回一个默认数据的表头return QAbstractTableModel::headerData(section, orientation, role);if (orientation == Qt::Orientation::Horizontal) {return this->m_HorizontalHead[section];}return QAbstractTableModel::headerData(section, orientation, role);
}// 返回索引数据
QVariant TableModel::data(const QModelIndex &index, int role) const {// 不存在则返回默认if (!index.isValid()) return QVariant();if (role == Qt::DisplayRole || role == Qt::EditRole) {switch (index.column()) {case COLUMN_HEAD_INDEX::No:return this->m_No[index.row()];break;case COLUMN_HEAD_INDEX::Reference:return this->m_Reference[index.row()];break;case COLUMN_HEAD_INDEX::X:return this->m_X[index.row()];break;case COLUMN_HEAD_INDEX::Y:return this->m_Y[index.row()];break;case COLUMN_HEAD_INDEX::Z:return this->m_Z[index.row()];break;case COLUMN_HEAD_INDEX::R:return this->m_R[index.row()];break;case COLUMN_HEAD_INDEX::Part:return this->m_Part[index.row()];break;case COLUMN_HEAD_INDEX::Feeder:return this->m_Feeder[index.row()];break;case COLUMN_HEAD_INDEX::Nozzle:return this->m_Nozzle[index.row()];break;case COLUMN_HEAD_INDEX::HD:return this->m_HD[index.row()];break;case COLUMN_HEAD_INDEX::CY:return this->m_CY[index.row()];break;case COLUMN_HEAD_INDEX::AR:return this->m_AR[index.row()];break;case COLUMN_HEAD_INDEX::BL:return this->m_BL[index.row()];default:return QVariant();break;}} else if (role == Qt::CheckStateRole) {switch (index.column()) {case COLUMN_HEAD_INDEX::CS:return this->m_CS[index.row()] == Qt::Checked ? Qt::Checked : Qt::Unchecked;break;case COLUMN_HEAD_INDEX::SK:return this->m_SK[index.row()] == Qt::Checked ? Qt::Checked : Qt::Unchecked;break;case COLUMN_HEAD_INDEX::FID:return this->m_FID[index.row()] == Qt::Checked ? Qt::Checked : Qt::Unchecked;break;default:return QVariant();break;}}return QVariant();
}// 返回模型索引
QModelIndex TableModel::index(int row, int column, const QModelIndex &parent) const {// 行和列的合法性检查if (row < 0 || row >= this->m_No.size() || column < 0 || column >= COLUMN_HEAD_INDEX::COLUMN) {return QModelIndex();}switch (column) {case COLUMN_HEAD_INDEX::No:// 获取对应行和列单元格的数据//QString str = this->m_No[row];// 新建一个索引并绑定指针数据返回return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::Reference://QString str = this->m_Reference[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::X://QString str = this->m_X[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::Y://QString str = this->m_Y[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::Z://QString str = this->m_Z[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::R://QString str = this->m_R[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::Part://QString str = this->m_Part[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::Feeder://QString str = this->m_Feeder[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::Nozzle://QString str = this->m_Nozzle[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::HD://QString str = this->m_HD[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::CS://QString str = this->m_CS[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::CY://QString str = this->m_CY[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::SK://QString str = this->m_SK[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::AR://QString str = this->m_AR[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::FID://QString str = this->m_FID[row];return createIndex(row, column/*, &str*/);break;case COLUMN_HEAD_INDEX::BL://QString str = this->m_BL[row];return createIndex(row, column/*, &str*/);break;default:return createIndex(row, column);break;}return createIndex(row, column);
}// 返回模型索引的编辑方式
Qt::ItemFlags TableModel::flags(const QModelIndex &index) const {if (!index.isValid()) {// 返回用户可以与界面进行交互return Qt::ItemIsEnabled | Qt::ItemIsSelectable;}switch (index.column()) {case COLUMN_HEAD_INDEX::No:// 返回用户可以与界面进行交互return Qt::ItemIsEnabled | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::Reference:// 返回用户可以进行编辑return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::X:return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::Y:return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::Z:return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::R:return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::Part:return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::Feeder:return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::Nozzle:return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::HD:return QAbstractItemModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::CS:return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;break;case COLUMN_HEAD_INDEX::CY:return Qt::ItemIsEnabled | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::SK:return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;break;case COLUMN_HEAD_INDEX::AR:return Qt::ItemIsEnabled | Qt::ItemIsSelectable;break;case COLUMN_HEAD_INDEX::FID:return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;break;case COLUMN_HEAD_INDEX::BL:return Qt::ItemIsEnabled | Qt::ItemIsSelectable;default:return Qt::ItemIsEnabled | Qt::ItemIsSelectable;break;}return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}// 设置模型索引数据
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role) {if (index.isValid() && role == Qt::EditRole) {switch (index.column()) {case COLUMN_HEAD_INDEX::No:this->m_No[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::Reference:this->m_Reference[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::X:this->m_X[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::Y:this->m_Y[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::Z:this->m_Z[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::R:this->m_R[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::Part:this->m_Part[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::Feeder:this->m_Feeder[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::Nozzle:this->m_Nozzle[index.row()] = value.toString();break;case COLUMN_HEAD_INDEX::HD:this->m_HD[index.row()] = value.toString();break;//case COLUMN_HEAD_INDEX::CS:// //this->m_CS[index.row()] = value.toString();// this->m_CS[index.row()] = (value.toInt() == Qt::Checked ? Qt::Checked : Qt::Unchecked);// break;case COLUMN_HEAD_INDEX::CY:this->m_CY[index.row()] = value.toString();break;/*case COLUMN_HEAD_INDEX::SK:this->m_SK[index.row()] = value.toString();break;*/case COLUMN_HEAD_INDEX::AR:this->m_AR[index.row()] = value.toString();break;/*case COLUMN_HEAD_INDEX::FID:this->m_FID[index.row()] = value.toString();break;*/case COLUMN_HEAD_INDEX::BL:this->m_BL[index.row()] = value.toString();default:break;}// 设置完成后发射信号告诉视图数据有修改emit dataChanged(index, index); // 参数是左上角和右下角的模型索引(这里是相同的)return true;} else if (index.isValid() && role == Qt::CheckStateRole) {switch (index.column()) {case COLUMN_HEAD_INDEX::CS:this->m_CS[index.row()] = (value.toInt() == Qt::Checked ? Qt::Checked : Qt::Unchecked);break;case COLUMN_HEAD_INDEX::SK:this->m_SK[index.row()] = (value.toInt() == Qt::Checked ? Qt::Checked : Qt::Unchecked);break;case COLUMN_HEAD_INDEX::FID:this->m_FID[index.row()] = (value.toInt() == Qt::Checked ? Qt::Checked : Qt::Unchecked);break;default:break;}// 设置完成后发射信号告诉视图数据有修改emit dataChanged(index, index); // 参数是左上角和右下角的模型索引(这里是相同的)return true;}return false;
}// 插入行 参数:插入的位置;插入的行数;父项的模型索引
bool TableModel::insertRows(int row, int count, const QModelIndex &parent) {// 如果插入零行,则返回false,表示插入失败if (count == 0) return false;// 没有父类if (!parent.isValid()) {// 从row开始插入行,直到row + count - 1处beginInsertRows(QModelIndex(), row, row + count - 1);// 有父类} else {// 从row开始插入行,直到row + count - 1处beginInsertRows(parent, row, row + count - 1);}// 按照位置在链表对应位置进行插入数据for (int addCount = 0; addCount < count; addCount++) {this->m_No.insert(row + addCount, "");this->m_Reference.insert(row + addCount, "");this->m_X.insert(row + addCount, "");this->m_Y.insert(row + addCount, "");this->m_Z.insert(row + addCount, "");this->m_R.insert(row + addCount, "");this->m_Part.insert(row + addCount, "");this->m_Feeder.insert(row + addCount, "");this->m_Nozzle.insert(row + addCount, "");this->m_HD.insert(row + addCount, "");this->m_CS.insert(row + addCount, Qt::CheckState::Unchecked);this->m_CY.insert(row + addCount, "");this->m_SK.insert(row + addCount, Qt::CheckState::Unchecked);this->m_AR.insert(row + addCount, "");this->m_FID.insert(row + addCount, Qt::CheckState::Unchecked);this->m_BL.insert(row + addCount, "");}// 结束插入行endInsertRows();return true;
}// 删除行
bool TableModel::removeRows(int row, int count, const QModelIndex &parent) {if (count == 0) return false;if (!parent.isValid()) {beginRemoveRows(QModelIndex(), row, row + count - 1);} else {beginInsertRows(parent, row, row + count - 1);}// 按照位置在链表对应位置进行移除数据for (int removeCount = 0; removeCount < count; removeCount++) {this->m_No.removeAt(row);this->m_Reference.removeAt(row);this->m_X.removeAt(row);this->m_Y.removeAt(row);this->m_Z.removeAt(row);this->m_R.removeAt(row);this->m_Part.removeAt(row);this->m_Feeder.removeAt(row);this->m_Nozzle.removeAt(row);this->m_HD.removeAt(row);this->m_CS.remove(row + removeCount);this->m_CY.removeAt(row);this->m_SK.remove(row + removeCount);this->m_AR.removeAt(row);this->m_FID.remove(row + removeCount);this->m_BL.removeAt(row);}endRemoveRows();return true;
}
==
==
HzcTableEdit.h
#pragma once#include <QtWidgets/QWidget>
#include "ui_HzcTableEdit.h"
#include "TableModel.h"
#include "WidgetDelegate.h"#include <QMenu>class HzcTableEdit : public QWidget {Q_OBJECTpublic:HzcTableEdit(QWidget *parent = Q_NULLPTR);private:void setInsertRowData(int _row);bool isSelectionRows();// 右键显示菜单事件void contextMenuEvent(QContextMenuEvent * event);private slots:void on_selectBtn_clicked();void on_cancelBtn_clicked();void on_insertBtn_clicked();void on_removeBtn_clicked();void onCheckBox();void onUnCheckBox();void onCheckAndUnCheckBox(Qt::CheckState State);private:Ui::HzcTableEditClass ui;TableModel *m_tableModel;// QComboBoxQMap<int, WidgetDelegate *> m_cBoxDelegateArray;// 列的初始默认数据QMap<int, QString> m_columnData;// No.列int m_no;// 存储查找显示的索引QModelIndexList m_showList;// 菜单QMenu *m_menu;};
HzcTableEdit.cpp
#include "HzcTableEdit.h"#include <QDebug>
#include <QMessageBox>HzcTableEdit::HzcTableEdit(QWidget *parent) : QWidget(parent) {ui.setupUi(this);this->m_no = 1;// 单元格的宽度int headWidth[COLUMN_HEAD_INDEX::COLUMN] = { 40, 100, 80, 80, 80, 80, 100, 120, 100, 40, 30, 30, 30, 30, 30, 30 };QStringList part, feederList, nozzleList, HDList;part << "";feederList << "" << "A";nozzleList << "" << "A" << "EMPTY" << "CN020" << "CN030" << "CN040" << "CN065" << "CN140" << "CN220" << "CN400";HDList << "" << "1" << "2" << "3" << "4" << "5" << "6" << "7";// 行的初始数据this->m_columnData.insert(COLUMN_HEAD_INDEX::X, "0.000");this->m_columnData.insert(COLUMN_HEAD_INDEX::Y, "0.000");this->m_columnData.insert(COLUMN_HEAD_INDEX::Z, "0.000");this->m_columnData.insert(COLUMN_HEAD_INDEX::R, "0.000");this->m_columnData.insert(COLUMN_HEAD_INDEX::CY, "1");this->m_columnData.insert(COLUMN_HEAD_INDEX::AR, "1");this->m_columnData.insert(COLUMN_HEAD_INDEX::BL, "0");this->m_tableModel = new TableModel;ui.tableView->setModel(this->m_tableModel);this->m_cBoxDelegateArray.insert(COLUMN_HEAD_INDEX::Part, new WidgetDelegate(part));this->m_cBoxDelegateArray.insert(COLUMN_HEAD_INDEX::Feeder, new WidgetDelegate(feederList));this->m_cBoxDelegateArray.insert(COLUMN_HEAD_INDEX::Nozzle, new WidgetDelegate(nozzleList));this->m_cBoxDelegateArray.insert(COLUMN_HEAD_INDEX::HD, new WidgetDelegate(HDList));// 添加委托ui.tableView->setItemDelegateForColumn(COLUMN_HEAD_INDEX::Part, this->m_cBoxDelegateArray.value(COLUMN_HEAD_INDEX::Part));ui.tableView->setItemDelegateForColumn(COLUMN_HEAD_INDEX::Feeder, this->m_cBoxDelegateArray.value(COLUMN_HEAD_INDEX::Feeder));ui.tableView->setItemDelegateForColumn(COLUMN_HEAD_INDEX::Nozzle, this->m_cBoxDelegateArray.value(COLUMN_HEAD_INDEX::Nozzle));ui.tableView->setItemDelegateForColumn(COLUMN_HEAD_INDEX::HD, this->m_cBoxDelegateArray.value(COLUMN_HEAD_INDEX::HD));// 设置水平头文本居中ui.tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignHCenter);//设置隔行变色ui.tableView->setAlternatingRowColors(true);// 设置最后一列填满表格剩余空间ui.tableView->horizontalHeader()->setStretchLastSection(true);// 设置表格行高ui.tableView->verticalHeader()->setDefaultSectionSize(30);for (int i = 0; i < COLUMN_HEAD_INDEX::COLUMN; i++) {// 设置表格列宽ui.tableView->setColumnWidth(i, headWidth[i]);}// 设置用户可以拖动行//ui.tableView->verticalHeader()->setSectionsMovable(true);// 设置固定行高ui.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);//ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);// 设置行表头背景颜色样式为浅黄色ui.tableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:#ffff9b;}");// 设置列表头背景颜色样式为浅蓝色ui.tableView->verticalHeader()->setStyleSheet("QHeaderView::section{background:#a7fffa;}");// 设置左上角两个表头相交的区域的颜色样式为浅灰色ui.tableView->setStyleSheet("QTableCornerButton::section{background:#c2c2c2;}");this->m_menu = new QMenu(this);QMenu *childMenu = new QMenu("填充原件");// 定义菜单子项QAction *addRowAction = new QAction("插入行");QAction *removeRowAction = new QAction("删除行");QAction *skipPointsAction = new QAction("跳过点");QAction *cancleSkipPointsAction = new QAction("取消跳过点");QAction *skipJointBoardAction = new QAction("跳过拼版");QAction *separator = this->m_menu->addSeparator(); // 返回一个分隔符this->m_menu->addAction(addRowAction);this->m_menu->addAction(removeRowAction);this->m_menu->addAction(skipPointsAction);this->m_menu->addAction(cancleSkipPointsAction);this->m_menu->addAction(separator);this->m_menu->addAction(skipJointBoardAction);this->m_menu->addMenu(childMenu);connect(addRowAction, SIGNAL(triggered()), this, SLOT(on_insertBtn_clicked()));connect(removeRowAction, SIGNAL(triggered()), this, SLOT(on_removeBtn_clicked()));connect(skipPointsAction, SIGNAL(triggered()), this, SLOT(onCheckBox()));connect(cancleSkipPointsAction, SIGNAL(triggered()), this, SLOT(onUnCheckBox()));
}void HzcTableEdit::on_selectBtn_clicked() {// 清空链表this->m_showList.clear();QString lineEdit = ui.lineEdit->text();QString rowStr = "";// 为空则显示所有行if (lineEdit.isEmpty()) {for (int row = 0; row < m_tableModel->rowCount(); row++) {ui.tableView->showRow(row);} return;}// 遍历所有行for (int row = 0; row < m_tableModel->rowCount(); row++) {// 获得Reference单元格的文本QModelIndex index = m_tableModel->index(row, COLUMN_HEAD_INDEX::Reference);rowStr = m_tableModel->data(index).toString();if (rowStr.isEmpty()) {// 隐藏当前行ui.tableView->hideRow(row);} else {// Reference列的文本是否与输入框中的文本相等if (lineEdit.contains(rowStr)) {ui.tableView->showRow(row);// 将显示的行索引存入链表中this->m_showList.append(index);} else {ui.tableView->hideRow(row);}}}
}void HzcTableEdit::on_cancelBtn_clicked() {ui.lineEdit->setText("");// 显示所有行for (int row = 0; row < m_tableModel->rowCount(); row++) {ui.tableView->showRow(row);}// 点击了取消按钮,链表需要清空this->m_showList.clear();
}void HzcTableEdit::on_insertBtn_clicked() {if (!isSelectionRows()) {QMessageBox::information(this, "提示", "请选中一行!");return;}// 获取所有选中的索引QModelIndexList indexs = ui.tableView->selectionModel()->selectedIndexes();if (indexs.size() == 0) {m_tableModel->insertRows(m_tableModel->rowCount(), 1);setInsertRowData(m_tableModel->rowCount() - 1);return;}// 获得最后一个索引的行int row = indexs.last().row();m_tableModel->insertRows(row + 1, 1);setInsertRowData(row + 1);
}void HzcTableEdit::on_removeBtn_clicked() {if (!isSelectionRows()) {QMessageBox::information(this, "提示", "请选中一行!");return;}// 移除用户查询后选中的行if (this->m_showList.size() != 0) {// 从后往前移除for (int row = m_showList.size() - 1; row >= 0; row--) {m_tableModel->removeRow(m_showList[row].row());}m_showList.clear();return;}// 获取所有选中列的行(不重复)QModelIndexList indexs = ui.tableView->selectionModel()->selectedRows();if (indexs.size() == 0) return;// 检测用户选择的项是否是连续的bool successionFlag = true;for (int row = 0; row < indexs.size(); row++) {if (row + 1 == indexs.size()) break;if (indexs[row].row() + 1 != indexs[row + 1].row()) {successionFlag = false;break;}}if (successionFlag) {m_tableModel->removeRows(indexs.first().row(), indexs.size());} else {for (int row = indexs.size() - 1; row >= 0; row--) {m_tableModel->removeRow(indexs[row].row()); // 实际是调用removeRows(indexs[row].row(), 1),进行删除一行}}// 删除完后,对No列进行排序this->m_no = m_tableModel->rowCount();for (int row = 0; row < m_no; row++) {QModelIndex index = m_tableModel->index(row, 0);m_tableModel->setData(index, QString("%1").arg(row + 1));}this->m_no += 1;}// 为插入的行设置初始数据
void HzcTableEdit::setInsertRowData(int _row) {QString value = "";QModelIndex index0 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::No);m_tableModel->setData(index0, QString("%1").arg(m_no++));static int ref = 1;QModelIndex index1 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::Reference);m_tableModel->setData(index1, QString("%1").arg(ref++));QModelIndex index2 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::X);m_tableModel->setData(index2, m_columnData.value(COLUMN_HEAD_INDEX::X));QModelIndex index3 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::Y);m_tableModel->setData(index3, m_columnData.value(COLUMN_HEAD_INDEX::Y));QModelIndex index4 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::Z);m_tableModel->setData(index4, m_columnData.value(COLUMN_HEAD_INDEX::Z));QModelIndex index5 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::R);m_tableModel->setData(index5, m_columnData.value(COLUMN_HEAD_INDEX::R));QModelIndex index6 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::Part);value = this->m_cBoxDelegateArray[COLUMN_HEAD_INDEX::Part]->getCurrentComboBoxData(0);m_tableModel->setData(index6, value);QModelIndex index7 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::Feeder);value = this->m_cBoxDelegateArray[COLUMN_HEAD_INDEX::Feeder]->getCurrentComboBoxData(1);m_tableModel->setData(index7, value);QModelIndex index8 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::Nozzle);value = this->m_cBoxDelegateArray[COLUMN_HEAD_INDEX::Nozzle]->getCurrentComboBoxData(6);m_tableModel->setData(index8, value);QModelIndex index9 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::HD);value = this->m_cBoxDelegateArray[COLUMN_HEAD_INDEX::HD]->getCurrentComboBoxData(1);m_tableModel->setData(index9, value);QModelIndex index10 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::CS);m_tableModel->setData(index10, Qt::CheckState::Unchecked);QModelIndex index11 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::CY);m_tableModel->setData(index11, m_columnData.value(COLUMN_HEAD_INDEX::CY));QModelIndex index12 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::SK);m_tableModel->setData(index12, Qt::CheckState::Unchecked);QModelIndex index13 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::AR);m_tableModel->setData(index13, m_columnData.value(COLUMN_HEAD_INDEX::AR));QModelIndex index14 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::FID);m_tableModel->setData(index14, Qt::CheckState::Unchecked);QModelIndex index15 = m_tableModel->index(_row, COLUMN_HEAD_INDEX::BL);m_tableModel->setData(index15, m_columnData.value(COLUMN_HEAD_INDEX::BL));
}// 检查是否选中一整行
bool HzcTableEdit::isSelectionRows() {// 获取所有选中的索引QModelIndexList list = ui.tableView->selectionModel()->selectedIndexes();// 存储被选中的所有行QVector<int> selectedRows;// 获得所有选中单元格的行for each (QModelIndex index in list) {selectedRows.append(index.row());}// 进行排序std::sort(selectedRows.begin(), selectedRows.end());// 去除容器内重复元素auto it = std::unique(selectedRows.begin(), selectedRows.end());selectedRows.erase(it, selectedRows.end());// 遍历所有行的单元格,存在有一个单元格没有被选中,则返回falsebool selectedFlag = true;for (int row = 0; row < selectedRows.count(); row++) {for (int column = 0; column < COLUMN_HEAD_INDEX::COLUMN; column++) {QModelIndex index = m_tableModel->index(selectedRows.at(row), column);// 检测该索引是否被选中if (ui.tableView->selectionModel()->isSelected(index) == false) {selectedFlag = false;break;}}// 为false,直接返回结果if (selectedFlag == false) return selectedFlag;}/* 此处为良好BUG :当用于没有选中任何单元格,也会返回true,是为了默认在行末尾连续插入行 */return selectedFlag;
}// 鼠标右键显示菜单事件
void HzcTableEdit::contextMenuEvent(QContextMenuEvent * event) {if (!isSelectionRows()) {return;}// 菜单出现的位置为当前鼠标的位置this->m_menu->exec(QCursor::pos());
}// 跳过点
void HzcTableEdit::onCheckBox() {onCheckAndUnCheckBox(Qt::CheckState::Checked);
}// 取消跳过点
void HzcTableEdit::onUnCheckBox() {onCheckAndUnCheckBox(Qt::CheckState::Unchecked);
}void HzcTableEdit::onCheckAndUnCheckBox(Qt::CheckState State) {// 获取所有选中的索引QModelIndexList list = ui.tableView->selectionModel()->selectedIndexes();if (list.size() == 0) return;// 存储被选中的所有行QVector<int> selectedRows;// 获得所有选中单元格的行for each (QModelIndex index in list) {selectedRows.append(index.row());}// 进行排序std::sort(selectedRows.begin(), selectedRows.end());// 去除容器内重复元素auto it = std::unique(selectedRows.begin(), selectedRows.end());selectedRows.erase(it, selectedRows.end());for each (int row in selectedRows) {// 获取到对应的索引QModelIndex index = m_tableModel->index(row, COLUMN_HEAD_INDEX::SK);// 将设置索引数据为选中状态m_tableModel->setData(index, State, Qt::CheckStateRole);qDebug() << m_tableModel->data(index, Qt::CheckStateRole).toBool();}
}
如果想拷贝代码下来运行,还得将文章开头链接委托里的代码也一并考下来,方可运行。
总结:
此篇博客也当做是代码记录吧。项目具体实现有兴趣的朋友得自己去看代码了,代码量太多,实在是不知道怎么写。
小项目结合了重写了委托QItemDelegate、模型QAbstractTableModel类,还有QTableVIew的使用方法,三者结合才有此小项目。如果你也有这方面的需求,真的可以研究一下此小项目的代码。