Qt操作Excel表格

简单介绍:

文章内使用的是Windows系统的ActiveX对象(QAxObject)操作Excel。

相关参考:

微软vba开发指南:https://docs.microsoft.com/zh-cn/office/vba/api/overview/
其他人的文章:https://blog.csdn.net/fcqwin/article/details/17885557

主要操作接口

主要流程先是querySubObject() 获取某一个子对象,然后可以对其设置属性setProperty()或读取属性property(),进一步可以调用dynamicCall()来实现对其进行操作。

querySubObject:获取对象的某个属性,示例代码如下

QAxObject* range = sheet->querySubObject("Cells(int,int)", row, column);

dynamicCall:调用对象的某个方法,示例代码如下

range->dynamicCall("SetValue(const QString&)", value);

setProperty:设置对象的属性值,示例代码如下

range->setProperty("VerticalAlignment", -4108);//xlCenter

相关代码

class QTCREATOR_UTILS_EXPORT QExcel : public QObject
{
	Q_OBJECT
public:
	QExcel(QString name = "");
	~QExcel();

	void writeTitle(const QString& module, const QStringList& titles);
	void writeLineTest(int row, QVariantList pValues, UNI_UINT32 valCnt);
	QVariant readLine(int row, int column);

	QAxObject* getWorkBooks();
	QAxObject* getWorkBook();
	QAxObject* getWorkSheets();
	QAxObject* getWorkSheet();

	/**************************************************************************/
	/* 工作表                                                                 */
	/**************************************************************************/
	void selectSheet(const QString& sheetName);
	//sheetIndex 起始于 1  
	void selectSheet(int sheetIndex);
	void deleteSheet(const QString& sheetName);
	void deleteSheet(int sheetIndex);
	void insertSheet(QString sheetName);
	int getSheetsCount();
	//在 selectSheet() 之后才可调用  
	QString getSheetName();
	QString getSheetName(int sheetIndex);
	//datas是二维数组,datas的值类型是QList<QVariant>
	bool readSheet(QString sheetName, QVariantList& datas);
	bool writeSheet(QString sheetName, int startRow, int startCol, QVariantList& datas);
	bool writeSheet(QString sheetName, const int& startRow, const int& startCol, const int& endRow, const int& endCol, const QVariant& datas);

	/**************************************************************************/
	/* 单元格                                                                 */
	/**************************************************************************/
	void setCellString(int row, int column, const QString& value);
	//cell 例如 "A7"  
	void setCellString(const QString& cell, const QString& value);
	//range 例如 "A5:C7"  
	void mergeCells(const QString& range);
	void mergeCells(int topLeftRow, int topLeftColumn, int bottomRightRow, int bottomRightColumn);
	QVariant getCellValue(int row, int column);
	void clearCell(int row, int column);
	void clearCell(const QString& cell);
	void setCellDropItems(int row, int col, const QString& items);

	/**************************************************************************/
	/* 布局格式                                                               */
	/**************************************************************************/
	void getUsedRange(int* topLeftRow, int* topLeftColumn, int* bottomRightRow, int* bottomRightColumn);
	void setColumnWidth(int column, int width);
	void setRowHeight(int row, int height);
	void setCellTextCenter(int row, int column);
	void setCellTextCenter(const QString& cell);
	void setCellTextWrap(int row, int column, bool isWrap);
	void setCellTextWrap(const QString& cell, bool isWrap);
	void setAutoFitRow(int row);
	void mergeSerialSameCellsInAColumn(int column, int topRow);
	int getUsedRowsCount();
	void setCellFontBold(int row, int column, bool isBold);
	void setCellFontBold(const QString& cell, bool isBold);
	void setCellFontSize(int row, int column, int size);
	void setCellFontSize(const QString& cell, int size);
	//复制指令区域的内容到目标区域,带格式
	void copyRangeToRange(QString resource, QString target);

	/**************************************************************************/
	/* 文件                                                                   */
	/**************************************************************************/
	void save();
	void saveAs(const QString& filePath);
	void close();


	/**************************************************************************/
	/* 自定义辅助方法								                                     */
	/**************************************************************************/
	QString getRangeString(int startRow, int startCol, int endRow, int endCol);

	static bool isFileUsed(const QString& fpath);
private:
	void freeSheet();
	QString columnIntToString(int col);
private:
	QAxObject* excel;
	QAxObject* workBooks;
	QAxObject* workBook;
	QAxObject* sheets;
	QAxObject* sheet;
};
``

```cpp

QExcel::QExcel(QString name) :
	excel(nullptr),
	workBooks(nullptr),
	workBook(nullptr),
	sheets(nullptr),
	sheet(nullptr)
{
	CoInitializeEx(NULL, COINIT_MULTITHREADED);
	excel = new QAxObject(this);
	excel->setControl("Excel.Application");
	excel->dynamicCall("SetVisible (bool Visible)", "false");
	excel->setProperty("DisplayAlerts", false);
	workBooks = excel->querySubObject("Workbooks");	
	if(!name.isEmpty())
		workBooks->dynamicCall("Open(const QString&)", name);
	else
		workBooks->dynamicCall("Add");
	workBook = excel->querySubObject("ActiveWorkBook");
	sheets = workBook->querySubObject("WorkSheets");
}

QExcel::~QExcel()
{
	close();
}

void QExcel::writeTitle(const QString& module, const QStringList& titles)
{
	freeSheet();

	sheets->querySubObject("Add()");
	sheet = sheets->querySubObject("Item(int)", 1);
	sheet->setProperty("Name", module);

	for (int column =0; column < titles.size(); ++column)
	{
		QAxObject* cell = sheet->querySubObject("Cells(int,int)", 1, column + 1);
		cell->dynamicCall("SetValue(const QString&)", titles.at(column));
		delete cell;
		cell = nullptr;
	}
}

void QExcel::writeLineTest(int row, QVariantList pValues, UNI_UINT32 valCnt)
{
	for (UNI_UINT32 column = 0; column < valCnt; ++column)
	{
		QAxObject* cell = sheet->querySubObject("Cells(int,int)", row, column + 1);
		cell->dynamicCall("SetValue(const QString&)", pValues[column]);
		delete cell;
		cell = nullptr;
	}
}

QVariant QExcel::readLine(int row, int column)
{
	QVariant data;
	QAxObject* range = sheet->querySubObject("Cells(int,int)", row, column);
	//        return range->property("value");
	if (range)
	{
		data = range->dynamicCall("Value2()");
	}
	return data;
}

void QExcel::close()
{
	//关闭excel
	excel->dynamicCall("Quit()");

	delete sheet;
	delete sheets;
	delete workBook;
	delete workBooks;
	delete excel;

	excel = nullptr;
	workBooks = nullptr;
	workBook = nullptr;
	sheets = nullptr;
	sheet = nullptr;

	CoUninitialize();
}

QAxObject* QExcel::getWorkBooks()
{
	return workBooks;
}

QAxObject* QExcel::getWorkBook()
{
	return workBook;
}

QAxObject* QExcel::getWorkSheets()
{
	return sheets;
}

QAxObject* QExcel::getWorkSheet()
{
	return sheet;
}

void QExcel::selectSheet(const QString& sheetName)
{
	sheet = sheets->querySubObject("Item(const QString&)", sheetName);
}

void QExcel::deleteSheet(const QString& sheetName)
{
	QAxObject* a = sheets->querySubObject("Item(const QString&)", sheetName);
	a->dynamicCall("delete");
}

void QExcel::deleteSheet(int sheetIndex)
{
	QAxObject* a = sheets->querySubObject("Item(int)", sheetIndex);
	a->dynamicCall("delete");
}

void QExcel::selectSheet(int sheetIndex)
{
	sheet = sheets->querySubObject("Item(int)", sheetIndex);
}

void QExcel::setCellString(int row, int column, const QString& value)
{
	QAxObject* range = sheet->querySubObject("Cells(int,int)", row, column);
	range->dynamicCall("SetValue(const QString&)", value);
}

void QExcel::setCellFontBold(int row, int column, bool isBold)
{
	QString cell;
	cell.append(QChar(column - 1 + 'A'));
	cell.append(QString::number(row));

	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range = range->querySubObject("Font");
	range->setProperty("Bold", isBold);
}

void QExcel::setCellFontSize(int row, int column, int size)
{
	QString cell;
	cell.append(QChar(column - 1 + 'A'));
	cell.append(QString::number(row));

	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range = range->querySubObject("Font");
	range->setProperty("Size", size);
}

void QExcel::copyRangeToRange(QString resource, QString target)
{
	if (sheet)
	{
		QAxObject* resRange = sheet->querySubObject("Range(const QString&)", resource);
		resRange->dynamicCall("Copy");//复制指令区域内容到剪贴板
		
		QAxObject* tarRange = sheet->querySubObject("Range(const QString&)", target);//选中目标区域
		tarRange->dynamicCall("PasteSpecial");
	}
}

void QExcel::mergeCells(const QString& cell)
{
	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range->setProperty("VerticalAlignment", -4108);//xlCenter  
	range->setProperty("WrapText", true);
	range->setProperty("MergeCells", true);
}

void QExcel::mergeCells(int topLeftRow, int topLeftColumn, int bottomRightRow, int bottomRightColumn)
{
	QString cell;
	cell.append(QChar(topLeftColumn - 1 + 'A'));
	cell.append(QString::number(topLeftRow));
	cell.append(":");
	cell.append(QChar(bottomRightColumn - 1 + 'A'));
	cell.append(QString::number(bottomRightRow));

	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range->setProperty("VerticalAlignment", -4108);//xlCenter  
	range->setProperty("WrapText", true);
	range->setProperty("MergeCells", true);
}

QVariant QExcel::getCellValue(int row, int column)
{
	QAxObject* range = sheet->querySubObject("Cells(int,int)", row, column);
	return range->property("Value");
}

void QExcel::save()
{
	workBook->dynamicCall("Save()");
}

void QExcel::saveAs(const QString& filePath)
{
	//QDateTime current_date_time = QDateTime::currentDateTime();
	//QString current_date = current_date_time.toString("yyyy_MM_dd_hh_mm_ss");
	//if (isFileUsed(filePath))
	//{
	//	filePath == "C:\\Users\\" + current_date + ".xlsx";
	//}
	workBook->dynamicCall("SaveAs(const QString &)", QDir::toNativeSeparators(filePath));
	workBook->dynamicCall("Close(Boolean)", true);
}

int QExcel::getSheetsCount()
{
	return sheets->property("Count").toInt();
}

QString QExcel::getSheetName()
{
	return sheet->property("Name").toString();
}

QString QExcel::getSheetName(int sheetIndex)
{
	QAxObject* a = sheets->querySubObject("Item(int)", sheetIndex);
	return a->property("Name").toString();
}

bool QExcel::readSheet(QString sheetName, QVariantList& datas)
{
	selectSheet(sheetName);
	if (sheet == nullptr)
		return false;
	if (QAxObject* usedRange = sheet->querySubObject("UsedRange"))
	{
		QVariantList varRows = usedRange->dynamicCall("Value").toList();
		datas = varRows;
	}
	return true;
}

bool QExcel::writeSheet(QString sheetName, int startRow, int startCol, QVariantList& datas)
{
	selectSheet(sheetName);
	if (sheet == nullptr)
		return false;
	
	for (auto iter : datas)
	{
		QList<QVariant> rowDatas = iter.toList();
		int curCol = startCol;
		for (auto value : rowDatas)
		{
			setCellString(startRow, curCol, value.toString());
			++curCol;
		}
		++startRow;
	}
	//QString rangeCells = getRangeString(startRow, startCol, startRow + datas.size(), startCol + colCnt);
	//QAxObject* range = sheet->querySubObject("Range(const QString&)", rangeCells);
	//range->dynamicCall("SetValue(const QString&)", datas);

	return true;
}

bool QExcel::writeSheet(QString sheetName, const int& startRow, const int& startCol, const int& endRow, const int& endCol,
	const QVariant& datas)
{
	selectSheet(sheetName);
	if (sheet == nullptr)
		return false;
	QString rangeStr = getRangeString(startRow, startCol, endRow, endCol);
	QAxObject* range = sheet->querySubObject("Range(const QString&)", rangeStr);
	if (range)
	{
		bool res = range->setProperty("Value", datas);
		return res;
	}
	return true;
}

void QExcel::getUsedRange(int* topLeftRow, int* topLeftColumn, int* bottomRightRow, int* bottomRightColumn)
{
	QAxObject* usedRange = sheet->querySubObject("UsedRange");
	*topLeftRow = usedRange->property("Row").toInt();
	*topLeftColumn = usedRange->property("Column").toInt();

	QAxObject* rows = usedRange->querySubObject("Rows");
	*bottomRightRow = *topLeftRow + rows->property("Count").toInt() - 1;

	QAxObject* columns = usedRange->querySubObject("Columns");
	*bottomRightColumn = *topLeftColumn + columns->property("Count").toInt() - 1;
}

void QExcel::setColumnWidth(int column, int width)
{
	QString columnName;
	columnName.append(QChar(column - 1 + 'A'));
	columnName.append(":");
	columnName.append(QChar(column - 1 + 'A'));

	QAxObject* col = sheet->querySubObject("Columns(const QString&)", columnName);
	col->setProperty("ColumnWidth", width);
}

void QExcel::setCellTextCenter(int row, int column)
{
	QString cell;
	cell.append(QChar(column - 1 + 'A'));
	cell.append(QString::number(row));

	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range->setProperty("HorizontalAlignment", -4108);//xlCenter  
}

void QExcel::setCellTextWrap(int row, int column, bool isWrap)
{
	QString cell;
	cell.append(QChar(column - 1 + 'A'));
	cell.append(QString::number(row));

	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range->setProperty("WrapText", isWrap);
}

void QExcel::setAutoFitRow(int row)
{
	QString rowsName;
	rowsName.append(QString::number(row));
	rowsName.append(":");
	rowsName.append(QString::number(row));

	QAxObject* rows = sheet->querySubObject("Rows(const QString &)", rowsName);
	rows->dynamicCall("AutoFit()");
}

void QExcel::insertSheet(QString sheetName)
{
	sheets->querySubObject("Add()");
	QAxObject* a = sheets->querySubObject("Item(int)", 1);
	a->setProperty("Name", sheetName);
}

void QExcel::mergeSerialSameCellsInAColumn(int column, int topRow)
{
	int a, b, c, rowsCount;
	getUsedRange(&a, &b, &rowsCount, &c);

	int aMergeStart = topRow, aMergeEnd = topRow + 1;

	QString value;
	while (aMergeEnd <= rowsCount)
	{
		value = getCellValue(aMergeStart, column).toString();
		while (value == getCellValue(aMergeEnd, column).toString())
		{
			clearCell(aMergeEnd, column);
			aMergeEnd++;
		}
		aMergeEnd--;
		mergeCells(aMergeStart, column, aMergeEnd, column);

		aMergeStart = aMergeEnd + 1;
		aMergeEnd = aMergeStart + 1;
	}
}

void QExcel::clearCell(int row, int column)
{
	QString cell;
	cell.append(QChar(column - 1 + 'A'));
	cell.append(QString::number(row));

	clearCell(cell);
}

void QExcel::clearCell(const QString& cell)
{
	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range->dynamicCall("ClearContents()");
}

void QExcel::setCellDropItems(int row, int col, const QString& items)
{
	QAxObject* range = sheet->querySubObject("Range(const QString&)", getRangeString(row, col, row, col));
	QAxObject* validTion = range->querySubObject("Validation");
	//validTion->dynamicCall("Add(Type,AlertStyle,Operator,Formula1)", "xlValidateList", "xlValidAlertStop", "xlBetween", items);
	validTion->dynamicCall("Modify(int,int,int,QVariant)", 3, 1, 1, items);
	validTion->setProperty("IgnoreBlank", true);
	validTion->setProperty("InCellDropdown", true);
	validTion->setProperty("InputTitle", "test");
	//validTion->setProperty("Formula1", items);
}

int QExcel::getUsedRowsCount()
{
	QAxObject* usedRange = sheet->querySubObject("UsedRange");
	int topRow = usedRange->property("Row").toInt();
	QAxObject* rows = usedRange->querySubObject("Rows");
	int bottomRow = topRow + rows->property("Count").toInt() - 1;
	return bottomRow;
}

void QExcel::setCellString(const QString& cell, const QString& value)
{
	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range->dynamicCall("SetValue(const QString&)", value);
}

void QExcel::setCellFontSize(const QString& cell, int size)
{
	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range = range->querySubObject("Font");
	range->setProperty("Size", size);
}

void QExcel::setCellTextCenter(const QString& cell)
{
	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range->setProperty("HorizontalAlignment", -4108);//xlCenter  
}

void QExcel::setCellFontBold(const QString& cell, bool isBold)
{
	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range = range->querySubObject("Font");
	range->setProperty("Bold", isBold);
}

void QExcel::setCellTextWrap(const QString& cell, bool isWrap)
{
	QAxObject* range = sheet->querySubObject("Range(const QString&)", cell);
	range->setProperty("WrapText", isWrap);
}

void QExcel::setRowHeight(int row, int height)
{
	QString rowsName;
	rowsName.append(QString::number(row));
	rowsName.append(":");
	rowsName.append(QString::number(row));

	QAxObject* r = sheet->querySubObject("Rows(const QString &)", rowsName);
	r->setProperty("RowHeight", height);
}

QString QExcel::getRangeString(int startRow, int startCol, int endRow, int endCol)
{
	QString res = QString("%1%2:%3%4").arg(columnIntToString(startCol)).arg(startRow).arg(columnIntToString(endCol)).arg(endRow);
	if (startRow = endRow && startCol == endCol)
	{
		res = QString("%1%2").arg(columnIntToString(startCol)).arg(startRow);
	}
	return	res;
}

bool QExcel::isFileUsed(const QString& fpath)
{
	bool isUsed = false;

	QString fpathx = fpath + "x";

	QFile file(fpath);
	if (file.exists())
	{
		bool isCanRename = file.rename(fpath, fpathx);
		if (isCanRename == false)
		{
			isUsed = true;
		}
		else
		{
			file.rename(fpathx, fpath);
		}
	}
	file.close();

	return isUsed;
}

void QExcel::freeSheet()
{
	if (sheet)
	{
		delete sheet;
		sheet = nullptr;
	}	
}

QString QExcel::columnIntToString(int col)
{
	QString res;
	if (col / 26 > 0)
	{
		res += columnIntToString(col - 26);
	}
	else
	{
		res = QString("%1").arg(QString(col - 1 + 'A'));
	}
	return res;
}
上一篇:内存动态管理实例——动态数组的创建


下一篇:内部类、封装、内存分布、动态内存管理方式