Qt QAbstractTableModel 與 QTableView 結合使用,添加復選框,下拉菜單,不同控件


Qt QAbstractTableModel 與 QTableView 結合使用

前言

QAbstractTableModel的父類QABstractItemModel,他從父類中繼承下來了大量方法,我們需要使用該類的話,也是需要繼承與他(QAbstractTableModel),然后進行重寫其里面的方法。

項目實現效果:

image

image

總體來說,繼承於QAbstractTableModel實現起來還是蠻容易的,重寫方法,根據自己的項目需求進行編寫,對於數據的管理也是挺友好的。
相比於QTableWidget,QAbstractTableModel使用的內存更少,數據管理更方便。

定義

需要包含頭文件: #include <QAbstractTableModel>

  1. 繼承自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個數據變量來存儲。

  2. 重寫以下方法

    // 返回行個數
    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;
    
    
  3. 實現方法體

    (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文件:

image

再構造函數中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的使用方法,三者結合才有此小項目。如果你也有這方面的需求,真的可以研究一下此小項目的代碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM