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

1、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)html

2、編譯與安裝

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

1. 安裝Qt

去Qt網站下載安裝包,或者經過apt安裝。安裝完畢後,在命令行中執行qmake -v,查看輸出信息,確認Qt已安裝好。
注意:若是使用apt或者synaptic安裝Qt,那麼須要手動安裝Qt的一些模塊,例如multimedia等。以Qt5爲例,其模塊通常以libqt5爲開頭,能夠用apt或synaptic搜索關鍵字安裝。若是缺乏模塊,則編譯PythonQt時會報錯提示(報錯是 Unknown module(s))。若是出現相似錯誤,則須要安裝相關模塊,再從新編譯。
大部分Qt模塊的軟件包名稱都是以libqt5開頭的,例如libqt5gui五、libqt5multimedia五、libqt5qml5等,有些可能以-dev結尾。可是有一些模塊的名稱則不同,這裏列出來,以避免遺漏:linux

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

2. 安裝Python

用apt安裝Python和Python-dev。Linux通常預裝Python。
sudo apt install python python-devshell

3. 編譯

將下載的源碼解壓。進入解壓目錄,以後執行編譯指令。假設解壓目錄爲PythonQt編程

cd PythonQt
qmake
make all

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

4. 安裝

所謂安裝,是指讓系統可以找到編譯好的庫文件。實現的方式有多種,這裏介紹經過連接的方式安裝。
首先確認系統中的庫文件默認目錄是什麼。python2.7

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下的範例程序(雙擊或命令行執行),若是能夠運行,說明正常。函數

3、在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代碼裏不須要輸出,也能夠不鏈接上述信號。

4、執行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內的切換功能快速定位)。

相關文章
相關標籤/搜索