在Qt(C++)中與Python混合編程


一、PythonQt庫

在Qt(C++)中與Python混合編程,可以使用PythonQt庫。
網站首頁:http://pythonqt.sourceforge.net
下載頁面:https://sourceforge.net/projects/pythonqt/files/
只提供了源碼下載,需自行編譯。
版本要求
其網站building頁面上的要求:Qt 4.8.1以上,Python2.6以上
實際測試中得出的版本要求:Qt5.4以上,可以編譯得到動態鏈接庫(.so文件);Python2.7.12,編譯范例程序成功。
(備注:我的測試環境是操作系統 Ubuntu 16.04 64bit,PythonQt版本:3.2)

二、編譯與安裝

編譯文檔:http://pythonqt.sourceforge.net/Building.html
這里以Linux(Ubuntu系)為例,介紹一下編譯安裝方法。

1. 安裝Qt

去Qt網站下載安裝包,或者通過apt安裝。安裝完畢后,在命令行中執行qmake -v,查看輸出信息,確認Qt已安裝好。
注意:如果使用apt或者synaptic安裝Qt,那么需要手動安裝Qt的一些模塊,例如multimedia等。以Qt5為例,其模塊一般以libqt5為開頭,可以用apt或synaptic搜索關鍵字安裝。如果缺少模塊,則編譯PythonQt時會報錯提示(報錯是 Unknown module(s))。如果出現類似錯誤,則需要安裝相關模塊,再重新編譯。
大部分Qt模塊的軟件包名稱都是以libqt5開頭的,例如libqt5gui5、libqt5multimedia5、libqt5qml5等,有些可能以-dev結尾。但是有一些模塊的名稱則不一樣,這里列出來,以免遺漏:

  • qtdeclarative5-dev:與qml和quick模塊有關
  • qtmultimedia5-dev:與multimedia模塊有關

2. 安裝Python

用apt安裝Python和Python-dev。Linux一般預裝Python。
sudo apt install python python-dev

3. 編譯

將下載的源碼解壓。進入解壓目錄,之后執行編譯指令。假設解壓目錄為PythonQt

cd PythonQt
qmake
make all

編譯可能需要花費幾分鍾,請耐心等待。
編譯完成后,編譯得到的庫文件以及范例程序都在PythonQt/lib下。此時運行范例程序可能失敗,需要先安裝剛編譯好的庫。

4. 安裝

所謂安裝,是指讓系統能夠找到編譯好的庫文件。實現的方式有多種,這里介紹通過鏈接的方式安裝。
首先確認系統中的庫文件默認目錄是什么。

cd /etc/ld.so.conf.d/
ls

可能列出一些配置文件,文件名是對應的目錄,比如x86_64-linux-gnu.conf。用文本編輯器打開,就可以看到對應的完整目錄。我的系統中默認庫目錄有
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
這里,我選擇/usr/lib/x86_64-linux-gnu這個目錄安裝。

# 進入PythonQt的目錄
cd PythonQt
# 復制文件(文件名中的數字與版本有關,不一定和我一樣)
sudo cp lib/libPythonQt-Qt5-Python2.7.so.3.2.0 /usr/lib/x86_64-linux-gnu
sudo cp lib/libPythonQt_QtAll-Qt5-Python2.7.so.3.2.0 /usr/lib/x86_64-linux-gnu
# 進入安裝目錄
cd /usr/lib/x86_64-linux-gnu/
# 創建鏈接
sudo ln -sf libPythonQt-Qt5-Python2.7.so.3.2.0 libPythonQt-Qt5-Python2.7.so
sudo ln -sf libPythonQt-Qt5-Python2.7.so.3.2.0 libPythonQt-Qt5-Python2.7.so.3
sudo ln -sf libPythonQt-Qt5-Python2.7.so.3.2.0 libPythonQt-Qt5-Python2.7.so.3.2
sudo ln -sf libPythonQt_QtAll-Qt5-Python2.7.so.3.2.0 libPythonQt_QtAll-Qt5-Python2.7.so
sudo ln -sf libPythonQt_QtAll-Qt5-Python2.7.so.3.2.0 libPythonQt_QtAll-Qt5-Python2.7.so.3
sudo ln -sf libPythonQt_QtAll-Qt5-Python2.7.so.3.2.0 libPythonQt_QtAll-Qt5-Python2.7.so.3.2
# 更新
sudo ldconfig

安裝完成。運行PythonQt/lib下的范例程序(雙擊或命令行執行),如果可以運行,說明正常。

三、在Qt項目中使用PythonQt

可以看范例源碼和官方文檔學習。這里以Linux(Ubuntu系)環境為例,簡單介紹一下使用方法。

1. 新建項目

使用QtCreator新建項目。

2. 准備庫文件

需要將一些配置文件和PythonQt庫的頭文件復制到項目文件夾下(可以新建一個子文件夾)。假設項目目錄為[PRJ],PythonQt目錄為[PYQ]。

cd [PRJ]
mkdir PythonQt
cp [PYQ]/src/PythonQt*.h PythonQt/
cp [PYQ]/build/*.prf ./
cp [PYQ]/lib/libPythonQt-Qt5-Python*.so* ./

其中.h是頭文件,.prf是配置文件,.so是鏈接庫。
頭文件可以直接使用。配置文件需要修改,主要是修改相關目錄。
說明:配置文件的注釋方式是每行前加注釋符號#
以下是一種修改方式:
common.prf
將以下三行刪除或注釋掉:

CONFIG(debug, debug|release) { 
  TARGET = $${TARGET}_d 
}

將所有的$$PWD/../改為$$PWD/

PythonQt.prf
刪除或注釋掉以下內容:

INCLUDEPATH += $$PWD/../src 

# check if debug or release 
CONFIG(debug, debug|release) { 
  DEBUG_EXT = _d 
} else { 
  DEBUG_EXT = 
}

修改

unix::LIBS += -L$$PWD/../lib -lPythonQt-Qt5-Python$${PYTHON_VERSION}$${DEBUG_EXT}

改為

unix::LIBS += -L$$PWD -lPythonQt-Qt5-Python$${PYTHON_VERSION}$${DEBUG_EXT}

3. 修改.pro文件

在項目.pro文件中加入以下內容:

include ( common.prf )
include ( PythonQt.prf )

INCLUDEPATH += PythonQt

4. 在代碼中調用PythonQt

這個可以參考PythonQt的范例,然后慢慢摸索。
首先,引用頭文件

#include "PythonQt.h"

在使用PythonQt時,首先要對PythonQt的單例對象進行操作。包括初始化,獲取對象等。

// init PythonQt and Python
    PythonQt::init();

之后,獲取__main__模塊。

PythonQtObjectPtr mainModule;
// get the __main__ python module
    mainModule = PythonQt::self()->getMainModule();

為了能看到python程序中的打印信息,需要連接PythonQt單例對象信號,與你自己寫的槽。

// connect output signals
    connect(PythonQt::self(), SIGNAL(pythonStdOut(const QString&)), this, SLOT(stdOut(const QString&)));
    connect(PythonQt::self(), SIGNAL(pythonStdErr(const QString&)), this, SLOT(stdErr(const QString&)));

第一個信號是向std::out的輸出,第二個信號是std::err的輸出。
之后就可以操作mainModule的方法來調用python代碼了。當然,如果python代碼里不需要輸出,也可以不連接上述信號。

四、執行Python語句或腳本

按照上一節的說明初始化后,就可以執行Python語句或調用Python腳本了。

1. evalScript——執行少量語句

如果要調用的python代碼只有單一一行語句或者少量幾語句,可以使用evalScript函數。該函數的參數是要執行的指令,返回執行結果。

QVariant result1 = mainModule.evalScript("19*2+4", Py_eval_input);
QVariant result2 = mainModule.evalScript("len([1, 2, 3])", Py_eval_input);

其中"19*2+4"是python語句,第二個參數表示執行的是獨立的python表達式。返回類型是QVariant,可以根據實際執行的語句,轉換成具體的數據類型。比如這里可以用QVariant::toInt()轉換成int,得到的結果分別是42和3。不熟悉的朋友請參考QVariant文檔。
evalScript 可以用於定義函數,方便以后調用。例如:

mainModule.evalScript("def add(a, b):\n    return a+b");

這樣就定義了一個Python中的函數,名為add,接受兩個參數ab,返回兩個數的和。
后面第三部分介紹如何調用Python函數。

2. evalFile——執行腳本

如果需要使用Python實現較為復雜的功能,寫在一個Python文件中比較方便。假設文件名為func.py。
Python文件的開頭需要加入如下語句:

from PythonQt import *

在Qt項目中新建資源文件(.qrc文件),在資源文件中添加func.py,以便調用。調用方式為:

mainModule.evalFile(":/func.py");

調用時的文件路徑與添加到資源文件時的前綴有關。注意evalFile沒有返回類型,所以不能用於獲得返回值,可以通過第四節所說的打印信息看到執行過程(如果Python程序中有輸出語句的話)。如果是Qt GUI項目,也可以把執行結果顯示在界面上,這一點在后面第四部分介紹。
執行過evalFile后,腳本中定義的函數可以在以后直接調用。所以可以把需要返回值的功能寫在函數中,后續調用。調用方式見第三部分。

3. call——調用函數

前面介紹了,使用evalScriptevalFile都能定義Python函數。定義的函數會保存,之后可以在代碼的任意位置調用。要調用這些函數,可使用call。例如第一點介紹中定義了一個Python中的函數,名為add,接受兩個參數ab,返回兩個數的和。調用該函數的方法如下:

int a = 2;
int b = 3;
QVariant c = mainModule.call("add", QVariantList() << a << b);

call 的第一個參數是要調用的函數名稱,用字符串表示;第二個參數是要調用的Python函數的參數,用一個QVariantList存放所有參數。這里,我們把a和b兩個數傳入。返回類型是QVariant,需要轉換成具體類型。這里的c轉換成整數后是5。

4. addObject——與Qt交互

如果Python程序需要與Qt中的對象交互,可以將繼承自QObject的類型實例傳入Python中。addObject就起到這個作用。
假設Qt GUI項目的mainwindow中有一個label,下面演示怎么通過Python改變label的文字。
首先將label傳入Python(label的類型是QLabel,繼承自QObject),並且賦予其一個在python中調用的變量名:

mainModule.addObject("label", ui->label);

這條語句將ui->label傳入,並且在Python中可以用label這個變量名調用。
Python程序如下:

def changeLabelText(text):
    label.text = text

通過evalScript或者evalFile調用上述程序后,再用call調用。

mainModule.evalFile(":/func.py");
mainModule.call("changeLabelText", QVariantList() << QString("Hello"));
mainModule.call("changeLabelText", QVariantList() << QString("World"));

第一次調用將標簽文字改為Hello,第二次調用改為World。
在Python中操作QObject對象時,要注意,使用的方法、函數、屬性等要用Python語法進行。例如在Qt(C++)中改變標簽文字的方法是

label->setText("Hello");

而在Python中,應該使用

label.text = 'Hello'

5. Python模塊路徑問題

既然使用Python,可能是要使用Python中成熟的庫,例如用於科學計算的NumPy。如果在Python程序中寫入:

import numpy as np

可能在使用PythonQt執行的時候報錯,說找不到numpy模塊。(當然是已安裝的情況下,在Python或命令行中都運行正常。)這很可能是路徑問題。
首先找到numpy的安裝路徑,例如/usr/lib/python2.7/dist-packages。然后在需要調用的Python文件中加入如下語句:

import sys
sys.path.append('/usr/lib/python2.7/dist-packages') # To use Numpy
import numpy as np

這樣就可以正常導入NumPy模塊了。

小結

以上介紹了PythonQt庫的安裝和使用方法。
更加復雜的功能,請參考PythonQt源碼中的范例,以及網站上的文檔。
開發者文檔:http://pythonqt.sourceforge.net/Developer.html
源碼中也有很多有用信息,關於一些API函數的調用,可以參考頭文件中的注釋。例如,關於上面介紹的evalScript等函數,可以參考PythonQtObjectPtr.h文件(可用QtCreator內的切換功能快速定位)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM