Qt QAbstractTableModel 与 QTableView 结合使用,添加复选框,下拉菜单,不同控件
Qt QAbstractTableModel 与 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); } (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_OBJECT public: 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; // QComboBox QMap<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()); // 遍历所有行的单元格,存在有一个单元格没有被选中,则返回false bool 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的使用方法,三者结合才有此小项目。如果你也有这方面的需求,真的可以研究一下此小项目的代码。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!