Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
時候,我們希望在 JavaScript 中將 2 個已經(jīng)存在的數(shù)組串拼接成 1 個數(shù)組。
簡單來說就是將數(shù)組進(jìn)行合并。
這個時候,我們可以使用 JavaScript 的 concat 函數(shù)。
考察下面的代碼:
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 個數(shù)組 array1 和 array2 合并成了一個新的數(shù)組為 array3, 在這個新的數(shù)組中的元素就是
array2 在 array1 后面添加得到的。
如果你需要合并的數(shù)組不只有 2 個,你還有多個的話,你可以同樣使用上面的方法,但是在參數(shù)中傳遞進(jìn)多個數(shù)組就行。
concat()
concat(value0)
concat(value0, value1)
concat(value0, value1, ... , valueN)
如上面的代碼,你并不需要將 concat 多次運行來進(jìn)行合并,concat 這個方法允許傳遞多個需要合并數(shù)組為參數(shù)。
網(wǎng)盜概念^-^相同的測試腳本使用不同的測試數(shù)據(jù)來執(zhí)行,測試數(shù)據(jù)和測試行為完全分離, 這樣的測試腳本設(shè)計模式稱為數(shù)據(jù)驅(qū)動。(網(wǎng)盜結(jié)束)當(dāng)我們測試某個網(wǎng)站的登錄功能時,我們往往會使用不同的用戶名和密碼來驗證登錄模塊對系統(tǒng)的影響,那么如果我們每一條數(shù)據(jù)都編寫一條測試用例,這無疑是增加了代碼量,代碼重復(fù),且顯得那么臃腫(誰不喜歡身材好的呢?你懂的),這時候我們可以使用不同數(shù)據(jù)驅(qū)動代碼執(zhí)行相同的用例測試不同的場景。
我們再來說說實施數(shù)據(jù)驅(qū)動測試的步驟:
1.創(chuàng)建/準(zhǔn)備測試數(shù)據(jù)
2.封裝讀取數(shù)據(jù)的方法,保留測試腳本調(diào)用的接口/屬性(我們需要傳遞給腳本什么參數(shù))
3.編寫自動化測試腳本
4.腳本中調(diào)用封裝好的處理數(shù)據(jù)文件的模塊并引入測試數(shù)據(jù)
5.執(zhí)行測試腳本并分析測試結(jié)果
1.安裝python3.x開發(fā)環(huán)境(能看到此文章的應(yīng)該都有這個環(huán)境,沒有的自行百度吧)
2.安裝數(shù)據(jù)驅(qū)動模塊ddt
安裝方式1:cmd下執(zhí)行命令 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進(jìn)行數(shù)據(jù)驅(qū)動
1.訪問地址:https://mail.sohu.com/fe/#/login
2.輸入用戶名和密碼
3.點擊登錄按鈕
4.判斷是否登錄成功
數(shù)據(jù)存在當(dāng)前腳本中
數(shù)據(jù)準(zhǔn)備
我們要實現(xiàn)的是用戶登錄的操作,所以用戶名和密碼是必須有的,期望結(jié)果可以有也可以沒有。數(shù)據(jù)類型看源代碼!
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 簡單數(shù)據(jù)驅(qū)動測試
8 '''
9 @ddt
10 class ddtTest(unittest.TestCase):
11 # 數(shù)據(jù) 可以是元祖, 列表, 字典(可迭代對象)
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) # * 解析數(shù)據(jù)
22 @unpack# 用來解包, 將每組數(shù)據(jù)的第一個數(shù)據(jù)傳遞給uname依次類推, 當(dāng)數(shù)據(jù)為字典時,形參需和字段的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數(shù)據(jù)驅(qū)動的規(guī)范寫法,記住就ok)
2.@data(*value)裝飾測試用例(也是一種規(guī)范,這邊又涉及到裝飾器,不懂的可以百度,這邊不再贅述,一句話兩句話也說不清楚)記住:*value作用是打散數(shù)據(jù),比如上面代碼是用一個大列表存儲兩個小列表存放數(shù)據(jù)的,那么*value會得到兩個小列表,每個小列表是一組測試數(shù)據(jù)
3.@unpack 解析*value數(shù)據(jù),會把兩個小列表里面的每一個數(shù)據(jù)取出來分別傳遞給我們測試用例的形參
方式1缺點
存儲大量數(shù)據(jù)時,需查看源代碼,不利于腳本的維護(hù)
數(shù)據(jù)準(zhǔn)備
新建一個json文件(也可以是txt文件),將我們需要的兩組測試數(shù)據(jù)以列表的形式寫到j(luò)son文件中,每組數(shù)據(jù)的每一項參數(shù)用相同的符號分割開(方便腳本讀取數(shù)據(jù))
{
"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 從文件中讀測試數(shù)據(jù)
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') # 讀取文件的 文件中數(shù)據(jù)可以是一個列表,也可以是一個字典
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__))# 獲取當(dāng)前文件目錄
46 # reportPath = os.path.join(currentPath,'report') # 創(chuàng)建一個report目錄
47 # if not os.path.exists(reportPath):
48 # os.mkdir(reportPath) # 判斷目錄是否存在, 不存在就創(chuàng)建
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='數(shù)據(jù)驅(qū)動測試報告', description='數(shù)據(jù)驅(qū)動')
53 # runner.run(suite)
源碼分析
1.相對上個實例,這里使用了@file_data(文件路徑), 參數(shù)必須是一個文件,這里是一個json文件, 數(shù)據(jù)可以是一個列表,也可以是一個字典
# 列表形式
[
"*******@sohu.com||xiacha11520||https://mail.sohu.com/fe/#/homepage",
"*******@sohu.com||xiacha11520||https://mail.sohu.com/fe/#/homepage"
]
2.測試用例接收的是一個字符串,需要對字符串進(jìn)行處理,把用戶名,密碼,期望值解析出來
方式2優(yōu)缺點
測試數(shù)據(jù)存在文件中,方便管理修改,添加數(shù)據(jù),易于維護(hù),缺點呢?emmm個人認(rèn)為這種方式最好!
數(shù)據(jù)準(zhǔn)備
新建一個xml格式的文件,按照xml格式的語法需求,填寫數(shù)據(jù)(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 # 獲取根節(jié)點
8 def getRoot(self):
9 tree = ElementTree.parse(self.xmlpath)
10 root = tree.getroot()
11 return root
12
13 # 根據(jù)根節(jié)點查找子節(jié)點
14 def findNodeByName(self, parentNode, nodeName):
15 nodes = parentNode.findall(nodeName)
16 return nodes
17
18 def getNodeOfChildText(self, node):
19 # 獲取節(jié)點node下所有子節(jié)點的節(jié)點名作為key
20 # 本節(jié)點作為value組成的字典對象
21 childrenTextDict = {}
22 for i in list(node.iter())[1:]: # node 節(jié)點下的所有節(jié)點組成的列表
23 childrenTextDict[i.tag] = i.text
24 # print(list(node.iter())[1:])
25 return childrenTextDict
26
27 # 獲取節(jié)點node下面的節(jié)點的所有數(shù)據(jù)
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節(jié)點
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中的節(jié)點可以是任意名稱,每個節(jié)點同樣是成雙出現(xiàn)
2.增加了doXML.py文檔,用來解析xml文件,方便腳本獲取數(shù)據(jù)(注釋寫的很詳細(xì),不懂的化可以慢慢調(diào)試,哪里不懂print哪里)
3.測試腳本和上面的實例大致相同
方式3優(yōu)缺點
優(yōu)點是做到了數(shù)據(jù)與測試的分離,方便數(shù)據(jù)維護(hù),缺點也比較明顯,需要對xml文檔有一定的了解
上面的數(shù)據(jù)驅(qū)動測試步驟是我自己總結(jié)的,看了上面的實例對于步驟應(yīng)該還算合理,下面是我在網(wǎng)上找到的數(shù)據(jù)驅(qū)動測試步驟(感覺比較官方!大家可以參考)
1.編寫測試腳本, 腳本需要支持從程序?qū)ο螅?文件,或者數(shù)據(jù)庫讀入數(shù)據(jù)。(個人觀點:如果腳本先編寫完,測試數(shù)據(jù)還未準(zhǔn)備,后期還要做修改)
2.將測試腳本使用的測試數(shù)據(jù)存入程序?qū)ο螅募蛘邤?shù)據(jù)庫等外部介質(zhì)中。(個人觀點:這個階段實為準(zhǔn)備數(shù)據(jù)的階段,也就是我們數(shù)據(jù)要存在哪里,理應(yīng)放在第一步)
3.運行腳本過程中,循環(huán)調(diào)用存儲在外部介質(zhì)中的測試數(shù)據(jù)。(個人觀點:這里要考慮我們?nèi)绾巫x取,使用數(shù)據(jù))
4.驗證所有的測試結(jié)果是否符合預(yù)期結(jié)果
最后今天的文章就到這里了,喜歡的可以點贊收藏加關(guān)注。
閱讀本文大概需要 6 分鐘
日常開發(fā)軟件可能會遇到這類小眾需求,導(dǎo)出數(shù)據(jù)到 Word、Excel 以及 PDF文件,如果你使用 C++ 編程語言,那么可以選擇的方案不是很多,恰好最近剛好有這部分需求,整理下這段時間踩過的坑,方便后人
日常開發(fā)的軟件使用最多的應(yīng)該是導(dǎo)出數(shù)據(jù)到 Word 文檔中,目前可以用的方案有這幾種
沒有十全十美的方案,任何方案都存在優(yōu)點和缺點,下面來詳細(xì)看下這幾種方案的優(yōu)缺點以及適用場景
“
原理:事先編輯好一份 Word 模板,需要替換內(nèi)容的 地方預(yù)留好位置,然后使用特殊字段進(jìn)行標(biāo)記,后面使用代碼進(jìn)行全量替換即可完成
我們先編輯一份 Word 模板文檔,內(nèi)容大概如下所示:
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格式后還需要繼續(xù)調(diào)整,這種 XML 格式標(biāo)簽很多,不小心就修改錯了,導(dǎo)致導(dǎo)出的文檔打不開
這種方式適合模板內(nèi)容不太復(fù)雜,內(nèi)容較少的情況下使用
“
原理:采用 Micro Soft公開的接口進(jìn)行通訊,進(jìn)行讀寫時會打開一個 `Word進(jìn)程來交互
COM 技術(shù)概述
Qt 為我們提供了專門進(jìn)行交互的類和接口,使用 Qt ActiveX框架就可以很好的完成交互工作
使用時需要引入對應(yīng)的模塊,在 pro 文件引入模塊
QT *= axcontainer
打開文檔寫入內(nèi)容
QAxObject *pWordWidget = new(std::nothrow) QAxObject;
bool bResult = pWordWidget->setControl("word.Application");
if (!bResult)
{
return false;
}
// 設(shè)置是否顯示
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");
}
……
可以看出來使用起來不難,對于新手友好一點,很多寫入操作方法比較繁瑣,需要自己重新封裝一套接口
這種方式同樣適用于寫入 Excel 文件,后面再說
“
原理:這種方式得益于 Word支持 HTML格式導(dǎo)出渲染顯示,那么反向也可以支持,需要我們拼接 HTML格式內(nèi)容,然后寫入文件保存成 .doc格式
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實現(xiàn)生成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格式拼接,任何缺失字段都會導(dǎo)致導(dǎo)出失敗,適合小眾需求下導(dǎo)出
圖片問題其實可以手動進(jìn)行轉(zhuǎn)化,文檔導(dǎo)出成功后手動拷貝內(nèi)容到新的文檔,這樣圖片就真正插入到文檔中,文檔發(fā)送給別人也不會丟失圖片了
還有一個坑就是:如果你使用 WPS 打開導(dǎo)出的文檔,默認(rèn)顯示的是 web視圖,需要手動進(jìn)行調(diào)整
某些電腦分辨率變化也會導(dǎo)致生成的文檔中字體等產(chǎn)生變化
可以使用的第三方庫幾乎沒有,網(wǎng)絡(luò)上找到的有這么幾個
DuckX庫 docx庫
在讀寫 Word這部分,C++ 基本沒有可以使用的第三方庫,不像其他語言Java、C#、Python有很多可以選擇,這個痛苦也只有 C++ 程序員能夠理解了吧
所以怎么選擇還是看自己項目需求吧,沒有十全十美的方案
上面說了這么多,都是導(dǎo)出生成 Wrod,那么下面來看看有那些方式可以讀取顯示 Word內(nèi)容
這種需求應(yīng)該不會很多,而且顯示難度更大一些
使用 COM組件方式,即采用 QAxWidget框架顯示 office 文檔內(nèi)容,本質(zhì)上就是在我們編寫的 Qt 界面上嵌入 office 的軟件,這種方式其實和直接打開 Word查看沒有啥區(qū)別,效果、性能上不如直接打開更好一些
目前一般都會采用折中方案,把 Word 轉(zhuǎn)為 PDF 進(jìn)行預(yù)覽加載顯示,我們知道 PDF 渲染庫比較多,生態(tài)相對來說要好一些,在選擇上就更廣泛些,如何使用后面部分有專門介紹 PDF章節(jié)
目前有一個支持比較好的第三方庫可以使用,整體使用基本可以滿足日常使用
QXlsx
這款開源庫支持跨平臺,Linux、Windows、Mac、IOS、Android,使用方式支持動態(tài)庫調(diào)用和源碼直接集成,非常方便
編譯支持 qmake和cmake,可以根據(jù)你自己的項目直接集成編譯,讀寫速度非常快
QXlsx::Document xlsx;
// 設(shè)置一些樣式
QXlsx::Format titleFormat;
titleFormat.setBorderStyle(QXlsx::Format::BorderThin); // 邊框樣式
titleFormat.setRowHeight(1,1,30); // 設(shè)置行高
titleFormat.setHorizontalAlignment(QXlsx::Format::AlignHCenter); // 設(shè)置對齊方式
// 插入文本
xlsx.write(1,1, "微信公眾號:devstone", titleFormat);
// 合并單元格
xlsx.mergeCells(QXlsx::CellRange(2,1,4,4), titleFormat);
// 導(dǎo)出保存
xlsx.saveAs("D:/xlsx_export.xlsx");
// 添加工作表
xlsx.addSheet("devstone");
可以看到上手非常容易、各個函數(shù)命名也貼近 Qt Api,是一款非常良心的開源軟件
“
PS:注意該軟件使用 MIT 許可協(xié)議,這樣對于很多個人或者公司來說非常良心,意味著你可以無償使用、修改該項目,但是必須在你項目中也添加同樣的 MIP許可
上面也提到了,還可以使用 COM 組件的方式讀寫 Excel,不過有了這款開源庫基本就可以告別 COM組件方式了
PDF相關(guān)開源庫挺多的,給了 C++ 程序員莫大的幫助,目前可用的主要有這些
其中 mupdf和 poppler 屬于功能強大但是很難編譯的那種,需要有扎實的三方庫編譯能力,否則面對 n 個依賴庫會無從下手
不過可喜的是 Github 上有兩個開源庫可以供選擇
這個庫其實封裝了 pdf.js庫,使用 WebEngine來執(zhí)行 JavaScript進(jìn)而加載文件
項目地址
這種方式對環(huán)境有特殊要求了,如果你的項目使用的 Qt 版本不支持 WebEngine,那么就無法使用
這個庫是 Qt 官方親自操刀對第三方庫進(jìn)行了封裝,暴露的 API 和 Qt 類似,使用起來非常舒服
Qt 官方
代碼結(jié)構(gòu)以及使用 Demo
關(guān)于如何使用,官方已經(jīng)給了我們非常詳細(xì)的步驟了,直接跟著下面幾步就 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上同步了這個倉庫的鏡像,有條件還是建議直接下載最新穩(wěn)定版的
可正常訪問的倉庫地址:https://github.com/PDFium/PDFium
相關(guān)類可以看這個文檔:https://developers.foxit.com/resources/pdf-sdk/c_api_reference_pdfium/modules.html
“
最后還要注意項目開源協(xié)議:pdfium引擎開始來自于福昕,一個中國本土的軟件公司,Google與其合作最終進(jìn)行了開源,目前采用的是 BSD 3-Clause 協(xié)議,這種協(xié)議允許開發(fā)者自由使用、修改源代碼,也可以修改后重新發(fā)布,允許閉源進(jìn)行商業(yè)行為,不過需要你在發(fā)布的產(chǎn)品中包含原作者代碼中的 BSD 協(xié)議
以上就是項目中常用的文檔處理方法總結(jié),當(dāng)然了肯定也還有其它方案可以實現(xiàn),畢竟條條大路通羅馬,如果你還要不錯的方案和建議歡迎留言
PS: 以上方案和對應(yīng)的源碼編譯、使用例子會統(tǒng)一上傳到 GitHub對應(yīng)的倉庫,方便后人使用
取之互聯(lián)網(wǎng)、回報互聯(lián)網(wǎng)
原創(chuàng)不易,如果覺得對你有幫助,歡迎點贊、在看、轉(zhuǎn)發(fā)
推薦閱讀
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。