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()");
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 自动获得拟合系数,自动引用,计算