使用PythonQt實現C++與Python混合編程

PythonQt提供了一種將python腳本語言嵌入到Qt C++程序中的簡單方法。
我們可以使用一種腳本語言,擴展我們的應用,Qt中對於腳本化擴展應用程序有兩種方法

很多知名的軟件都是用python腳本的方式,擴展自己的應用程序,下面列舉一下使用該方式擴展應用的知名軟件:

  • Blender (開源三維動畫製作軟件)
  • Autodesk Maya (三維動畫製作軟件)
  • OpenOffice.org (跨平臺的辦公室軟件套件)
  • MeVisLab (醫學圖像處理軟件)
  • Scribus (桌面排版軟件)

參考鏈接:
Embedding Python into Qt Applications
PythonQt主頁
pythonqt源碼下載

下面介紹關於PythonQt的簡單使用


1. C++調用python腳本

關於pythonqt的編譯這裏就不過多的介紹了,簡單的說分爲三個步驟:

  1. 下載官方編譯好的python庫或者自己編譯python的源代碼。
  2. 下載pythonqt的源碼。
  3. 修改pythonqt工程配置文件中的python庫的路徑,直接編譯即可。

在使用pythonqt的時候,需要先對其進行初始化,使用頭文件 PythonQt.h

PythonQt::init();

如果使用封裝好的Qt類型,使用如下代碼初始化,使用頭文件 PythonQt_QtAll.h

PythonQt_QtAll::init();

使用函數 evalScript() , 執行python代碼

// 獲取 __main__ python module
PythonQtObjectPtr mainModule = PythonQt::self()->getMainModule();
QVariant result = mainModule.evalScript("100 + 200", Py_eval_input);
qDebug() << result;

這裏使用函數 PythonQt::self()->getMainModule() 獲取 __main__ 模塊,然後調用函數 evalScript 執行python 腳本。

使用函數 evalFile() 執行一個python腳本

PythonQtObjectPtr mainModule = PythonQt::self()->getMainModule();
mainModule.evalFile(":example.py");

執行python的函數,並傳遞參數

mainModule.evalScript("def addFunc(num1, num2):\n   return num1 + num2");
QVariantList nums;
nums << 100 << 200;
result = mainModule.call("addFunc", nums);
qDebug() << "Called addFunc the result is " << result;

2. python中調用C++

(1)直接使用Qt中的模塊

如果編譯了 PythonQt_QtAll 庫,則可以直接使用Qt中提供的類,他提供了完整的QtAPI的python封裝(包括C++類和QObject類的所有非槽函數、槽函數、信號、屬性等等)。

包括如下模塊 QtCore、QtGui、QtNetwork、QtOpenGL、QtSql、QtSvg、QtWebKit、QtXml、QtXmlPatterns、QtMultimedia、QtQml、QtQuick ,這些模塊都是作爲 PythonQt 的子模塊使用。 對於 QtQuick 的支持還處於實驗性階段,目前不支持從python中註冊新的QML組件。支持多態比如 QEvent 和多繼承比如 QGraphicsTextItem

下面是一個實例:
使用pythonQt製作如下的界面:
PythonQt
python中的代碼如下:

from PythonQt import QtCore, QtGui

# 創建Widget和佈局
mainWidget = QtGui.QWidget()
mainWidget.setWindowTitle('PythonWidget')
mainLayout = QtGui.QVBoxLayout(mainWidget)

# 創建TextEdit和一個按鈕,並添加到佈局
textEdit = QtGui.QTextEdit()
mainLayout.addWidget(textEdit)
testButton = QtGui.QPushButton('OK')
mainLayout.addWidget(testButton)

# 點擊按鈕時執行的函數
def onClicekedButton():
    textEdit.append('Hello PythonQt')

# 連接信號和處理函數
testButton.connect('clicked()', onClicekedButton)
# 顯示界面
mainWidget.resize(800, 600)
mainWidget.show()

點擊按鈕時,文本添加字符串 Hello PythonQt

(2)python中使用C++對象

下面是使用C++的一個類:
頭文件

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    Q_INVOKABLE void setTextEditText(const QString& text);

private:
    QTextEdit* m_pTextEdit = nullptr;
};

cpp文件

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QVBoxLayout* mainLayout = new QVBoxLayout(this);

    m_pTextEdit = new QTextEdit;
    m_pTextEdit->setObjectName("TestTextEdit");

    mainLayout->addWidget(m_pTextEdit);
}

Widget::~Widget()
{

}

void Widget::setTextEditText(const QString& text)
{
    m_pTextEdit->setText(text);
}

使用函數 addObject() 添加一個對象到python環境中:
調用如下:

Widget w;
w.show();
// 添加一個對象
mainModule.addObject("testWidget", &w);
// 添加終端
PythonQtScriptingConsole console(nullptr, mainModule);
console.show();

這裏使用了 PythonQtScriptingConsole 類創建了一個python終端窗口。
可以直接在終端中調用添加的對象的函數,這裏可以調用槽函數、使用 Q_INVOKABLE 修飾的函數、屬性、信號等。
pythonqt
同時也可以通過objectname定位到子控件:

pythonqt

(3)python中使用C++的類

PythonQt提供了一種通用的方法,來實現不論繼承QObject的類還是普通的C++類來封裝類提供給python環境。

如果想要在python中調用C++的類,只需如下三個步驟:

  1. 定義自定義類。
  2. 實現裝飾器。
  3. 註冊到Python環境中。

通過繼承QObject實現包裝器,並以特定的命名完成構造等函數的實現。

  • SomeClassName* new_SomeClassName(…) 定義構造函數
  • void delete_SomeClassName(SomeClassName* o) 定義析構函數
  • anything static_SomeClassName_someMethodName(…) 定義靜態函數
  • anything someMethodName(SomeClassName* o, …) 定義普通函數,這裏的第一參數同必須是該類型的指針。

注意:以上這些參數均要在槽函數中定義。

下面是一個普通的類定義:

// 測試普通類
class NormalClass
{
public:
    NormalClass(){
        qDebug() << __FUNCTION__;
    }
    ~NormalClass(){
        qDebug() << __FUNCTION__;
    }

    QString m_sTestName = "";
};

對於沒有繼承自 QObject 的類,還需要加上如下代碼

// register it to the meta type system
Q_DECLARE_METATYPE(NormalClass)

下面是裝飾器的定義

class NormalClassWrapper : public QObject
{
    Q_OBJECT

public slots:
	// Normal Class
    NormalClass* new_NormalClass(){ return new NormalClass; }
    void delete_NormalClass(NormalClass* o){ delete o; }

    void setTestName(NormalClass* o, const QString& name){ o->m_sTestName = name; }
    QString getTestName(NormalClass* o) { return o->m_sTestName; }

	// Widget
    Widget* new_Widget(QWidget* parent = nullptr){return new Widget(parent);}
    void delete_Widget(Widget* o){ delete o; }
};

這裏的 Widget 是之前定義的那個 Widget

需要 注意 的是,如果是繼承自 QObject 的對象,使用函數 registerClass() 註冊;如果是普通的C++類,則需要使用函數 registerCPPClass() 註冊。如果使用 registerCPPClass() 註冊的繼承自 QObject ,則不能使用默認的屬性、普通函數、槽函數等等 QObject 特有的特性,除非在裝飾器中自己實現相關的定義。

下面是註冊代碼:

// 註冊普通類
PythonQt::self()->registerCPPClass("NormalClass", "", \
    "example", PythonQtCreateObject<NormalClassWrapper>);
// 註冊繼承自QObject的類
PythonQt::self()->registerClass(&Widget::staticMetaObject, \
    "example", PythonQtCreateObject<NormalClassWrapper>);

測試python腳本

from PythonQt import *

normalObj = example.NormalClass()
normalObj.setTestName('不會飛的紙飛機')

w = example.Widget()
w.setTextEditText(normalObj.getTestName())
w.show()

作者:douzhq
個人博客主頁:不會飛的紙飛機

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章