前言
我最近遇到一個這樣的需求,即把某個軟件中采集的數(shù)據(jù)按照特定的格式導(dǎo)出到world文檔中。因為程序是用Qt開發(fā)的,所以想找一個滿足要求的C++庫,通過一番查詢發(fā)現(xiàn)能完成這個需求的常用C++庫有LibreOffice、OpenOffice。這兩個庫雖然能實現(xiàn)這一需求但是學(xué)習(xí)成本比較高,在規(guī)定的時間內(nèi)完成這個需求比較困難。
這時只能將目光轉(zhuǎn)向其他語言的讀寫word文檔的庫上,我發(fā)現(xiàn)python-docx 這個庫不僅能實現(xiàn)需求而且學(xué)習(xí)成本很低,只要懂點Python幾乎不需要額外花時間就能學(xué)會使用這個庫。
本文主要從三個方面介紹了如何在Qt應(yīng)用程序中調(diào)用Python實現(xiàn)數(shù)據(jù)導(dǎo)出到world文檔中。這三個方面分別是C++調(diào)用python、在程序中使用Python虛擬環(huán)境及Python-docx庫的基本應(yīng)用。
1.C++調(diào)用python
1.1 安裝Python開發(fā)環(huán)境
麒麟V10 默認安裝了python3.8,但是沒有安裝C++調(diào)用Python需的頭文件,在系統(tǒng)中找不到Python.h文件。安裝Python 開發(fā)環(huán)境,執(zhí)行下面的命令:
sudo apt-get update
sudo apt-get install python3-dev
安裝完成后會在/usr/include/python3.8 目錄下看到需要的頭文件。
1.2 修改Qt工程配置
新建Qt工程demo,并在pro文件中增加Python頭文件和庫的引用。
INCLUDEPATH += /usr/include/python3.8
LIBS += -L/usr/lib/python3.8/config-3.8-aarch64-linux-gnu -lpython3.8
1.3 初始化Python環(huán)境
//初始化Python C API
Py_Initialize();
//將Python文件所在的目錄添加到環(huán)境變量,以便能正確加載Python文件。
PyRun_SimpleString("import sys");
QString pyfilePath = QString::fromLocal8Bit("sys.path.append('%1')").arg(qApp->applicationDirPath() + "/python");
qDebug() << "pyfilePath:" << pyfilePath;
PyRun_SimpleString(pyfilePath.toLocal8Bit().data());
QString printSysPath = "print(sys.path)";
PyRun_SimpleString(printSysPath.toLocal8Bit().data());
//加載Python文件,其中testdocx.py 所在的目錄即為 qApp->applicationDirPath() + /python/testdocx.py
PyObject *pWriteWordModule = PyImport_ImportModule("testdocx");
if(!pWriteWordModule)
{
qDebug() << "import module faild!";
//如果失敗打印錯誤,方便調(diào)試
PyErr_Print();
}
上面的代碼,首先初始化Python環(huán)境,然后設(shè)置Python文件搜索路徑到path中,最后加載testdocx.py 文件。
1.4 C++ 調(diào)用Python 函數(shù)
假設(shè)testdocx.py 文件中的內(nèi)容如下,定義了一個函數(shù),并打印傳進來的參數(shù)。
def writeWord(wordPath):
print(wordPath)
在C++中調(diào)用這個函數(shù)
//獲取Python中定義的函數(shù)的指針
PyObject *pFunc = PyObject_GetAttrString(pWriteWordModule, "writeWord");
if(!pFunc)
{
qDebug() << "get func faild!";
}
//初始化參數(shù)元組,其中包含一個參數(shù)
PyObject *pTuple = PyTuple_New(1);
QString wordFilePath = QString("%1/python/%2").arg(qApp->applicationDirPath()).arg(QString::fromLocal8Bit("xxx.docx"));
//設(shè)置參數(shù)
PyTuple_SetItem(pTuple, 0, Py_BuildValue("s", wordFilePath.toLocal8Bit().data()));
//調(diào)用Python中的函數(shù)
PyObject_CallObject(pFunc, pTuple);
運行程序,如果一切順利的話會在終端打印:
/home/demo/bin/python/xxx.docx
1.5 常用的Python接口
Python C API參考文檔地址
PyObject 在C++和Python混合編程中最常用的一個類型,所有對象類型都是這個類型的擴展。PyObject中包含了Python需要將對象的指針視為對象的信息。在一般的“release”版本中,它只包含對象的引用計數(shù)和指向相應(yīng)類型對象的指針。實際上并沒有任何東西被聲明為PyObject,但是每個指向Python對象的指針都可以轉(zhuǎn)換為PyObject*。必須使用宏P(guān)y_REFCNT和Py_TYPE來訪問成員。下面是一些編程中常用的Python C API。
- PyList_New 創(chuàng)建一個Python list 對象,返回類型為PyObject*;
- PyDict_New 創(chuàng)建一個Python Dict 對象,返回類型為PyObject*;
- PyTuple_New 創(chuàng)建一個Python 元組對象,返回類型為PyObject*;
- Py_BuildValue 將C數(shù)據(jù)類型轉(zhuǎn)換為Python對象,返回類型為PyObject*,下面是Py_BuildValue函數(shù)的原型:
PyObject* Py_BuildValue(const char* format, …);其中,format是一個描述返回的Python對象的格式字符串,而…表示可變數(shù)量的參數(shù)列表,這些參數(shù)與格式字符串中指定的類型相對應(yīng)。
下面是一些常見的格式字符串和相應(yīng)的參數(shù)列表:
‘()’:表示一個空元組。
‘(i)’:表示一個包含一個整數(shù)(參數(shù)為整數(shù))的元組。
‘(ii)’:表示一個包含兩個整數(shù)(參數(shù)為兩個整數(shù))的元組。
‘s’:表示一個字符串。
‘d’:表示一個浮點數(shù)。
‘z’:表示一個NULL指針或None。
‘O’:表示一個通用對象。
下面是一個示例,演示如何使用Py_BuildValue函數(shù)將C數(shù)據(jù)類型轉(zhuǎn)換為Python對象:
#include <Python.h>
int main(int argc, char* argv[]) {
Py_Initialize();
int x = 42;
double y = 3.14;
char* z = "Hello, World!";
PyObject* result = Py_BuildValue("(iO)", x, Py_None);
if (result == NULL) {
PyErr_Print();
return 1;
}
printf("Result: %s\n", PyUnicode_AsUTF8(result));
Py_DECREF(result);
Py_Finalize();
return 0;
}
- PyDict_SetItemString 設(shè)置字典鍵值對;
//函數(shù)原型
PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item);
//示例:
PyObject* pDictObj = PyDict_New();
PyObject* deviceNameObj = Py_BuildValue("s", deviceData.deviceName.toLocal8Bit().data());
PyDict_SetItemString(pDictObj, "deviceName", deviceNameObj);
- PyList_Append 向列表中添加元素;
//函數(shù)原型
PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);
//示例:
PyObject *PointDataListObj = PyList_New(0);
PyList_Append(PointDataListObj, pDictObj);
- PyRun_SimpleString 在Python解釋器中執(zhí)行一個簡單的 Python 代碼字符串;
//函數(shù)原型
int PyRun_SimpleString(const char *command);
//示例
const char* code = "print('Hello, World!')";
int result = PyRun_SimpleString(code);
if (result == 0) {
//執(zhí)行成功
printf("Successfully executed code.\n");
} else {
//執(zhí)行失敗
printf("Failed to execute code.\n");
}
- PyImport_ImportModule 在C程序中導(dǎo)入Python模塊;
//函數(shù)原型
PyObject* PyImport_ImportModule(const char *name);
//說明,該函數(shù)返回一個Python對象,表示導(dǎo)入的模塊。如果導(dǎo)入成功,則返回該模塊對象的引用,否則返回NULL。
//示例:
PyObject* mymodule = PyImport_ImportModule("mymodule");
if (mymodule != NULL) {
printf("Successfully imported mymodule.\n");
} else {
printf("Failed to import mymodule.\n");
}
- PyObject_CallObject 在C中調(diào)用Python對象的方法和函數(shù);
//函數(shù)原型
PyObject* PyObject_CallObject(PyObject *callable, PyObject *args);
//說明,callable是一個指向要調(diào)用的Python對象(如函數(shù)或方法)的指針,args是一個指向參數(shù)列表的指針。
//該函數(shù)返回調(diào)用結(jié)果,表示為Python對象。如果調(diào)用成功,則返回調(diào)用結(jié)果的引用,否則返回NULL。
//示例:
PyObject* mymodule = PyImport_ImportModule("mymodule");
PyObject* myfunction = PyObject_GetAttrString(mymodule, "myfunction");
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, PyLong_FromLong(42));
PyObject* result = PyObject_CallObject(myfunction, args);
if (result != NULL) {
printf("Function returned: %ld\n", PyLong_AsLong(result));
} else {
printf("Failed to call function.\n");
}
- PyErr_Print 用于將當(dāng)前錯誤信息打印到標(biāo)準(zhǔn)錯誤輸出,方便調(diào)試程序。
2.python虛擬環(huán)境
2.1Python虛擬環(huán)境簡介
virtualenv和anaconda都是用于創(chuàng)建Python虛擬環(huán)境的工具,二者的主要區(qū)別如下:
- 包管理器:virtualenv使用pip作為包管理器,而anaconda使用conda作為包管理器。
- 環(huán)境隔離:virtualenv只能隔離Python包,而anaconda可以隔離整個Python環(huán)境及其依賴項。
- 系統(tǒng)依賴:anaconda自帶了許多科學(xué)計算所需的系統(tǒng)依賴,而virtualenv則需要手動安裝這些依賴。
- 平臺支持:anaconda支持Windows、Linux和MacOS等多個平臺,而virtualenv則主要支持Linux和MacOS。
在這里選擇virtualenv,原因是anaconda 在飛騰架構(gòu)的麒麟系統(tǒng)上安裝失敗。
2.2 virtualenv 安裝及使用
- 1.安裝
sudo apt-get install pip
pip install virtualenv
- 2.創(chuàng)建虛擬環(huán)境
cd /home/demo/bin/python
virtualenv demoEnv
#或者指定Python版本
virtualenv -p /usr/bin/python3.8 demoEnv
- 3.激活虛擬環(huán)境
demoEnv\Scripts\activate
- 4.安裝依賴包
pip install -r requirements.txt
pip install python-docx
- 5.退出虛擬環(huán)境
demoEnv\Scripts\deactivate
2.3 在C++程序中配置virtualenv 虛擬環(huán)境
demo程序利用了系統(tǒng)自帶的Python3.8環(huán)境,所以在C++程序中只需配置好虛擬環(huán)境的包安裝目錄即可,這點是參考在終端通過命令執(zhí)行Python腳本時的環(huán)境設(shè)置。例如執(zhí)行下面的命令來查看當(dāng)前Python環(huán)境的path內(nèi)容:
python3 test.py
#其中 test.py 內(nèi)容如下
import sys
print(sys.path)
因此Qt程序中Python環(huán)境初始化部分要增加設(shè)置Python虛擬環(huán)境安裝包路徑的代碼,如下:
QString modulePath = QString::fromLocal8Bit("sys.path.append('%1')").arg(qApp->applicationDirPath() + "/python/demoEnv/lib/python3.8/site-packages");
PyRun_SimpleString(modulePath.toLocal8Bit().data());
經(jīng)過上述設(shè)置就可以在虛擬環(huán)境中用pip命令安裝應(yīng)用程序所需的python包,并在應(yīng)用程序所在目錄的python文件中使用第三方包,如python-docx。當(dāng)Qt程序需要發(fā)布到其他飛騰架構(gòu)的麒麟V10設(shè)備上時,可以直接把虛擬環(huán)境一起打包,省去了在新的設(shè)備安裝依賴包的步驟。
3.python-docx庫的應(yīng)用
官方參考文檔https://python-docx.readthedocs.io
python-docx 作為word文檔讀寫庫算是很輕量了,其涉及的概念不多,很容易理解與掌握。下面是python-docx的一些核心概念。
- Document 表示word文檔。
- Text 表示word文檔中的文本和段落。
- Section 表示頁面布局,方向相同的頁面。
- Headers 、 Footers 表示頁眉頁腳
- Table 表格
- Image 圖片
下面是一個快速上手指南:文章來源:http://www.zghlxwxcb.cn/news/detail-655015.html
- 打開一個word文檔
from docx import Document
document = Document()
- 添加一個段落
paragraph = document.add_paragraph('Lorem ipsum dolor sit amet.')
或
prior_paragraph = paragraph.insert_paragraph_before('Lorem ipsum')
- 添加頁眉
document.add_heading('The REAL meaning of the universe')
或
document.add_heading('The role of dolphins', level=2)
- 添加分頁符
document.add_page_break()
- 添加表格
table = document.add_table(rows=2, cols=2)
cell = table.cell(0, 1)
cell.text = 'parrot, possibly dead'
row = table.rows[1]
row.cells[0].text = 'Foo bar to you.'
row.cells[1].text = 'And a hearty foo bar to you too sir!'
for row in table.rows:
for cell in row.cells:
print(cell.text)
row_count = len(table.rows)
col_count = len(table.columns)
row = table.add_row()
# get table data -------------
items = (
(7, '1024', 'Plush kittens'),
(3, '2042', 'Furbees'),
(1, '1288', 'French Poodle Collars, Deluxe'),
)
# add table ------------------
table = document.add_table(1, 3)
# populate header row --------
heading_cells = table.rows[0].cells
heading_cells[0].text = 'Qty'
heading_cells[1].text = 'SKU'
heading_cells[2].text = 'Description'
# add a data row for each item
for item in items:
cells = table.add_row().cells
cells[0].text = str(item.qty)
cells[1].text = item.sku
cells[2].text = item.desc
table.style = 'LightShading-Accent1'
- 添加一個圖片
document.add_picture('image-filename.png')
#設(shè)置圖片尺寸
from docx.shared import Inches
document.add_picture('image-filename.png', width=Inches(1.0))
4.總結(jié)
以上就是本篇的所有內(nèi)容了,關(guān)于python-docx應(yīng)用的一些細節(jié)本文并未涉及太多,想要了解更多的話還是要閱讀官方文檔。對C++ python混合編程有疑問的朋友歡迎留言討論?。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-655015.html
到了這里,關(guān)于Qt+Pyhton實現(xiàn)麒麟V10系統(tǒng)下word文檔讀寫功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!