整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript 數組進行拼接的函數

          時候,我們希望在 JavaScript 中將 2 個已經存在的數組串拼接成 1 個數組。

          簡單來說就是將數組進行合并。

          這個時候,我們可以使用 JavaScript 的 concat 函數。

          考察下面的代碼:

          const array1 = ['a', 'b', 'c'];
          const array2 = ['d', 'e', 'f'];
          const array3 = array1.concat(array2);
          
          console.log(array3);
          // expected output: Array ["a", "b", "c", "d", "e", "f"]
          
          

          上面的代碼將 2 個數組 array1 和 array2 合并成了一個新的數組為 array3, 在這個新的數組中的元素就是
          array2 在 array1 后面添加得到的。

          如果你需要合并的數組不只有 2 個,你還有多個的話,你可以同樣使用上面的方法,但是在參數中傳遞進多個數組就行。

          concat()
          concat(value0)
          concat(value0, value1)
          concat(value0, value1, ... , valueN)
          
          

          如上面的代碼,你并不需要將 concat 多次運行來進行合并,concat 這個方法允許傳遞多個需要合并數組為參數。

          網盜概念^-^相同的測試腳本使用不同的測試數據來執行,測試數據和測試行為完全分離, 這樣的測試腳本設計模式稱為數據驅動。(網盜結束)當我們測試某個網站的登錄功能時,我們往往會使用不同的用戶名和密碼來驗證登錄模塊對系統的影響,那么如果我們每一條數據都編寫一條測試用例,這無疑是增加了代碼量,代碼重復,且顯得那么臃腫(誰不喜歡身材好的呢?你懂的),這時候我們可以使用不同數據驅動代碼執行相同的用例測試不同的場景。

          一、實施數據驅動步驟

          我們再來說說實施數據驅動測試的步驟:

          1.創建/準備測試數據

          2.封裝讀取數據的方法,保留測試腳本調用的接口/屬性(我們需要傳遞給腳本什么參數)

          3.編寫自動化測試腳本

          4.腳本中調用封裝好的處理數據文件的模塊并引入測試數據

          5.執行測試腳本并分析測試結果

          二、數據驅動測試環境準備

          1.安裝python3.x開發環境(能看到此文章的應該都有這個環境,沒有的自行百度吧)

          2.安裝數據驅動模塊ddt

          安裝方式1:cmd下執行命令 pip install ddt

          安裝方式2:https://pypi.org/simple/ddt/ 下載 并解壓任意目錄,cmd 運行命令python setup.py install

          3.驗證安裝 pycharm 新建python文件并輸入 import ddt 運行無報錯信息既表示安裝成功或者cmd 命令依次輸入python回車 import ddt回車 無保存信息表示安裝成功

          4.unittest框架和ddt進行數據驅動

          三、測試步驟

          1.訪問地址:https://mail.sohu.com/fe/#/login

          2.輸入用戶名和密碼

          3.點擊登錄按鈕

          4.判斷是否登錄成功

          四、數據存儲

          數據存在當前腳本中

          數據準備

          我們要實現的是用戶登錄的操作,所以用戶名和密碼是必須有的,期望結果可以有也可以沒有。數據類型看源代碼!

          五、實例代碼 

           1 from selenium import webdriver
           2 from ddt import ddt, data, unpack
           3 import unittest
           4 import time
           5 from selenium.common.exceptions import NoSuchWindowException
           6 '''
           7 簡單數據驅動測試
           8 '''
           9 @ddt
          10 class ddtTest(unittest.TestCase):
          11     # 數據 可以是元祖, 列表, 字典(可迭代對象)
          12     value = [['12691569846@sohu.com', 'xiacha11520','https://mail.sohu.com/fe/#/homepage'],
          13              ['12691569844@sohu.com', 'xiacha11520','https://mail.sohu.com/fe/#/homepage']]
          14     # value = [{'uname':'******@sohu.com', 'password':'xiacha11520','expected':'https://mail.sohu.com/fe/#/homepage'},
          15     #          {'uname':'******@sohu.com', 'password':'xiacha11520','expected':'https://mail.sohu.com/fe/#/homepage'}]
          16     def setUp(self):
          17         self.testUrl = 'https://mail.sohu.com/fe/#/login'
          18         self.driver = webdriver.Firefox()
          19         self.driver.get(self.testUrl)
          20 
          21     @data(*value) # * 解析數據
          22     @unpack# 用來解包, 將每組數據的第一個數據傳遞給uname依次類推, 當數據為字典時,形參需和字段的key值相同
          23     def test_case1(self, uname, password, expected):
          24         try:
          25             username = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的郵箱']")
          26             username.send_keys(uname)
          27             time.sleep(1)
          28             userpassword = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的密碼']")
          29             userpassword.send_keys(password)
          30             self.driver.find_element_by_xpath("//input[@type='submit']").click()
          31             time.sleep(2)
          32             currenturl = self.driver.current_url
          33             self.assertEqual(expected, currenturl,'登錄失敗')
          34         except NoSuchWindowException as e:
          35             print(e)
          36             raise
          37         except AssertionError:
          38             print('期望值是{}, 實際值是{}'.format(expected,currenturl))
          39             raise
          40         except Exception:
          41             raise
          42     def tearDown(self):
          43         self.driver.quit()
          44         # pass
          45 if __name__ == '__main__':
          46     unittest.main()

          六、源碼分析

          1.@ddt來裝飾測試類(ddt數據驅動的規范寫法,記住就ok)

          2.@data(*value)裝飾測試用例(也是一種規范,這邊又涉及到裝飾器,不懂的可以百度,這邊不再贅述,一句話兩句話也說不清楚)記住:*value作用是打散數據,比如上面代碼是用一個大列表存儲兩個小列表存放數據的,那么*value會得到兩個小列表,每個小列表是一組測試數據

          3.@unpack 解析*value數據,會把兩個小列表里面的每一個數據取出來分別傳遞給我們測試用例的形參

          方式1缺點

          存儲大量數據時,需查看源代碼,不利于腳本的維護    

          七、json文件讀取測試數據進行數據驅動測試

          數據準備

          新建一個json文件(也可以是txt文件),將我們需要的兩組測試數據以列表的形式寫到json文件中,每組數據的每一項參數用相同的符號分割開(方便腳本讀取數據)

          實例代碼

          {
            "value1":"******@sohu.com||xiacha11520||https://mail.sohu.com/fe/#/homepage",
            "value2":"******@sohu.com||xiacha11520||https://mail.sohu.com/fe/#/homepage"
          }
           1 from selenium import webdriver
           2 from ddt import ddt, file_data
           3 import unittest, time
           4 from selenium.common.exceptions import NoSuchWindowException
           5 import HTMLTestRunner
           6 '''
           7 從文件中讀測試數據
           8 '''
           9 
          10 @ddt # ddt裝飾測試類
          11 class Testdata(unittest.TestCase):
          12 
          13     def setUp(self):
          14         self.driver = webdriver.Firefox()
          15         self.driver.get('https://mail.sohu.com/fe/#/login')
          16 
          17     @file_data('test_data.json') # 讀取文件的 文件中數據可以是一個列表,也可以是一個字典
          18     def test_data(self,value):
          19         uname, password, expected = tuple(value.strip().split('||')) # value是一個字符串
          20         # print(type(value),value)
          21         try:
          22             username = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的郵箱']")
          23             username.send_keys(uname)
          24             time.sleep(1)
          25             userpassword = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的密碼']")
          26             userpassword.send_keys(password)
          27             self.driver.find_element_by_xpath("//input[@type='submit']").click()
          28             time.sleep(2)
          29             currenturl = self.driver.current_url
          30             self.assertEqual(expected, currenturl,'登錄失敗')
          31         except NoSuchWindowException as e:
          32             raise e
          33         except AssertionError:
          34             print('期望值是{}, 實際值是{}'.format(expected,currenturl))
          35             raise
          36         except Exception:
          37             raise
          38 
          39     def tearDown(self):
          40         self.driver.quit()
          41 if __name__ == '__main__':
          42     unittest.main()
          43     # import os
          44     # from datetime import date
          45     # currentPath = os.path.dirname(os.path.abspath(__file__))# 獲取當前文件目錄
          46     # reportPath = os.path.join(currentPath,'report') # 創建一個report目錄
          47     # if not os.path.exists(reportPath):
          48     #     os.mkdir(reportPath) # 判斷目錄是否存在, 不存在就創建
          49     # reportName = os.path.join(reportPath, str(date.today())+'report.html') # 拼接html報告
          50     # with open(reportName,'wb') as f:
          51     #     suite = unittest.TestLoader().loadTestsFromTestCase(Testdata)
          52     #     runner = HTMLTestRunner.HTMLTestRunner(stream=f,verbosity=1, title='數據驅動測試報告', description='數據驅動')
          53     #     runner.run(suite)

          源碼分析

          1.相對上個實例,這里使用了@file_data(文件路徑), 參數必須是一個文件,這里是一個json文件, 數據可以是一個列表,也可以是一個字典

          # 列表形式
          [
            "*******@sohu.com||xiacha11520||https://mail.sohu.com/fe/#/homepage",
            "*******@sohu.com||xiacha11520||https://mail.sohu.com/fe/#/homepage"
          ]

          2.測試用例接收的是一個字符串,需要對字符串進行處理,把用戶名,密碼,期望值解析出來

          方式2優缺點

          測試數據存在文件中,方便管理修改,添加數據,易于維護,缺點呢?emmm個人認為這種方式最好!

          八、從xml讀取數據進行數據驅動測試

          數據準備

          新建一個xml格式的文件,按照xml格式的語法需求,填寫數據(xml文檔我也不是很懂,簡單的顯示個文字啥的還可以^-^!)

          <?xml version="1.0"?>
          <bookList type="technolog">
              <book>
                  <uname>******@sohu.com</uname>
                  <password>xiaochao11520</password>
                  <expected>https://mail.sohu.com/fe/#/homepage</expected>
              </book>
              <book>
                  <uname>******@sohu.com</uname>
                  <password>xiaochao11520</password>
                  <expected>https://mail.sohu.com/fe/#/homepage</expected>
              </book>
          </bookList>

          實例代碼

           1 from xml.etree import ElementTree
           2 
           3 class ParseXml(object):
           4     def __init__(self, xmlpath):
           5         self.xmlpath = xmlpath
           6 
           7     # 獲取根節點
           8     def getRoot(self):
           9         tree = ElementTree.parse(self.xmlpath)
          10         root = tree.getroot()
          11         return root
          12 
          13     # 根據根節點查找子節點
          14     def findNodeByName(self, parentNode, nodeName):
          15         nodes = parentNode.findall(nodeName)
          16         return nodes
          17 
          18     def getNodeOfChildText(self, node):
          19         # 獲取節點node下所有子節點的節點名作為key
          20         # 本節點作為value組成的字典對象
          21         childrenTextDict = {}
          22         for i in list(node.iter())[1:]: # node 節點下的所有節點組成的列表
          23             childrenTextDict[i.tag] = i.text
          24         # print(list(node.iter())[1:])
          25         return childrenTextDict
          26 
          27     # 獲取節點node下面的節點的所有數據
          28     def getDataFromXml(self, node):
          29         root = self.getRoot()
          30         books = self.findNodeByName(root, node)
          31         dataList=[]
          32         for book in books:
          33             childrentext = self.getNodeOfChildText(book)
          34             dataList.append(childrentext)
          35         return dataList
          36 if __name__=='__main__':
          37     xml = ParseXml('./xmlData.xml')
          38     root = xml.getRoot()
          39     print(root.tag)
          40     books = xml.findNodeByName(root, 'book') # 查找所有的book節點
          41     for book in books:
          42         # print(book[0].tag, book[0].text)
          43         print(xml.getNodeOfChildText(book))
          44     print(xml.getDataFromXml('book'))
           1 from dataDdt.doXML import ParseXml
           2 from selenium import webdriver
           3 from selenium.common.exceptions import NoSuchWindowException, TimeoutException
           4 import unittest
           5 from ddt import ddt, data,unpack
           6 import time
           7 from selenium.webdriver.support import expected_conditions as EC
           8 from selenium.webdriver.support.ui import WebDriverWait
           9 from selenium.webdriver.common.by import By
          10 values = ParseXml('./xmlData.xml')
          11 @ddt
          12 class xmltest(unittest.TestCase):
          13 
          14     def setUp(self):
          15         self.driver = webdriver.Firefox()
          16         self.driver.get('https://mail.sohu.com/fe/#/login')
          17     @data(*values.getDataFromXml('book'))
          18     @unpack
          19     def test_xml(self,uname, password, expected):
          20         try:
          21             wait = WebDriverWait(self.driver,5)
          22             wait.until(EC.element_to_be_clickable((By.XPATH, "//input[@type='submit']")))
          23             username = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的郵箱']")
          24             username.send_keys(uname)
          25             time.sleep(1)
          26             userpassword = self.driver.find_element_by_xpath("//input[@placeholder='請輸入您的密碼']")
          27             userpassword.send_keys(password)
          28             self.driver.find_element_by_xpath("//input[@type='submit']").click()
          29             time.sleep(2)
          30             currenturl = self.driver.current_url
          31             self.assertEqual(expected, currenturl, '登錄失敗')
          32         except TimeoutException as e:
          33             raise e
          34         except NoSuchWindowException as e:
          35             raise e
          36         except AssertionError as e:
          37             print('期望值是{}, 實際值是{}'.format(expected, currenturl))
          38             raise e
          39         except Exception:
          40             raise
          41     def tearDown(self):
          42         self.driver.quit()
          43 
          44 if __name__=='__main__':
          45     unittest.main()

          源碼分析

          1.xml文檔編寫(深入了解需百度)有點像html,但又有不同,xml中的節點可以是任意名稱,每個節點同樣是成雙出現

          2.增加了doXML.py文檔,用來解析xml文件,方便腳本獲取數據(注釋寫的很詳細,不懂的化可以慢慢調試,哪里不懂print哪里)

          3.測試腳本和上面的實例大致相同

          方式3優缺點

          優點是做到了數據與測試的分離,方便數據維護,缺點也比較明顯,需要對xml文檔有一定的了解

          總結

          上面的數據驅動測試步驟是我自己總結的,看了上面的實例對于步驟應該還算合理,下面是我在網上找到的數據驅動測試步驟(感覺比較官方!大家可以參考)

          1.編寫測試腳本, 腳本需要支持從程序對象, 文件,或者數據庫讀入數據。(個人觀點:如果腳本先編寫完,測試數據還未準備,后期還要做修改)

          2.將測試腳本使用的測試數據存入程序對象,文件,或者數據庫等外部介質中。(個人觀點:這個階段實為準備數據的階段,也就是我們數據要存在哪里,理應放在第一步)

          3.運行腳本過程中,循環調用存儲在外部介質中的測試數據。(個人觀點:這里要考慮我們如何讀取,使用數據)

          4.驗證所有的測試結果是否符合預期結果

          最后今天的文章就到這里了,喜歡的可以點贊收藏加關注。

          閱讀本文大概需要 6 分鐘

          日常開發軟件可能會遇到這類小眾需求,導出數據到 WordExcel 以及 PDF文件,如果你使用 C++ 編程語言,那么可以選擇的方案不是很多,恰好最近剛好有這部分需求,整理下這段時間踩過的坑,方便后人

          讀寫 Word

          日常開發的軟件使用最多的應該是導出數據到 Word 文檔中,目前可以用的方案有這幾種

          沒有十全十美的方案,任何方案都存在優點和缺點,下面來詳細看下這幾種方案的優缺點以及適用場景

          XML 模板替換

          原理:事先編輯好一份 Word 模板,需要替換內容的 地方預留好位置,然后使用特殊字段進行標記,后面使用代碼進行全量替換即可完成

          優點

          • 代碼量相對較少、導出速度快
          • 跨平臺,支持多個系統,系統不安裝 office 也能導出;
          • 支持圖片以及固定格式導出;

          缺點

          • 導出格式固定,可擴展性不強,如果需求變化導出格式變了,那么模板也要跟著改變;
          • 一種格式對應一份模板,如果導出格式較多,需要準備的模板文件較多,這樣比較繁瑣;
          • 需要 Word 2003 以上版本;

          舉個栗子

          我們先編輯一份 Word 模板文檔,內容大概如下所示:

          • 將該文檔另存為 Word XML 文檔 XML-Template.xml
          • 讀取文檔內容進行變量替換
              QFile file("XML-Template.xml");
              if (!file.open(QIODevice::ReadOnly))
              {
                  qDebug() << "open xxml file fail. " << file.errorString();
                  return 0;
              }
              QByteArray baContent = file.readAll();
              file.close();
              QString strAllContent = QString::fromLocal8Bit(baContent);
          
              strAllContent.replace("$VALUE0", "1");
              strAllContent.replace("$VALUE1", QString::fromLocal8Bit("法外狂徒張三"));
              strAllContent.replace("$VALUE2", QString::fromLocal8Bit("考試不合格"));
              strAllContent.replace("$VALUE3", "2");
              strAllContent.replace("$VALUE4", QString::fromLocal8Bit("李四"));
              strAllContent.replace("$VALUE5", QString::fromLocal8Bit("合格"));
          
              QFile newFile("export.doc");
              if (!newFile.open(QIODevice::WriteOnly))
              {
                  qDebug() << "file open fail." << newFile.errorString();;
                  return 0;
              }
          
              newFile.write(strAllContent.toLocal8Bit());
              newFile.close();
          
          • 保存替換后的內容,寫入文件

          可以看出來這種方式比較繁瑣,重點是編輯固定的模板格式,而且編輯好后保存成XML格式后還需要繼續調整,這種 XML 格式標簽很多,不小心就修改錯了,導致導出的文檔打不開

          這種方式適合模板內容不太復雜,內容較少的情況下使用

          COM 組件方式

          原理:采用 Micro Soft公開的接口進行通訊,進行讀寫時會打開一個 `Word進程來交互

          COM 技術概述

          Qt 為我們提供了專門進行交互的類和接口,使用 Qt ActiveX框架就可以很好的完成交互工作

          優點

          • 實現簡單,快速上手;

          缺點

          • 導出寫入速度慢,因為相當于打開 word 文檔操作;
          • Windows平臺可用,其它平臺失效;
          • 需要程序運行的電腦安裝 office word,否則調用失敗

          舉個栗子

          使用時需要引入對應的模塊,在 pro 文件引入模塊

          QT  *= axcontainer
          

          打開文檔寫入內容

          QAxObject *pWordWidget = new(std::nothrow) QAxObject;
          
          bool bResult = pWordWidget->setControl("word.Application");
          
          if (!bResult)
          {
              return false;
          }
          
          // 設置是否顯示
          pWordWidget->setProperty("Visible", false);
          
          QAxObject *pAllDocuments = pWordWidget->querySubObject("Documents");
          
          if(nullptr == pAllDocuments)
          {
              return false;
          }
          
          // 新建一個空白文檔
          pAllDocuments->dynamicCall("Add (void)");
          
          // 獲取激活的文檔并使用
          QAxObject *pActiveDocument = pAllDocuments->querySubObject("ActiveDocument");
          if(nullptr == pActiveDocument)
          {
              return false;
          }
          
          // 插入字符串
          QAxObject *pSelectObj = pWordWidget->querySubObject("Selection");
          if (nullptr != pSelectObj)
          {
              pSelectObj->dynamicCall("TypeText(const QString&)", "公眾號:devstone");
          }
          
          ……
          

          可以看出來使用起來不難,對于新手友好一點,很多寫入操作方法比較繁瑣,需要自己重新封裝一套接口

          • 這種方案比較適合那些排版比較復雜,圖片、文字、表格混排的場景下,而且內容都是動態變化的,可以很好的實現定制化
          • 當然了它的缺點也不少,也有一些坑,有時候莫名其妙會失敗,還有就是比如你電腦安裝的 Word 沒有激活,那么每次啟動會彈激活窗口
          • 還有就是這種方式要求所有的路徑必須是本地化的,比如 D:\Soft\test.png
          • 使用前最好讀取注冊表判斷當前電腦是否安裝了 Office Word,如果沒有安裝,直接讀取操作肯定會崩潰

          這種方式同樣適用于寫入 Excel 文件,后面再說

          HTML 方式

          原理:這種方式得益于 Word支持 HTML格式導出渲染顯示,那么反向也可以支持,需要我們拼接 HTML格式內容,然后寫入文件保存成 .doc格式

          優點

          • 跨平臺,不僅限于 Windows平臺,代碼可擴展性比較好
          • 導出速度快、代碼可擴展;

          缺點

          • 字符串拼接 HTML 容易出錯,缺失標簽導出后無法顯示;
          • 插入的圖片是本地圖片文件的鏈接,導出的 word文檔拷貝到其它電腦圖片無法顯示

          舉個栗子

          QString HTML2Word::getHtmlContent()
          {
              QString strHtml = "";
              strHtml += "<html>";
              strHtml += "<head>";
              strHtml += "<title>測試生成word文檔</title>";
              strHtml += "<head>";
              strHtml += "<body style=\"bgcolor:yellow\">";
              strHtml += "<h1 style=\"background-color:red\">測試qt實現生成word文檔</h1>";
              strHtml += "<hr>";
              strHtml += "<p>這里是插入圖片<img src=\"D:\\title.jpg" alt=\"picture\" width=\"100\" height=\"100\"></p>";
              strHtml += "</hr>";
              strHtml += "</body>";
              strHtml += "</html>";
          
              return strHtml;
          }
          
          // 保存寫入文件
          QFile file("D:/htmp2Word.doc");
          if (!file.open(QIODevice::WriteOnly))
          {
              return false;
          }
          
          QTextStream out(&file);
          out << getHtmlContent();
          file.close();
          
          

          這種方式難點在于 HTML格式拼接,任何缺失字段都會導致導出失敗,適合小眾需求下導出

          圖片問題其實可以手動進行轉化,文檔導出成功后手動拷貝內容到新的文檔,這樣圖片就真正插入到文檔中,文檔發送給別人也不會丟失圖片了

          還有一個坑就是:如果你使用 WPS 打開導出的文檔,默認顯示的是 web視圖,需要手動進行調整

          某些電腦分辨率變化也會導致生成的文檔中字體等產生變化

          第三方開源庫

          可以使用的第三方庫幾乎沒有,網絡上找到的有這么幾個

          • OpenOffice: 兼容性差,集成調用難度大
          • LibOffice: 太龐大,不容易集成
          • DuckX: 太小眾,只能簡單的使用
          • docx:小眾庫

          DuckX庫 docx庫

          在讀寫 Word這部分,C++ 基本沒有可以使用的第三方庫,不像其他語言JavaC#Python有很多可以選擇,這個痛苦也只有 C++ 程序員能夠理解了吧

          所以怎么選擇還是看自己項目需求吧,沒有十全十美的方案


          上面說了這么多,都是導出生成 Wrod,那么下面來看看有那些方式可以讀取顯示 Word內容

          這種需求應該不會很多,而且顯示難度更大一些

          使用 COM組件方式,即采用 QAxWidget框架顯示 office 文檔內容,本質上就是在我們編寫的 Qt 界面上嵌入 office 的軟件,這種方式其實和直接打開 Word查看沒有啥區別,效果、性能上不如直接打開更好一些

          目前一般都會采用折中方案,把 Word 轉為 PDF 進行預覽加載顯示,我們知道 PDF 渲染庫比較多,生態相對來說要好一些,在選擇上就更廣泛些,如何使用后面部分有專門介紹 PDF章節

          讀寫 Excel

          目前有一個支持比較好的第三方庫可以使用,整體使用基本可以滿足日常使用

          QXlsx

          這款開源庫支持跨平臺,Linux、Windows、Mac、IOS、Android,使用方式支持動態庫調用和源碼直接集成,非常方便

          編譯支持 qmakecmake,可以根據你自己的項目直接集成編譯,讀寫速度非常快

          QXlsx::Document xlsx;
          
          // 設置一些樣式
          QXlsx::Format titleFormat;
          titleFormat.setBorderStyle(QXlsx::Format::BorderThin);  // 邊框樣式
          titleFormat.setRowHeight(1,1,30);   // 設置行高
          titleFormat.setHorizontalAlignment(QXlsx::Format::AlignHCenter);   // 設置對齊方式
          
          // 插入文本
          xlsx.write(1,1, "微信公眾號:devstone", titleFormat);
          
          // 合并單元格
          xlsx.mergeCells(QXlsx::CellRange(2,1,4,4), titleFormat);
          
          // 導出保存
          xlsx.saveAs("D:/xlsx_export.xlsx");
          
          // 添加工作表
          xlsx.addSheet("devstone");
          

          可以看到上手非常容易、各個函數命名也貼近 Qt Api,是一款非常良心的開源軟件

          PS:注意該軟件使用 MIT 許可協議,這樣對于很多個人或者公司來說非常良心,意味著你可以無償使用、修改該項目,但是必須在你項目中也添加同樣的 MIP許可

          上面也提到了,還可以使用 COM 組件的方式讀寫 Excel,不過有了這款開源庫基本就可以告別 COM組件方式了

          讀寫 PDF

          PDF相關開源庫挺多的,給了 C++ 程序員莫大的幫助,目前可用的主要有這些

          其中 mupdfpoppler 屬于功能強大但是很難編譯的那種,需要有扎實的三方庫編譯能力,否則面對 n 個依賴庫會無從下手

          不過可喜的是 Github 上有兩個開源庫可以供選擇

          qpdf 庫

          這個庫其實封裝了 pdf.js庫,使用 WebEngine來執行 JavaScript進而加載文件

          項目地址

          • 直接從本地文件加載;
          • 支持從內存數據直接加載渲染 PDF 內容;

          這種方式對環境有特殊要求了,如果你的項目使用的 Qt 版本不支持 WebEngine,那么就無法使用

          qtpdf 庫

          這個庫是 Qt 官方親自操刀對第三方庫進行了封裝,暴露的 APIQt 類似,使用起來非常舒服

          Qt 官方

          代碼結構以及使用 Demo

          小試牛刀

          關于如何使用,官方已經給了我們非常詳細的步驟了,直接跟著下面幾步就 OK 了

          官方教程

          git clone git://code.qt.io/qt-labs/qtpdf
          cd qtpdf
          git submodule update --init --recursive
          qmake
          make
          cd examples/pdf/pdfviewer
          qmake
          make
          
          ./pdfviewer /path/to/my/file.pdf
          

          可以看到使用了谷歌開源的 pdfium 三方庫,編譯時需要單獨更新下載這個庫,因為某些原因可能你無法下載,不過好在有人在 GitHub上同步了這個倉庫的鏡像,有條件還是建議直接下載最新穩定版的

          可正常訪問的倉庫地址:https://github.com/PDFium/PDFium

          相關類可以看這個文檔:https://developers.foxit.com/resources/pdf-sdk/c_api_reference_pdfium/modules.html

          最后還要注意項目開源協議:pdfium引擎開始來自于福昕,一個中國本土的軟件公司,Google與其合作最終進行了開源,目前采用的是 BSD 3-Clause 協議,這種協議允許開發者自由使用、修改源代碼,也可以修改后重新發布,允許閉源進行商業行為,不過需要你在發布的產品中包含原作者代碼中的 BSD 協議

          總結

          以上就是項目中常用的文檔處理方法總結,當然了肯定也還有其它方案可以實現,畢竟條條大路通羅馬,如果你還要不錯的方案和建議歡迎留言

          PS: 以上方案和對應的源碼編譯、使用例子會統一上傳到 GitHub對應的倉庫,方便后人使用

          取之互聯網、回報互聯網

          原創不易,如果覺得對你有幫助,歡迎點贊、在看、轉發

          推薦閱讀

          • Qt Creator 源碼學習筆記01,初識QTC
          • Qt Creator 源碼學習筆記02,認識框架結構結構
          • Qt Creator 源碼學習筆記03,大型項目如何管理工程
          • Qt Creator 源碼學習筆記04,多插件實現原理分析
          • Qt Creator 源碼學習筆記 05,菜單欄是怎么實現插件化的?

          主站蜘蛛池模板: 亚洲AV日韩综合一区尤物| 综合激情区视频一区视频二区| 国产亚洲日韩一区二区三区| 色婷婷av一区二区三区仙踪林| 国产精品毛片一区二区三区| 一区二区国产在线观看| 亚洲片国产一区一级在线观看| 天堂一区二区三区精品| 亚洲性色精品一区二区在线| 无码一区二区三区AV免费| 亚洲AV日韩综合一区| 老熟妇高潮一区二区三区| 成人一区专区在线观看| 在线播放国产一区二区三区| 色狠狠AV一区二区三区| 国产精品一级香蕉一区| 无码中文字幕人妻在线一区二区三区| 精品国产鲁一鲁一区二区| 竹菊影视欧美日韩一区二区三区四区五区 | 亚洲av无码一区二区乱子伦as| 熟妇人妻系列av无码一区二区 | 变态调教一区二区三区| 亚洲男人的天堂一区二区| 成人日韩熟女高清视频一区| 亚洲av无码一区二区三区不卡| 国产99久久精品一区二区| 午夜一区二区免费视频| 丰满人妻一区二区三区视频| 中文字幕精品亚洲无线码一区应用 | 日本一区免费电影| 内射白浆一区二区在线观看 | 亚洲一区中文字幕在线电影网| 99精品国产一区二区三区2021| 久久精品无码一区二区三区不卡 | 国产精品视频一区二区三区四| 久久精品国产AV一区二区三区| 精品久久久久久中文字幕一区| 一区二区三区在线观看免费 | 国产主播一区二区三区| 亚洲韩国精品无码一区二区三区| 精品一区精品二区制服|