一、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
,接受兩個參數a
和b
,返回兩個數的和。
后面第三部分介紹如何調用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——調用函數
前面介紹了,使用evalScript
和evalFile
都能定義Python函數。定義的函數會保存,之后可以在代碼的任意位置調用。要調用這些函數,可使用call
。例如第一點介紹中定義了一個Python中的函數,名為add
,接受兩個參數a
和b
,返回兩個數的和。調用該函數的方法如下:
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內的切換功能快速定位)。