Qt5生成Word格式报告
- 引言
- 一使用ActiveQt模塊
- 二子線程中使用
- 三準備word模板
- 四代碼
- 插入書簽位置
- 批量插入
- 插入表格
- 方法一利用Range對象定位后插入表格
- 方法二利用bookmark定位后插入表格
- 五其他
- 參考
引言
項目中需要生成word格式的報告文件,初探了Qt5通過word模板生成報告的方法,整理了使用時的環境配置、子線程中使用時的注意事項以及常用的操作方法,于此記錄。
環境:vs2012+Qt5.2+word2016
一、使用ActiveQt模塊
注意:ActiveQt只適用于windows平臺下,linux和macOS版本的Qt中是沒有這個庫的
首先需要添加庫文件,可以直接在VS2012菜單欄Qt5->Qt Project Setting->勾選Active Qt
勾選之后再次查看Qt Project Setting,發現自動勾上了Active Qt server,同時“項目屬性->配置屬性->鏈接器->輸入->附加依賴性”中自動加入了Qt5AxContainerd.lib;Qt5AxBased.lib
此后便可成功include頭文件
二、子線程中使用
在使用過程中發現調用word過程比較耗時,會阻塞GUI線程,于是將保存報告操作移到子線程中.
不過在子線程中使用QAxWidget會報錯ASSERT failure in QWidget: "Widgets must be created in the GUI thread."這是由于線程里面不能創建GUI對象。
解決方案是用 QAxObjec取代QAxWidget,初始化過程如下:
同時由于在QApplication的主線程中,會自動初始化COM庫,而新開辟的子線程不會自動初始化COM庫,所以需要我們手動來初始化,方法如下:
添加頭文件:
構造函數中初始化COM庫:
Report::Report(QObject *parent): QObject(parent) {HRESULT result = OleInitialize(0);if (result != S_OK && result != S_FALSE){qDebug()<<QString("Could not initialize OLE (error %x)").arg((unsigned int)result);}//moveToThread方法產生線程this->moveToThread(&m_thread);m_thread.start(); }析構函數中釋放:
Report::~Report() {OleUninitialize();m_thread.quit();m_thread.wait(); }三、準備word模板
在word文檔中手動添加書簽(bookmark)后保存為dot格式
四、代碼
1.插入書簽位置
QString outFileName = QFileDialog::getSaveFileName(this, QStringLiteral("請輸入要保存的名字:"),".", "Microsoft Word 97-2003(*.doc);;Microsoft Word 2007-2013(*.docx)");if (outFileName.isEmpty()) {QMessageBox::warning(this, tr("警告"),tr("輸入的文件名為空!"),QMessageBox::Ok);return ;}// 新建一個word應用程序,并設置為不可見 QAxWidget *word=new QAxWidget("Word.Application", 0, Qt::MSWindowsOwnDC); word->setProperty("Visible", false); // 獲取所有的工作文檔 QAxObject * documents = word->querySubObject("Documents");// 以文件testTemplate.dot為模版新建一個文檔,注意這里的路徑為絕對路徑QDir dir(".");documents->dynamicCall("Add(QString)",QString("%1/testTemplate.dot").arg(dir.absolutePath())); // 獲取當前激活的文檔 QAxObject *document=word->querySubObject("ActiveDocument"); // 獲取文檔中名字為TSName_1_1的標簽 QString bookmakrName="TSName_1_1";QAxObject*bookmark_text=document->querySubObject(QString("Bookmarks(%1)").arg(bookmakrName).toLocal8Bit().data()); // 選中標簽,將字符插入到標簽位置 if(!bookmark_text->isNull()) { bookmark_text->dynamicCall("Select(void)"); bookmark_text->querySubObject("Range")->setProperty("Text",QStringLiteral("測試輸入")); } // 將文件另存為outFileName,關閉工作文檔,退出應用程序 document->dynamicCall("SaveAs (const QString&)", outFileName); document->dynamicCall("Close (boolean)", true); //關閉文本窗口word->dynamicCall("Quit(void)"); //退出worddelete bookmark_text; delete document; delete documents; delete word;2.批量插入
QString outFileName = QFileDialog::getSaveFileName(this, QStringLiteral("請輸入要保存的名字:"),".", "Microsoft Word 97-2003(*.doc);;Microsoft Word 2007-2013(*.docx)");if (outFileName.isEmpty()) {QMessageBox::warning(this, tr("警告"),tr("輸入的文件名為空!"),QMessageBox::Ok);return ;}word = new QAxWidget("Word.Application", 0, Qt::MSWindowsOwnDC);word->setProperty("Visible", false);word->setProperty("DisplayAlerts", true);QAxObject *docs = word->querySubObject("Documents");if (!docs) {QMessageBox::warning(this, tr("警告"), tr("無法獲得Documents對象!"),QMessageBox::Ok);return ;}QStringList items;QStringList sometexts;//items按順序依次是“待匹配標簽名”和“插入的內容”items<<"TSName_1_2"<<"TSName222"<<"TSName_1_1"<<"TSName111"<<"555";sometexts<<"111"<<"222"<<"333"<<"444"<<"555";editBookMarks(docs, sometexts, items, outFileName);word->dynamicCall("Quit(boolean)", true);delete word; void testword::editBookMarks(QAxObject *docs, QStringList sometexts, QStringList &itemList, QString outFileName) {QDir dir(".");docs->dynamicCall("Add(QString)", QString("%1/testTemplate.dot").arg(dir.absolutePath()));QAxObject *currentDoc = word->querySubObject("ActiveDocument");if(!currentDoc){QMessageBox::warning(this, QStringLiteral("警告"), QStringLiteral("無法獲取當前打開文件對象!"),QMessageBox::Ok);return;}QAxObject *allBookmarks = currentDoc->querySubObject("Bookmarks");if (!allBookmarks) {QMessageBox::warning(this, QStringLiteral("警告"), QStringLiteral("無法獲取模板中的書簽,請先插入書簽!"), QMessageBox::Ok);return ;}int count = allBookmarks->property("Count").toInt();/* 填寫模板中的書簽 */for (int i = count; i > 0; --i) {QAxObject *bookmark = allBookmarks->querySubObject("Item(QVariant)", i);QString name= bookmark->property("Name").toString();int j=0;foreach(QString itemName , itemList){if (name == itemName) {QAxObject *curBM = currentDoc->querySubObject("Bookmarks(QString)", name);curBM->querySubObject("Range")->setProperty("Text", itemList.at(j+1));break;}j++;}if (j == itemList.length()) {//如果遍歷itemList,未找到匹配的書簽,提示輸入QString text = QInputDialog::getText(this, QStringLiteral("請輸入"), QStringLiteral("%1").arg(name));bookmark->querySubObject("Range")->setProperty("Text", text);itemList.append(name);itemList.append(text);}}//依次插入sometexts中內容while(!sometexts.isEmpty()){QAxObject *currentRange = currentDoc->querySubObject("Range()");int rangeEnd = currentRange->property("End").toInt();currentRange->dynamicCall("setRange(QVariant, QVariant)", rangeEnd, rangeEnd);currentRange->dynamicCall("InsertAfter(QString)", QStringLiteral("\n%1-%3\n").arg(sometexts[0]).arg(1));sometexts.removeAt(0);}currentDoc->dynamicCall("SaveAs(QString&)", outFileName);currentDoc->dynamicCall("Close()"); }效果如下:
3.插入表格
方法一:利用Range對象定位后插入表格
/******************************************************************************* 函數:intsertTable* 功能:創建表格* 參數:nStart 開始位置; nEnd 結束位置; row hang; column 列* 返回值: void*****************************************************************************/ void WordEngine::intsertTable(int nStart, int nEnd, int row, int column) { QAxObject* ptst = m_wordDocuments->querySubObject( "Range( Long, Long )",nStart, nEnd );QAxObject* pTable = m_wordDocuments->querySubObject( "Tables" );QVariantList params;params.append(ptst->asVariant());params.append(row);params.append(column);if( pTable ){pTable->dynamicCall( "Add(QAxObject*, Long ,Long )",params);} // QAxObject* table = selection->querySubObject("Tables(1)"); // table->setProperty("Style", "網格型"); }方法二:利用bookmark定位后插入表格
QAxObject *WordEngine::insertTable(QString sLabel, int row, int column) { QAxObject *bookmark = m_pWorkDocument->querySubObject("Bookmarks(QVariant)", sLabel); if(bookmark) { bookmark->dynamicCall("Select(void)"); QAxObject *selection = m_pWord->querySubObject("Selection"); selection->dynamicCall("InsertAfter(QString&)", "\n"); //selection->dynamicCall("MoveLeft(int)", 1); selection->querySubObject("ParagraphFormat")->dynamicCall("Alignment", "wdAlignParagraphCenter"); //selection->dynamicCall("TypeText(QString&)", "Table Test");//設置標題 QAxObject *range = selection->querySubObject("Range"); QAxObject *tables = m_pWorkDocument->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.toAscii().constData()); borders->dynamicCall("SetLineStyle(int)",1); } return table; } }插入表格,修改列寬等是后來看到的,可參考這個github項目中的WordEngine實現
五、其他
用以下方法往bookmark插入內容:
QString bookmakrName="TSName1_1_2";//假設dot文件中并沒有這個bookmarkQAxObject* bookmark_text=document->querySubObject("Bookmarks(const QString&)", bookmakrName); if(NULL == bookmark_text)//注意這個判斷不可少,否則下面調用isNull()時會出錯{return;}// 選中標簽,將字符插入到標簽位置 if(!bookmark_text->isNull()) //如果沒有匹配到對應的bookmark,直接判斷會出錯,所以要提前返回{ bookmark_text->dynamicCall("Select(void)"); bookmark_text->querySubObject("Range")->setProperty("Text",QStringLiteral("測試輸入")); }如果dot文件中并沒有這個bookmark,會報如下錯誤:
QAxBase: Error calling IDispatch member Bookmarks: Exception thrown by serverCode : 5941Source : Microsoft WordDescription: ????????????Help : wdmain11.chm [25421]Connect to the exception(int,QString,QString,QString) signal to catch this exception這時判斷NULL == bookmark_text返回即可,如果要避免匹配不存在的bookmark,可以在前面處理,比如上文“2.批量插入”中所示的先利用QAxObject *allBookmarks = currentDoc->querySubObject("Bookmarks");獲取所有Bookmarks,然后在進行處理。
最后附上相關源碼:demo-Qt5生成Word格式報告(demo是最開始寫的,未包含插入表格,多線程等方法實現,比較簡單)
參考
Qt利用ActiveX生成Word文檔
qt中如何使用ActiveX讀寫word
github-試卷自動生成系統
github-QTScada
QT在子線程中使用QAxWidget需要初始化COM的問題
總結
以上是生活随笔為你收集整理的Qt5生成Word格式报告的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux ppsspp速度,PPSSP
- 下一篇: seo相关文章