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 最近中文字幕视频国语中文字幕,日本www免费,美女毛片视频

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          JavaScript 數(shù)組進(jìn)行拼接的函數(shù)

          時候,我們希望在 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ū)動步驟

          我們再來說說實施數(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é)果

          二、數(shù)據(jù)驅(qū)動測試環(huán)境準(zhǔn)備

          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ù)存儲

          數(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ù)    

          七、json文件讀取測試數(shù)據(jù)進(jìn)行數(shù)據(jù)驅(qū)動測試

          數(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)為這種方式最好!

          八、從xml讀取數(shù)據(jù)進(jìn)行數(shù)據(jù)驅(qū)動測試

          數(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文檔有一定的了解

          總結(jié)

          上面的數(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ù)到 WordExcel 以及 PDF文件,如果你使用 C++ 編程語言,那么可以選擇的方案不是很多,恰好最近剛好有這部分需求,整理下這段時間踩過的坑,方便后人

          讀寫 Word

          日常開發(fā)的軟件使用最多的應(yīng)該是導(dǎo)出數(shù)據(jù)到 Word 文檔中,目前可以用的方案有這幾種

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

          XML 模板替換

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

          優(yōu)點

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

          缺點

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

          舉個栗子

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

          • 將該文檔另存為 Word XML 文檔 XML-Template.xml
          • 讀取文檔內(nèi)容進(jìn)行變量替換
              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();
          
          • 保存替換后的內(nèi)容,寫入文件

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

          這種方式適合模板內(nèi)容不太復(fù)雜,內(nèi)容較少的情況下使用

          COM 組件方式

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

          COM 技術(shù)概述

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

          優(yōu)點

          • 實現(xiàn)簡單,快速上手;

          缺點

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

          舉個栗子

          使用時需要引入對應(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");
          }
          
          ……
          

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

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

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

          HTML 方式

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

          優(yōu)點

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

          缺點

          • 字符串拼接 HTML 容易出錯,缺失標(biāo)簽導(dǎo)出后無法顯示;
          • 插入的圖片是本地圖片文件的鏈接,導(dǎo)出的 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實現(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ò)上找到的有這么幾個

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

          DuckX庫 docx庫

          在讀寫 Word這部分,C++ 基本沒有可以使用的第三方庫,不像其他語言JavaC#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é)

          讀寫 Excel

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

          QXlsx

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

          編譯支持 qmakecmake,可以根據(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

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

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

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

          qpdf 庫

          這個庫其實封裝了 pdf.js庫,使用 WebEngine來執(zhí)行 JavaScript進(jìn)而加載文件

          項目地址

          • 直接從本地文件加載;
          • 支持從內(nèi)存數(shù)據(jù)直接加載渲染 PDF 內(nèi)容;

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

          qtpdf 庫

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

          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é)

          以上就是項目中常用的文檔處理方法總結(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ā)

          推薦閱讀

          • Qt Creator 源碼學(xué)習(xí)筆記01,初識QTC
          • Qt Creator 源碼學(xué)習(xí)筆記02,認(rèn)識框架結(jié)構(gòu)結(jié)構(gòu)
          • Qt Creator 源碼學(xué)習(xí)筆記03,大型項目如何管理工程
          • Qt Creator 源碼學(xué)習(xí)筆記04,多插件實現(xiàn)原理分析
          • Qt Creator 源碼學(xué)習(xí)筆記 05,菜單欄是怎么實現(xiàn)插件化的?

          主站蜘蛛池模板: 蜜芽亚洲av无码一区二区三区| 激情啪啪精品一区二区| 亚洲一区二区三区高清| 亚洲一区二区观看播放| 国产区精品一区二区不卡中文| 女同一区二区在线观看| 91午夜精品亚洲一区二区三区| 中文字幕VA一区二区三区| 人妻无码一区二区三区| 精品国产一区二区三区免费看| 日韩人妻精品一区二区三区视频| 国产一区二区视频在线观看| 天天看高清无码一区二区三区 | 日韩精品一区二区三区中文版| 无码国产精品一区二区免费16| 国产免费播放一区二区| 国产精品一区二区av不卡| 波多野结衣一区在线| 精品一区二区三区在线观看l| 亚欧在线精品免费观看一区| 日韩精品中文字幕无码一区| 久久精品一区二区| 国产午夜精品一区理论片| 国产另类TS人妖一区二区| 免费无码一区二区三区蜜桃| 国产自产V一区二区三区C| 国产精品视频一区二区三区无码 | 无码一区二区三区视频| 精品一区二区三区免费| 久久中文字幕一区二区| 精品乱人伦一区二区三区| 亚洲图片一区二区| 国产大秀视频在线一区二区| 一本久久精品一区二区| 精品国产AV无码一区二区三区 | 精品国产日韩亚洲一区在线| 女人18毛片a级毛片一区二区| 精品女同一区二区三区免费播放| 丰满岳乱妇一区二区三区| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 肉色超薄丝袜脚交一区二区|