概述
-
函数声明:
-
QAxObject(IUnknown *iface, QObject *parent = nullptr)
-
QAxObject(const QString &c, QObject *parent = nullptr)
-
QAxObject(QObject *parent = nullptr)
-
-
作用:三种构造函数,第一种根据传入的IUnknown指针iface加载其指向的COM对象;第二种加载指定的COM对象c;第三种没有设置加载的COM对象
2. querySubObject
-
函数声明:QAxObject* QAxBase::querySubObject(const char *name, QList<QVariant> &vars)
-
作用:获取子对象。若name为对象的属性名,则忽略vars;若name为函数原型,则vars为入参列表,当函数有出参则更新到var
// 设置Excel控件
QAxObject* excel = new QAxObject("Excel.Application");
// 获取工作簿集合
QAxObject* workBooks = excel->querySubObject("WorkSheets");
// 获取当前工作簿
QAxObject* workBook = workBooks->querySubObject("ActiveWorkBook");
// 获取第n个工作簿
workBook = workBooks->querySubObject("Item(int)", n);
// 获取工作表集合
QAxObject* workSheets = workBook->querySubObject("WorkSheets");
// 获取当前工作表
QAxObject* workSheet = workSheets->querySubObject("ActiveSheet");
// 获取第n个工作表
workSheet = workSheets->querySubObject("Item(int)", n);
// 获取第i行,第j列的单元格
QAxObject* cell = worksheet->querySubObject("Cells(int, int)", i, j);
// 获取第i列的所有单元格
QAxObject* range = worksheet->querySubObject("Range(QString)", "i");
// 获取第i列到第j列的所有单元格
QAxObject* range = worksheet->querySubObject("Range(QString)", "i:j");
QAxObject* range = worksheet->querySubObject("Range(QString, QString)", "i", "j");
3. dynamicCall
-
函数声明:QVariant QAxBase::dynamicCall(const char *function, QList<QVariant> &vars)
-
作用:调用COM对象的方法函数并返回对应的返回值。function为函数原型,vars为入参列表。若函数无返回值或者调用失败则返回无效的QVariant对象,当函数有出参则更新到vars
// 增加新的工作簿
excel->dynamicCall("Add()");
// 退出Excel程序
excel->dynamicCall("Quit()");
// 关闭工作簿
workBook->dynamicCall("Close(bool)", true);
// 保存工作簿
workBook->dynamicCall("Save()");
// 工作簿保存到fileName
workBook->dynamicCall("SaveAs(const QString&)", fileName);
4. setControl
-
函数声明:bool setControl(const QString& name)
-
作用:设置操作的COM对象。一个对象只能操作一个COM对象,后设置的将覆盖先前的设置。主要设置方式有四种,效率以先后次序递
-
注册组件的UUID(CLSID):axObj->setControl("{8E27C92B-1264-101C-8A2F-040224009C02}");
-
注册组件类名PROGID(版本号可含可不含):axObj->setControl("MSCal.Calendar");
-
组件的全称:axObj->setControl("Calendar Control 9.0");
-
组件的路径名:axObj->setControl("c:/files/file.doc");
-
QAxObject* axObj = new QAxObject();
axObj->setControl("Excel.Application") // 设置Excel控件
axObj->setControl("ET.Application") // 设置WPS-Excel控件
axObj->setControl("Word.Application") // 设置Word控件
Office与WPS的注册ID映射表
COM组件 | PROGID | CLSID |
Office Word | Word.Application | {00020906-0000-0000-C000-000000000046} |
Office Excel | Excel.Application | {00020812-0000-0000-C000-000000000046} |
Office PPT | Powerpoint.Application | {91493441-5a91-11cf-8700-00aa0060263b} |
WPS Word | KWPS.Application | {000209FF-0000-4b30-A977-D214852036FF} |
WPS Excel(旧版) | ET.Application | {45540001-5750-5300-4B49-4E47534F4654} |
WPS Excel(新版) | KET.Application | {45540001-5750-5300-4B49-4E47534F4655} |
WPS PPT | KWPP.Application | {44720441-94BF-4940-926D-4F38FECF2A48} |
5. setProperty
-
函数声明:bool setProperty(const char *name, const QVariant& var)
-
作用:设置对象的name属性的值为var。对于存在相同作用的dynamicCall,setProperty和property(获取属性值)性能更优
// 不自动打开excel
excel->setProperty("Visible", false);
// 警告不进行弹窗提示
excel->setProperty("DisplayAlerts", false);
// 设置单元的值
QVariant value;
range->setProperty("Value", value);
// 设置单元的格式
range->setProperty("NumberFormatLocal", "@");
// 设置单元的水平对齐方式
range->setProperty("HorizontalAlignment", hAlignment);
// 设置单元的垂直对齐方式
range->setProperty("VerticalAlignment", vAlignment);
6. isNull
-
函数声明:bool QAxBase::isNull() const
-
作用:若对象没有加载COM对象则返回true,否则返回false
7. asVariant
-
函数声明:QVariant asVariant() const
-
作用:返回加载COM对象的QVariant。一般用于dynamicCall的入参列表
int sheetCount = workSheets->property("Count").toInt(); // 获取工作表数量
QAxObject* lastSheet = workSheets->querySubObject("Item(int)", sheetCount); // 获取最后一个工作表
workSheets->dynamicCall("Add(QVariant)",lastSheet->asVariant()); // 在最后一个表前新增一个工作表
QAxObject* newSheet = workSheets->querySubObject("Item(int)", sheetCount); // 获取新表
lastSheet->dynamicCall("Move(QVariant)", newSheet->asVariant()); // 将最后的表移动到新表之前
QAxBasehttps://doc.qt.io/qt-5/qaxbase.html QAxObject https://doc.qt.io/qt-5/qaxobject.html
void init()
{
excel = new QAxObject(); // 初始化
if (nullptr == excel)
{
return;
}
excel->setControl("Excel.Application"); // 加载Excel控件
if (excel->isNull())
{
excel->setControl("KET.Application"); // 若上面加载失败,则尝试加载WPS-Excel
}
excel->setProperty("Visible", false); // 不显示Excel
excel->setProperty("DisplayAlerts", false); // 不弹出警告提示窗
workbooks = excel->querySubObject("WorkSheets"); // 获取工作簿集合
if (nullptr == workbooks)
{
excel->dynamicCall("Quit()");
return;
}
workbooks->dynamicCall("Add()"); // 新增一个工作簿
workbook = workbooks->querySubObject("ActiveWorkBook"); // 获取当前活动工作簿
worksheets = workbook->querySubObject("WorkSheets"); // 获取工作表集合
}
void appendSheet()
{
int sheetCount = worksheets->property("Count").toInt(); // 获取工作表的数量
QAxObject* lastSheet = worksheets->querySubObject("Item(int)", sheetCount); // 获取最后一个工作表lastSheet
worksheets->dynamicCall("Add(QVariant)", lastSheet->asVariant()); // 在lastSheet之前插入一个新工作表
QAXObject* newSheet = worksheets->querySubObject("Item(int)", sheetCount); // 获取新增的工作表newSheet
lastSheet->dynamicCall("Move(QVariant)", newSheet->asVariant()); // 将lastSheet移动到newSheet之前
}
// 注意:
// Add(QVariant var):新增一个对象插入到var对应的对象“之前”
// Move(QVariant var):将调用对象移动到var对应对象“之前”
// 若worksheets直接调用Add(),入参为空,则是在活动工作表之前插入一个新的工作表
bool write(const QString& filename, const QList<QList<QVariant>>& datas)
{
// 根据excel的格式类型获取单个工作表的最大行数限制,一般xls为65536,xlsx为1048576(不同版本的excel软件可能不一样)
QFileInfo fileInfo(filename);
int limitMaxRowCount= "xls" == fileInfo.completeSuffix() ? 65536 : 1048576;
QList<QList<QVariant>> curDatas;
int rowCount = 0;
int curSheetIndex= 0;
for (auto& rowData : datas)
{
++rowCount;
curDatas.append(rowData);
// 写入数据超过单个工作表最大的行数限制,则需将多余的数据写入新的工作表中
if (0 == rowCount % limitMaxRowCount)
{
++curSheetIndex;
if (!writeDatasToSheet(curSheetIndex))
{
return false;
}
}
}
if (!writeDatasToSheet(curSheetIndex + 1))
{
return false;
}
// 保存
saveAs(filename);
return true;
}
// 获取加载的COM对象工作表的最大行数限制(该方法不适用于xlsx和xls的区别)
int getMaxLimitRowCount() const
{
int count = INT_MAX;
if(nullptr != worksheet && !worksheet->isNull())
{
QAxObject* colObj = worksheet->querySubObject("Columns");
if(nullptr != colObj)
{
count = colObj->property("Count").toInt();
}
}
return count;
}
// 数据写入
bool writeDatasToSheet(int sheetIndex, const QList<QList<QVariant>>& datas)
{
if (setCurrentSheet(currentSheetIndex))
{
return false;
}
if (writeCurrentSheet(curDatas))
{
return false;
}
return true;
}
// 插入新的工作表并设置活动工作表
bool setCurrentSheet(int index)
{
bool ret = false;
if (nullptr != worksheets && !worksheets->isNull())
{
delete worksheet;
worksheet = nullptr;
// 尝试插入新的工作表
int sheetCount = worksheets->property("Count").toInt();
if (sheetCount < index)
{
appendSheet();
}
// 获取并激活工作表
worksheet = worksheets->querySubObject("Item(int)", index);
ret = nullptr != worksheet && !worksheet ->isNull();
if (ret)
{
worksheet ->dynamicCall("Activate(void)");
}
}
return ret;
}
// 向活动工作表写入数据
bool writeCurrentSheet(const QList<QList<QVariant>>& datas)
{
if (nullptr == worksheet || datas.empty())
{
return false;
}
int rows = datas.size();
int cols = datas[0].size();
// 获取需写入的单元格范围
QString rangStr = "";
convertColIndexToName(cols, rangStr);
rangeStr += QString::number(rows);
rangeStr = "A1:" + rangeStr;
QAxObject* range = worksheet->querySubObject("Range(const QString&)", rangeStr);
if (nullptr == range)
{
return false;
}
// 设置单元格的数据格式
range->setProperty("NumberFormatLocal", "@");
// 将数据从QList<QList<QVariant>>转换为QVariant
QVariant varCurData;
convertListListVarToVar(datas, varCurData);
return range->setProperty("Value2", varCurData);
}
// 计算最后一列的标识
void convertColIndexToName(int cols, QString& colName)
{
int tempData = cols / 27;
if (tempData > 0)
{
int mode = cols % 26;
convertColIndexToName(mode, colName);
convertColIndexToName(tempData, colName);
}
else
{
colName = (to26AlphabetString(cols) + colName);
}
}
// 根据数字索引获取对应的字母,1-A、2-B以此类推
QString numberToAlphabetStr(int n)
{
QChar ch = 0x40 + n;
return QString(ch);
}
// QList<QList<QVariant>>转换成QVariant
void convertListListVarToVar(const QList<QList<QVariant>>& datas, QVariant& var)
{
QVariantList varList;
for (int i = 0; i < datas.size(); ++i)
{
varList.append(QVariant(datas[i]));
}
var = QVariant(varList);
}
// 保存
void save()
{
if (nullptr != excel)
{
excel->dynamicCall("Save()");
}
}
// 另存为
void saveAs(cons QString& filename)
{
if (nullptr != workbook && !workbook->isNull())
{
QFileInfo fileInfo(filename);
int nFileType = ("xlsx" == fileInfo.completeSuffix()) ? 51 : 56;
// 文件路径需要将'/'改为'\'
QString path = filename;
path = path.replace("/", "\");
// 第二种方式:path = QDir::toNativeSeparators(filename);
// 调用保存方法
workbook->dynamicCall("SaveAs(const QString&,int,const QString&,const QString&,bool,bool)",
strPath, nFileType, QString(""), QString(""), false, false);
}
}
void exit()
{
if (nullptr != excel)
{
excel->dynamicCall("Quit()");
delete excel;
excel = nullptr;
}
}
// 需包含头文件Ole2.h
// 在操作COM对象的API前初始化COM库
HRESULT r = OleInitialize(0);
if (S_OK != r && S_FALSE != r)
{
qWarning("Qt:初始化Ole失败(error %x)", static_cast<unsigned int>(r));
}
// 使用完毕后需要释放COM库
OleUninitialize();
// 注意:OleInitialize和OleUninitialize的调用次数要一致
Win+R打开cmd,输入dcomcnfg打开组件服务,“控制台节点”=>“组件服务”=>“计算机”=>“我的电脑”=>“DCOM配置”,查看Microsoft Excel Application的属性,记录下应用程序ID和本地路径。命令输入regedit打开注册表编辑器,Ctrl+F查找刚才记录下的ID,其下的LocalServer和LocalServer32中的数据需要与在之前记录的本地路径一致
组件服务中打开Microsoft Excel Application的属性,修改其中的“启动和激活权限”、“访问权限”以及“配置权限”
// 注册WPS的KET
void registerKet()
{
QProcess p(NULL);
p.setStandardInputFile("\log.txt");
vector<QString> vCommand;
vCommand.reserve(3);
vCommand.push_back("reg copy HKCU\SOFTWARE\Classes\KET.Application HKLM\SOFTWARE\Classes\KET.Application /s /f");
vCommand.push_back("reg copy HKCU\SOFTWARE\Classes\KET.Application.9 HKLM\SOFTWARE\Classes\KET.Application.9 /s /f");
vCommand.push_back("reg copy HKCU\SOFTWARE\Classes\CLSID\{45540001-5750-5300-4B49-4E47534F4655} HKLM\SOFTWARE\Classes\CLSID\{45540001-5750-5300-4B49-4E47534F4655} /s /f");
for(auto& cmd : vCommand)
{
p.start(cmd);
p.waitForFinished();
}
}
registerKet();
QAxObject* excel = new QAxObject("KET.Application");
if(nullptr != excel && excel->isNull())
{
excel->setControl("{45540001-5750-5300-4B49-4E47534F4655}");
}
// 上述方法也适用于Office,只需修改注册的cmd以及对应的CLSID/PROGID
// 代码来源 https://blog.csdn.net/SomeOne75/article/details/123007669
(5)加载控件时用的是PROGID,而实际注册表中没有设置该ID(比较常见)
可以考虑若根据PROGID加载失败时,尝试根据CLSID加载
QAxObject* excel = new QAxObject();
excel->setControl("Excel.Application");
if(excel->isNull())
{
excel->setControl("{00020812-0000-0000-C000-000000000046}");
}
2. 既能操作Office也能操作WPS
-
加载控件对象Excel.Application失败,可以尝试加载WPS的KET.Application
-
设置值和获取值用的是setProperty("Value", value) / property("Value"),"Value"只支持Office,将其中的 "Value"改为"Value2",Office和WPS都支持
-
文件路径没有进行处理,其中''需要转换为"\"才能正确的获取到相应路径的文件;
-
设置值或获取值时调用的方法不适用于当前的COM对象(详见2的第二条);
-
excel主要有两种格式,xls和xlsx,一般xls单个工作表限制最大行数为65536,xlsx为1048576,因此插入新表再写入额外数据。 可以打开excel文件,选中第一行,再按“Ctrl+↓”可以查看最大行数和最大列数
-
property("NumberFormatLocal")获取单元格的格式,默认为“G/通用格式”,通过setProperty("NumberFormatLocal", "@")设置单元格为字符串格式。需注意不同版本的excel支持的格式类型可能有差异性
-
一般在实际开发中,数据的导出(写Excel)用的比较多。若数据一行一行的写,则需要多次调用querySubObject获取指定范围的单元格1*cols;而直接获取整个工作表中需要写入的单元格rows*cols,一个工作表只需调用一次querySubObject,从而提高写入的效率
最后
以上就是魁梧机器猫为你收集整理的QAxObject写Excel(详解)的全部内容,希望文章能够帮你解决QAxObject写Excel(详解)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复