QAxObject保存Excel表格

QAxObject新建并保存Excel表格

C++中有三种常用的操作Excel的方式,Xlnt、QtXlsx以及QAxObject
本文使用QAxObject来新建并保存Excel表格

1:初始化Excel程序

	/*初始化Excel对象*/
	QAxObject *_excelObject = NULL; ///< 代表了EXCEL服务器,
	QAxObject *_curWorkbook;        ///< 当前工作簿
	QAxObject *_curWorkSheet;       ///< 当前sheet(实际就是第一个sheet)
	QAxObject *_usedRange;          ///< sheet里的数据区域范围

	/*初始化服务,否则报错误
	CoCreateInstance failure 
	QAxBase::setControl: requested control Excel.Application could not be instantiated
	QAxBase::dynamicCallHelper: Object is not initialized, or initialization failed
	*/
	HRESULT r = OleInitialize(0);
	if (r != S_OK && r != S_FALSE)
	{
		return -1;
	}

	if (_excelObject == NULL)
	{
		/*代表整个 Microsoft Excel 应用程序。*/
		_excelObject = new QAxObject("Excel.Application");
		if (!_excelObject)
		{
			return -1;
		}
	}

2:隐藏Excel窗口

	/*隐藏Excel窗口*/
	_excelObject->setProperty("Visible", false); 

3:新建一个Excel

	/*返回一个表示所有打开的工作簿的**工作簿** 集合。 此为只读属性*/
	QAxObject * workbooks = _excelObject->querySubObject("WorkBooks");

	/*打开存在的Excel文件*/
	//workbooks->dynamicCall("Open (const QString&)", "D:/123.xlsx");

    /*新建一个工作表。 新工作表将成为活动工作表。*/
	workbooks->dynamicCall("Add");

	/*workbooks必须指向活跃 WorkBook,否则保存时报错:
	  QAxBase::dynamicCallHelper: SaveCopyAs(QString): No such property in  [unknown]  Candidates are:*/
	workbooks = _excelObject->querySubObject("ActiveWorkBook");	// ToDo ...

	/*活动工作表*/
	_curWorkbook = _excelObject->querySubObject("ActiveWorkBook");

	/*第一个sheet*/
	_curWorkSheet = _curWorkbook->querySubObject("Worksheets(int)", 1);///< 当前sheet(实际就是第一个sheet)

3:常用操作

快速填充数据块

    /*快速填充数据块, 填充B1到C20这一数据块*/
    /*https://www.cnblogs.com/lifexy/p/10743316.html*/
    QAxObject *user_range = _curWorkSheet->querySubObject("Range(const QString&)", cols_range);
    QList<QList<QVariant> > datas;
    for (int i = 0; i<20; i++)
	{
		QList<QVariant> rows;
		rows.append(i);
		rows.append(3 * i + 0.1);
		datas.append(rows);
	}
	QVariant var;
	castListListVariant2Variant(datas, var);
	user_range->setProperty("Value", var);

设置单个单元格的数据和颜色

/*设置单个单元格的内容,第5行,第1列填入字符串‘Hello’*/
	QAxObject *singleCell = _curWorkSheet->querySubObject("Cells(int,int)", 5, 1);
	singleCell->dynamicCall("SetValue(const QVariant&)", QVariant(QStringLiteral("Hello")));

	/*设置背景色为红色:http://blog.sina.com.cn/s/blog_a6fb6cc90101gv2p.html*/
	QAxObject* interior = singleCell->querySubObject("Interior");
	interior->setProperty("Color", QColor(0, 255, 0));   //设置单元格背景色(绿色)

使用批量公式

	/*使用批量公式:https://github.com/muellerj/hexplorer/blob/master/src/excel.cpp*/
	const int average_use_cnt = 100;
	cols_range.sprintf("E%d:E%d", 1, 20);
	QAxObject *user_range_formular = _curWorkSheet->querySubObject("Range(const QString&)", cols_range);
	QList<QList<QVariant> > datas_formular;
	for (int i = 0; i<19; i++)
	{
		QList<QVariant> rows;
		QString formular;
		//formular.sprintf("=RC[-2]*100");
		formular.sprintf("=AVERAGE(RC[-2]:R[1]C[-2])");
		rows.append(formular);

		datas_formular.append(rows);
	}
	QVariant var_formular;
	castListListVariant2Variant(datas_formular, var_formular);
	user_range_formular->setProperty("Value", var_formular);

添加折线图

	/*画折线图*/
	// 先选中一个空白的地方,不然后面的SeriesCollection会先将所有行都做个散点图。。。
	_usedRange = _curWorkSheet->querySubObject("UsedRange");
	QAxObject *clos = _usedRange->querySubObject("Columns");
	QAxObject *emptyCell = _curWorkSheet->querySubObject("Cells(int,int)", 1, clos->property("Count").toInt() + 3);
	emptyCell->dynamicCall("Select(void)");

	/*表达式。AddChart2(Style、 XlChartType、 Left、 Top、 Width、 Height、 NewLayout)*/
	QAxObject *shapes = _curWorkSheet->querySubObject("Shapes");
	shapes->dynamicCall("AddChart(int)", 74, 100, 100, 500, 800); //74:折线散点图,左 上 宽 高
    
    // 再将视野拖回到左上角区域
	QAxObject *range = _curWorkSheet->querySubObject("Range(QVariant)", "$A$1:$A$2");
	range->dynamicCall("Select(void)");

	int nbChart = shapes->property("Count").toInt();
	QAxObject *shape = shapes->querySubObject("Range(int)", nbChart);

	/*根据指定的系数缩放图形宽度。 对于图片和 OLE 对象,您可以说明是相对于原始大小还是相对于当前大小缩放形状。 图片和 OLE 对象以外的形状总是相对于当前宽度缩放。*/
	//shape->dynamicCall("ScaleWidth(QVariant,QVariant)",2,0);
	shape->dynamicCall("Select(void)");

	QAxObject *chart = _curWorkbook->querySubObject("ActiveChart");
	QAxObject *seriesCollection = chart->querySubObject("SeriesCollection()");
	seriesCollection->dynamicCall("NewSeries (void)");
	int nb = seriesCollection->property("Count").toInt();
	QAxObject *series = chart->querySubObject("SeriesCollection(int)", nb);
	series->setProperty("Name", "Linear parameter");

	/*x轴最大最小值*/
	QAxObject* aixs_x = chart->querySubObject("Axes(int)", 1);
	aixs_x->setProperty("MinimumScale", 0);
	aixs_x->setProperty("MaximumScale", 25);

	/*y轴最大最小值*/
	QAxObject* aixs_y = chart->querySubObject("Axes(int)", 2);
	aixs_y->setProperty("MinimumScale", 0);
	aixs_y->setProperty("MaximumScale", 80);

	// 给图表添加数据
	//range->dynamicCall("Select(void)");
	cols_range.sprintf("B%d:B%d", 1, 20);/*B1:C20*/
	QAxObject * xvalues = _curWorkSheet->querySubObject("Range(const QString&)", cols_range);

	cols_range.sprintf("C%d:C%d", 1, 20);/*B1:C20*/
	QAxObject * yvalues = _curWorkSheet->querySubObject("Range(const QString&)", cols_range);
	series->setProperty("XValues", xvalues->asVariant());
	series->setProperty("Values", yvalues->asVariant());

添加趋势线

	// 添加趋势线
	QAxObject *trendLines = series->querySubObject("Trendlines()");
	if (trendLines)
	{
		trendLines->dynamicCall("Add()");
		int nbTrendLines = trendLines->property("Count").toInt();
		QAxObject *trendLine = series->querySubObject("Trendlines(int)", nbTrendLines);
		// 设定图标格式为 “散点图”,数字-4133来自 枚举变量 XlTrendlineType  : xlLogarithmic
		/*各种变量的值和属性名的关系:http://mugiseyebrows.ru/excel/Excel.Range.html#Cells*/
		trendLine->setProperty("Type", -4132);
		trendLine->setProperty("Name", "TrendLineName");
		trendLine->setProperty("DisplayEquation", true);/*显示公式,显示R2*/
		trendLine->setProperty("DisplayRSquared",true);

		/*获取公式*/
		QAxObject *dataLabel = trendLine->querySubObject("DataLabel");
		QString strFormula = dataLabel->property("Formula").toString();

		getFormulaCoeffcient_kx_plus_b(strFormula, out_k, out_b);
		out_formular = strFormula.toStdString();
	}

	/*将公式中的值自动填入,但是不能随着图表选择的数据范围动态刷新*/
	QAxObject *kvalue_Cell = _curWorkSheet->querySubObject("Cells(int,int)", 1, 7);
	QString formular;
	formular.sprintf("=INDEX(LINEST(R1C3:R20C3,R1C2:R20C2),1)");/*注意:Y值在左,X值在右,绝对位置*/
	kvalue_Cell->setProperty("Formula", formular);

	QAxObject *bvalue_Cell = _curWorkSheet->querySubObject("Cells(int,int)", 2, 7);
	formular.sprintf("=INDEX(LINEST(R1C3:R20C3,R1C2:R20C2),2)");/*注意:Y值在左,X值在右,绝对位置*/
	bvalue_Cell->setProperty("Formula", formular);

保存并退出Excel

	/* ./ 会将文件保存在我的文档目录下面*/
	QString filepath = "./test.xlsx";
	/*文件名字转换,否则Excel一直卡住,QDir::toNativeSeparators*/
	workbooks->dynamicCall("SaveCopyAs(QString)", QDir::toNativeSeparators(filepath));

	/*在关闭Excel之前先调用这个不显示警告框,否则会弹出是否保存sheet1的对话框*/
	_excelObject->setProperty("DisplayAlerts", false);
	_excelObject->dynamicCall("Quit()");
运行结果

QAxObject保存Excel表格_第1张图片

示例代码

main.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include "iostream"
#include "stdio.h"
using namespace std;

//! 形如:"y = -9.113x + 78.016"
//! 则 a=-9.113 b=78.016
void getFormulaCoeffcient_kx_plus_b(QString strFormula, double &k, double &b)
{
	int posEqual = strFormula.indexOf('=');
	int posX = strFormula.indexOf('x');
	QString strK = strFormula.mid(posEqual + 1, posX - posEqual - 1);
	if (strK == " ") k = 1;
	else k = strK.toDouble();

	bool isPositive = true;
	QString str_after_x = strFormula.mid(posX + 1);
	int posB = str_after_x.indexOf('+');
	if (posB == -1)
	{
		isPositive = false;
		posB = str_after_x.indexOf('-');
	}
	else
	{
		isPositive = true;
	}

	if (-1 == posB)
	{
		b = 0;
	}
	else
	{
		QString strB = str_after_x.mid(posB + 1);
		b = strB.toDouble();
		if (!isPositive)
			b *= -1.0;
	}
}

//把QList > 转为QVariant,用于快速写入的
void castListListVariant2Variant(const QList<QList<QVariant> > &cells, QVariant &res)
{
	QVariantList vars;
	const int rows = cells.size();
	for (int i = 0; i<rows; ++i)
	{
		vars.append(QVariant(cells[i]));
	}
	res = QVariant(vars);
}

int main()
{
	std::string out_formular;
	double out_k;
	double out_b;

	/*初始化Excel对象*/
	QAxObject *_excelObject = NULL; ///< 代表了EXCEL服务器,
	QAxObject *_curWorkbook;        ///< 当前工作簿
	QAxObject *_curWorkSheet;       ///< 当前sheet(实际就是第一个sheet)
	QAxObject *_usedRange;          ///< sheet里的数据区域范围

	/*初始化服务,否则报错误
	CoCreateInstance failure 
	QAxBase::setControl: requested control Excel.Application could not be instantiated
	QAxBase::dynamicCallHelper: Object is not initialized, or initialization failed
	*/
	HRESULT r = OleInitialize(0);
	if (r != S_OK && r != S_FALSE)
	{
		return -1;
	}

	if (_excelObject == NULL)
	{
		/*代表整个 Microsoft Excel 应用程序。*/
		_excelObject = new QAxObject("Excel.Application");
		if (!_excelObject)
		{
			return -1;
		}
	}

	/*隐藏Excel窗口*/
	_excelObject->setProperty("Visible", false); 

	/*返回一个表示所有打开的工作簿的**工作簿** 集合。 此为只读属性*/
	QAxObject * workbooks = _excelObject->querySubObject("WorkBooks");

	/*打开存在的Excel文件*/
	//workbooks->dynamicCall("Open (const QString&)", "D:/123.xlsx");

    /*新建一个工作表。 新工作表将成为活动工作表。*/
	workbooks->dynamicCall("Add");

	/*workbooks必须指向活跃 WorkBook,否则保存时报错:
	  QAxBase::dynamicCallHelper: SaveCopyAs(QString): No such property in  [unknown]  Candidates are:*/
	workbooks = _excelObject->querySubObject("ActiveWorkBook");	// ToDo ...

	/*活动工作表*/
	_curWorkbook = _excelObject->querySubObject("ActiveWorkBook");

	/*第一个sheet*/
	_curWorkSheet = _curWorkbook->querySubObject("Worksheets(int)", 1);///< 当前sheet(实际就是第一个sheet)

	QList<QVariant> listXValues, listValues;

	QString cols_range;/* "B1:C20" */
	cols_range.sprintf("B%d:C%d", 1, 20);

	/*快速填充数据块, 填充B1到C20这一数据块*/
	/*https://www.cnblogs.com/lifexy/p/10743316.html*/
	QAxObject *user_range = _curWorkSheet->querySubObject("Range(const QString&)", cols_range);
	QList<QList<QVariant> > datas;
	for (int i = 0; i<20; i++)
	{
		QList<QVariant> rows;
		rows.append(i);
		rows.append(3 * i + 0.1);
		datas.append(rows);
	}
	QVariant var;
	castListListVariant2Variant(datas, var);
	user_range->setProperty("Value", var);

	/*设置单个单元格的内容,第5行,第1列填入字符串‘Hello’*/
	QAxObject *singleCell = _curWorkSheet->querySubObject("Cells(int,int)", 5, 1);
	singleCell->dynamicCall("SetValue(const QVariant&)", QVariant(QStringLiteral("Hello")));

	/*设置背景色为红色:http://blog.sina.com.cn/s/blog_a6fb6cc90101gv2p.html*/
	QAxObject* interior = singleCell->querySubObject("Interior");
	interior->setProperty("Color", QColor(0, 255, 0));   //设置单元格背景色(绿色)

	/*使用批量公式:https://github.com/muellerj/hexplorer/blob/master/src/excel.cpp*/
	const int average_use_cnt = 100;
	cols_range.sprintf("E%d:E%d", 1, 20);
	QAxObject *user_range_formular = _curWorkSheet->querySubObject("Range(const QString&)", cols_range);
	QList<QList<QVariant> > datas_formular;
	for (int i = 0; i<19; i++)
	{
		QList<QVariant> rows;
		QString formular;
		//formular.sprintf("=RC[-2]*100");
		formular.sprintf("=AVERAGE(RC[-2]:R[1]C[-2])");
		rows.append(formular);

		datas_formular.append(rows);
	}
	QVariant var_formular;
	castListListVariant2Variant(datas_formular, var_formular);
	user_range_formular->setProperty("Value", var_formular);

	/*画折线图*/
	// 先选中一个空白的地方,不然后面的SeriesCollection会先将所有行都做个散点图。。。
	_usedRange = _curWorkSheet->querySubObject("UsedRange");
	QAxObject *clos = _usedRange->querySubObject("Columns");
	QAxObject *emptyCell = _curWorkSheet->querySubObject("Cells(int,int)", 1, clos->property("Count").toInt() + 3);
	emptyCell->dynamicCall("Select(void)");

	/*表达式。AddChart2(Style、 XlChartType、 Left、 Top、 Width、 Height、 NewLayout)*/
	QAxObject *shapes = _curWorkSheet->querySubObject("Shapes");
	shapes->dynamicCall("AddChart(int)", 74, 100, 100, 500, 800); //74:折线散点图,左 上 宽 高
    
    // 再将视野拖回到左上角区域
	QAxObject *range = _curWorkSheet->querySubObject("Range(QVariant)", "$A$1:$A$2");
	range->dynamicCall("Select(void)");

	int nbChart = shapes->property("Count").toInt();
	QAxObject *shape = shapes->querySubObject("Range(int)", nbChart);

	/*根据指定的系数缩放图形宽度。 对于图片和 OLE 对象,您可以说明是相对于原始大小还是相对于当前大小缩放形状。 图片和 OLE 对象以外的形状总是相对于当前宽度缩放。*/
	//shape->dynamicCall("ScaleWidth(QVariant,QVariant)",2,0);
	shape->dynamicCall("Select(void)");

	QAxObject *chart = _curWorkbook->querySubObject("ActiveChart");
	QAxObject *seriesCollection = chart->querySubObject("SeriesCollection()");
	seriesCollection->dynamicCall("NewSeries (void)");
	int nb = seriesCollection->property("Count").toInt();
	QAxObject *series = chart->querySubObject("SeriesCollection(int)", nb);
	series->setProperty("Name", "Linear parameter");

	/*x轴最大最小值*/
	QAxObject* aixs_x = chart->querySubObject("Axes(int)", 1);
	aixs_x->setProperty("MinimumScale", 0);
	aixs_x->setProperty("MaximumScale", 25);

	/*y轴最大最小值*/
	QAxObject* aixs_y = chart->querySubObject("Axes(int)", 2);
	aixs_y->setProperty("MinimumScale", 0);
	aixs_y->setProperty("MaximumScale", 80);

	// 给图表添加数据
	//range->dynamicCall("Select(void)");
	cols_range.sprintf("B%d:B%d", 1, 20);/*B1:C20*/
	QAxObject * xvalues = _curWorkSheet->querySubObject("Range(const QString&)", cols_range);

	cols_range.sprintf("C%d:C%d", 1, 20);/*B1:C20*/
	QAxObject * yvalues = _curWorkSheet->querySubObject("Range(const QString&)", cols_range);
	series->setProperty("XValues", xvalues->asVariant());
	series->setProperty("Values", yvalues->asVariant());

	// 添加趋势线
	QAxObject *trendLines = series->querySubObject("Trendlines()");
	if (trendLines)
	{
		trendLines->dynamicCall("Add()");
		int nbTrendLines = trendLines->property("Count").toInt();
		QAxObject *trendLine = series->querySubObject("Trendlines(int)", nbTrendLines);
		// 设定图标格式为 “散点图”,数字-4133来自 枚举变量 XlTrendlineType  : xlLogarithmic
		/*各种变量的值和属性名的关系:http://mugiseyebrows.ru/excel/Excel.Range.html#Cells*/
		trendLine->setProperty("Type", -4132);
		trendLine->setProperty("Name", "TrendLineName");
		trendLine->setProperty("DisplayEquation", true);/*显示公式,显示R2*/
		trendLine->setProperty("DisplayRSquared",true);

		/*获取公式*/
		QAxObject *dataLabel = trendLine->querySubObject("DataLabel");
		QString strFormula = dataLabel->property("Formula").toString();

		getFormulaCoeffcient_kx_plus_b(strFormula, out_k, out_b);
		out_formular = strFormula.toStdString();
	}

	/*将公式中的值自动填入,但是不能随着图表选择的数据范围动态刷新*/
	QAxObject *kvalue_Cell = _curWorkSheet->querySubObject("Cells(int,int)", 1, 7);
	QString formular;
	formular.sprintf("=INDEX(LINEST(R1C3:R20C3,R1C2:R20C2),1)");/*注意:Y值在左,X值在右,绝对位置*/
	kvalue_Cell->setProperty("Formula", formular);

	QAxObject *bvalue_Cell = _curWorkSheet->querySubObject("Cells(int,int)", 2, 7);
	formular.sprintf("=INDEX(LINEST(R1C3:R20C3,R1C2:R20C2),2)");/*注意:Y值在左,X值在右,绝对位置*/
	bvalue_Cell->setProperty("Formula", formular);


	/* ./ 会将文件保存在我的文档目录下面*/
	QString filepath = "./test.xlsx";
	/*文件名字转换,否则Excel一直卡住,QDir::toNativeSeparators*/
	workbooks->dynamicCall("SaveCopyAs(QString)", QDir::toNativeSeparators(filepath));

	/*在关闭Excel之前先调用这个不显示警告框,否则会弹出是否保存sheet1的对话框*/
	_excelObject->setProperty("DisplayAlerts", false);
	_excelObject->dynamicCall("Quit()");

	delete _excelObject;
	_excelObject = NULL;

	OleUninitialize();
	return 0;
}

CMakeLists

cmake_minimum_required (VERSION 2.6 FATAL_ERROR)

project(QAxObject_Excel)
find_package (Qt5 REQUIRED Core Widgets AxContainer)

ADD_DEFINITIONS (${QT_DEFINITIONS})
ADD_DEFINITIONS(-D _XKEYCHECK_H)
ADD_EXECUTABLE  (Demo main.cpp)

set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Demo)

TARGET_LINK_LIBRARIES (Demo Qt5::Core Qt5::Widgets Qt5::AxContainer)
参考链接

简介的例子: Qt中使用QAxObject操作Excel
单元格详细操作: Qt之操作Excel
Excel vba文档:Excel VBA 参考
Excel 保存出错:Qt 使用QAxObject保存excel出错,使用双反斜线\路径分隔符
Excel COM 变量:The COM object is a QAxObject with the CLSID
Excel 自动拟合曲线:excel 自动获得拟合系数,自动引用,计算

你可能感兴趣的:(QT)