前言
QAxObject主要于COM(Component Object Model)对象进行交互,简单来说就是对COM对象的封装。
QAxObject的主要函数
querySubObject获取COM对象中的子对象,并返回相应的QAxObject对象。使用此方法时,需要指定子对象的名称或者索引。例如:
#用于获取Documents对象,就是word的文档集合
QAxObject *document = m_wordObject->querySubObject("Documents");
dynamicCall动态调用对象的方法,可以在运行时动态调用对象的方法。这意味着可以根据具体的运行时条件选择不同的方法进行调用,而不需要在编译时确定调用哪个方法。
该方法的参数是任意类型(基本类型、自定义类型等),并且支持传递多个参数,返回值也是任意类型。例如:
#添加文档,新建以模板类型的文档,传入参数为自定义模板路径
document->dynamicCall("Add(QString)", m_templateFilePath);
#新建一个空文档
document->dynamicCall("Add()");
setProperty设置对象属性,传入参数为属性的名称和属性的值。例如:
#设置是否显示窗口
m_wordObject->setProperty("Visible", m_visable);
property获取对象的属性值,传入参数为属性的名称,返回值为改属性的值。例如:
#获得页面左边距
pageSetup->property("LeftMargin")
访问word的对象模型(相关参数可以Microsoft Office的官方文档或者COM API文档)
访问对象接口函数前首先要对接口进行初始化(一个进程对COM对象接口只需要初始化一次)
//为当前线程初始化windows com的运行环境并指定当前线程的并发模型
//COINIT_APARTMENTTHREADED , COINIT_MULTITHREADED
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
程序退出之前要终止服务函数也就是需要反初始化释放COM资源
CoUninitialize();
bool custom_wordEngine::open()
{
//先关闭再打开
close();
//为当前线程初始化windows com的运行环境并指定当前线程的并发模型
//COINIT_APARTMENTTHREADED , COINIT_MULTITHREADED
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr)){
m_comInitialized = true;
}
QStringList progIds = {
"word.Application",
"kwps.Application",
"wps.Application",
};
//创建一个com对象
m_wordObject = new QAxObject();
bool flag = false;
for(const QString& progId : progIds){
flag = m_wordObject->setControl( progId );
if(flag){
break;
}else{
std::string id = progId.toStdString();
saError("Unable to start document:%s",id.c_str());
}
}
if(!flag){
if(m_wordObject != nullptr){
delete m_wordObject;
}
return false;
}
//是否显示窗体
m_wordObject->setProperty("Visible", m_visable);
//获得所有工作文档
QAxObject *document = m_wordObject->querySubObject("Documents");
if(!document){
saError("Unable to get working document");
QAxObject *lastError = m_wordObject->querySubObject("GetLastError()");
if(lastError){
QString error = lastError->dynamicCall("Description()").toString();
saError( "COM Error: %s",error.toStdString().c_str());
delete lastError;
}
return false;
}
//是否以文件template.dot为模版新建一个文档
if(m_templates){
if(m_templateFilePath.isEmpty())
return false;
//判断文件是否文件不存在
QFileInfo fileInfo(m_templateFilePath);
if(!fileInfo.exists())
return false;
document->dynamicCall("Add(QString)", m_templateFilePath);
}else{
//新建一个新文档
document->dynamicCall("Add()");
}
//获取当前激活的文档
m_wordDocument = m_wordObject->querySubObject("ActiveDocument");
//获得选定对象
m_selection = m_wordObject->querySubObject("Selection");
m_opened = true;
return m_opened;
}
2.保存
//word的保存是没有返回值的,因此需要自己判断文件是否保存成功,可以判断文件是否发生修改或者大小发生变化
bool custom_wordEngine::saveAs(const QString &strWordFilePath)
{
//如果文件已存在,记录原始信息用于对比
QFileInfo oldInfo(strWordFilePath);
bool fileExisted = oldInfo.exists();
QDateTime oldModified = oldInfo.lastModified();
qint64 oldSize = oldInfo.size();
QVariant result = m_wordDocument->dynamicCall("SaveAs(const QString&)",
QDir::toNativeSeparators(strWordFilePath));
QFileInfo newInfo(strWordFilePath);
if(!newInfo.exists()){
return false;
}
if(fileExisted){
//检测旧文件是否覆盖修改
if(newInfo.lastModified() == oldModified && newInfo.size() == oldSize){
return false;
}
}else{
//新文件大小
if(newInfo.size() <= 0){
return false;
}
}
return true;
}
bool custom_wordEngine::close()
{
//如果文档打开中
if(m_opened){
//关闭文档窗口
if(m_wordDocument)
m_wordDocument->dynamicCall("Close (bool)", true);
//退出word
if(m_wordObject)
m_wordObject->dynamicCall("Quit()");
//释放对象
if(m_wordObject){
delete m_wordObject;
m_wordObject = nullptr;
}
m_opened = false;
//反初始化
if(m_comInitialized){
CoUninitialize();
m_comInitialized = false;
}
}
return m_opened;
}
void custom_wordEngine::setText(const QString &text)
{
m_selection->dynamicCall("TypeText(QString&)", text);
}
void custom_wordEngine::setFont(QString strFamily, int nSize, bool bBold, bool bItalic, bool bUnderLine)
{
m_selection->querySubObject("Font")->setProperty("Name", strFamily);
m_selection->querySubObject("Font")->setProperty("Size", nSize);
m_selection->querySubObject("Font")->setProperty("Bold", bBold);
m_selection->querySubObject("Font")->setProperty("Italic", bItalic);
if(bUnderLine){
m_selection->querySubObject("Font")->setProperty("Underline",2);
}else{
m_selection->querySubObject("Font")->setProperty("Underline",0);
}
}
int custom_wordEngine::insertTable(int row, int column)
{
m_selection->querySubObject("ParagraphFormat")->dynamicCall("Alignment", "wdAlignParagraphCenter");
//m_selection->dynamicCall("TypeText(QString&)", "Table Test");//设置标题
QAxObject *range = m_selection->querySubObject("Range");
QAxObject *tables = m_wordDocument->querySubObject("Tables");
QAxObject *table = tables->querySubObject("Add(QVariant,int,int)",range->asVariant(),row,column);
//设置边框样式
for(int i=1;i<=6;i++){
QString str = QString("Borders(-%1)").arg(i);
QAxObject *borders = table->querySubObject(str.toLatin1().data());
borders->dynamicCall("SetLineStyle(int)",1);
}
//获取当前文档中所有表格的数量(新插入的表格是最后一个)
QAxObject *allTables = m_wordDocument->querySubObject("Tables");
int tableIndex = allTables->property("Count").toInt();
return tableIndex;
}
void custom_wordEngine::setCellString(int tableIndex, int row, int column, const QString &text)
{
QAxObject* tables = m_wordDocument->querySubObject( "Tables" );
QAxObject* table = tables->querySubObject("Item(int)", tableIndex);
if(table){
table->querySubObject("Cell(int, int)", row, column)->querySubObject("Range")->dynamicCall("SetText(QString)", text);
table->querySubObject("Cell(int, int)", row, column);
}
}
void custom_wordEngine::mergeCell(int tableIndex, int nStartRow, int nStartCol, int nEndRow, int nEndCol, bool bVerticalCenter)
{
QAxObject* tables = m_wordDocument->querySubObject("Tables");
QAxObject* table = tables->querySubObject("Item(int)",tableIndex);
QAxObject* StartCell =table->querySubObject("Cell(int, int)",nStartRow,nStartCol);
QAxObject* EndCell = table->querySubObject("Cell(int, int)",nEndRow,nEndCol);
//合并单元格
//StartCell->querySubObject("Merge(QAxObject *)",EndCell->asVariant());
StartCell->dynamicCall("Merge(IDispatch*)", EndCell->asVariant());
//设置对齐方式 -上下对齐
if(bVerticalCenter){
StartCell->dynamicCall("VerticalAlignment", "wdCellAlignVerticalCenter");
}else{
m_selection->querySubObject("ParagraphFormat")->dynamicCall("Alignment", "wdAlignParagraphJustify");
StartCell->dynamicCall("VerticalAlignment", "wdCellAlignVerticalTop");
}
}
//往单元格中插入图片
void custom_wordEngine::insertCellPic(int tableIndex, int row, int column, const QString &picPath)
{
QAxObject* pTables = m_wordDocument->querySubObject( "Tables" );
QAxObject* table = pTables->querySubObject("Item(int)", tableIndex);
QAxObject* range = table->querySubObject("Cell(int, int)", row, column)->querySubObject("Range");
range->querySubObject("InlineShapes")->dynamicCall("AddPicture(const QString&)",picPath);
}
//插入图片
void custom_wordEngine::insertPicture(QString picturePath)
{
// 插入图片
QAxObject* inlineShapes = m_selection->querySubObject("InlineShapes");
QAxObject* shape = inlineShapes->querySubObject("AddPicture(const QString&)", picturePath);
// 设置段落居中
QAxObject* paragraphFormat = m_selection->querySubObject("ParagraphFormat");
paragraphFormat->setProperty("Alignment", 1 /*wdAlignParagraphCenter*/);
}
std::unique_ptr<custom_wordEngine> pWord(new custom_wordEngine(this));
pWord->setVisable(false);
pWord->setEnableTemplates(false);
if(!pWord->open()){
saError("Unable to open document");
emit signals_save_failed();
return;
}
QFileInfo fileInfo(m_saveWordFilePath);
QString strWordName = fileInfo.baseName();
QList<QList<QString>>datas;
///设置页边距
pWord->setPageMargin(45,72,45,72);
pWord->setFont("SimSun",15,true,false,false);
pWord->setAlign(custom_wordEngine::AlignmentType::AlignmentTypeCenter);
pWord->setText(strWordName);
QStringList listHeader;
listHeader.append(tr("深度(m)"));
listHeader.append(tr("俯仰角(°)"));
listHeader.append(tr("翻滚角(°)"));
listHeader.append(tr("方位角(°)"));
int tableRows = m_fileData.testPointData.size() + 1;
//插入标题表格
int tableIndex = pWord->insertTable(tableRows,7);
pWord->setTableAlign(1,custom_wordEngine::AlignmentTypeCenter);
for(int j = 0;j < listHeader.size();j++){
pWord->setTableFont(tableIndex,1,j + 1,"SimSun",10,true,false,false);
pWord->setCellString(tableIndex,1,j + 1,listHeader.at(i));
}
QString text = "";
for(int j = 0;j < m_fileData.testPointData.size();j++){
SH::POINTDATA pointData = m_fileData.testPointData.at(j);
text = QString("%1").arg(pointData.Deep,0,'f',shAppConfig->deepDecimalPlacesIndex());
pWord->setTableFont(tableIndex,j+2,1,"SimSun",10,false,false,false);
pWord->setCellString(tableIndex,j+2,1,text);
text = QString("%1").arg(pointData.Pitch,0,'f',shAppConfig->angleDecimalPlacesIndex());
pWord->setTableFont(tableIndex,j+2,2,"SimSun",10,false,false,false);
pWord->setCellString(tableIndex,j+2,2,text);
text = QString("%1").arg(pointData.Roll,0,'f',shAppConfig->angleDecimalPlacesIndex());
pWord->setTableFont(tableIndex,j+2,3,"SimSun",10,false,false,false);
pWord->setCellString(tableIndex,j+2,3,text);
text = QString("%1").arg(pointData.Yaw,0,'f',shAppConfig->angleDecimalPlacesIndex());
pWord->setTableFont(tableIndex,j+2,4,"SimSun",10,false,false,false);
pWord->setCellString(tableIndex,j+2,4,text);
}
//移动到结尾
pWord->moveEnd();
//换页
pWord->skipPage();
//保存
pWord->saveAs(m_saveWordFilePath);
访问excel的对象模型
bool custom_excelEngine::open()
{
//先关闭再打开
close();
//为当前线程初始化windows com的运行环境并指定当前线程的并发模型
//COINIT_APARTMENTTHREADED , COINIT_MULTITHREADED
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr)){
m_comInitialized = true;
}
QStringList progIds = {
"Excel.Application",
"ket.Application",
"et.Application",
};
//创建一个com对象
m_excelObject = new QAxObject();
bool flag = false;
for(const QString& progId : progIds){
flag = m_excelObject->setControl( progId );
if(flag){
break;
}else{
std::string id = progId.toStdString();
saError("Unable to start document:%s",id.c_str());
}
}
if(!flag){
if(m_excelObject != nullptr){
delete m_excelObject;
}
return false;
}
//是否显示窗体
m_excelObject->setProperty("Visible", m_visable);
//不显示任何警告信息。如果为true那么在关闭是会出现类似“文件已修改,是否保存”的提示
m_excelObject->setProperty("DisplayAlerts", false);
//获取工作簿集合
QAxObject* workbooks = m_excelObject->querySubObject("WorkBooks");
if(!workbooks){
saError("Unable to get working excel");
QAxObject *lastError = m_excelObject->querySubObject("GetLastError()");
if(lastError){
QString error = lastError->dynamicCall("Description()").toString();
saError( "COM Error: %s",error.toStdString().c_str());
delete lastError;
}
return false;
}
//新建一个工作簿
workbooks->dynamicCall("Add()");
//获取当前工作簿
m_workbook = m_excelObject->querySubObject("ActiveWorkBook");
//获取工作表集合
QAxObject *worksheets = m_workbook->querySubObject("Sheets");
//获取工作表集合的工作表1,即sheet1
m_worksheet = worksheets->querySubObject("Item(int)", 1);
m_opened = true;
return m_opened;
}
/bool custom_excelEngine::saveAs(const QString &strExcelFilePath)
{
//注意一定要用QDir::toNativeSeparators将路径中的"/"转换为"\\",不然一定保存不了。
QVariant result = m_workbook->dynamicCall("SaveAs(const QString&)", QDir::toNativeSeparators(strExcelFilePath));
return result.toBool();
}
bool custom_excelEngine::close()
{
//如果文档打开中
if(m_opened){
//关闭文档窗口
if(m_workbook)
m_workbook->dynamicCall("Close (bool)", true);
//退出word
if(m_excelObject)
m_excelObject->dynamicCall("Quit()");
//释放对象
if(m_excelObject){
delete m_excelObject;
m_excelObject = nullptr;
}
m_opened = false;
//反初始化
if(m_comInitialized){
CoUninitialize();
m_comInitialized = false;
}
}
return m_opened;
}
//往某行某列写入数据
void custom_excelEngine::setCellString(int row, int column, const QString &value)
{
QAxObject *range = m_worksheet->querySubObject("Cells(int,int)", row, column);
range->dynamicCall("SetValue(const QString&)", value);
}
//往指定范围写入数据
void custom_excelEngine::setCellString(const QString &cell, const QVariant &value)
{
QAxObject *range = m_worksheet->querySubObject("Range(const QString&)", cell);
range->setProperty("NumberFormatLocal","@"); //设置所有单元格为文本属性
range->setProperty("Value2", value);
}
std::unique_ptr<custom_excelEngine> pExcel(new custom_excelEngine(this));
pExcel->setVisable(false);
if(!pExcel->open()){
saError("Unable to open excel");
emit signals_save_failed();
return;
}
//表格标题
QList<QVariant> excelTitles;
excelTitles.append(tr("序号"));
excelTitles.append(tr("俯仰角(°)"));
excelTitles.append(tr("翻滚角(°)"));
excelTitles.append(tr("方位角(°)"));
excelTitles.append(tr("测试深度(m)"));
//以二维数组的形式存储预写入数据
QList<QList<QVariant>> testingDatas;
//添加标题
testingDatas.append(excelTitles);
for (int i = 0;i < m_fileInfoList.size();i++){
SH::FileINFO fileInfo = m_fileInfoList.at(i);
QString filePath = QString("%1/%2").arg(fileInfo.folderPath).arg(fileInfo.fileName);
FileOperate fileOperate;
if(!fileOperate.readFile(filePath,m_fileData)){
continue;
}
for(int j = 0;j<m_fileData.testPointData.size();j++){
//实测数据表格
QList<QVariant> testingCols;
testingCols.append(QString("%1").arg(j));
testingCols.append(QString("%1").arg(m_fileData.testPointData.at(j).Pitch,0,'f',2));
testingCols.append(QString("%1").arg(m_fileData.testPointData.at(j).Roll,0,'f',2));
testingCols.append(QString("%1").arg(m_fileData.testPointData.at(j).Yaw)),0,'f',2));
testingCols.append(QString("%1").arg(m_fileData.testPointData.at(j).Deep,0,'f',2));
testingDatas.append(testingCols);
}
}
//单个写入数据
pExcel->mergeCells(1,1,1,11);
pExcel->setCellString(1,1,tr("数据表格"));
//二维数组转一维转变量
QList<QVariant> vars;
for(auto v:testingCols){
vars.append(QVariant(v));
}
QVariant var = QVariant(vars);
//获得指定范围
QString cell;
cell.append(QChar('A'));
cell.append(QString::number(2));
cell.append(QString(":"));
cell.append(QChar::fromLatin1(excelTitles.size() – 1 + 'A'));
cell.append(QString::number(designDatas.size() + 1));
//往指定范围类批量写入数据
pExcel->setCellString(cell,var);
//保存
pExcel->saveAs(m_saveExcelFilePath);
注意:往Excel表格中写入数据时,如果逐一单元格的写入会异常的缓慢,这是由于每次写入都要跨进程通信调用excel进程。所以推荐将数据包装成变量QVariant,一次性写入,避免频繁跨进程调用。


