將Python嵌入到Qt程序中

(原文連接: http://doc.trolltech.com/qq/qq23-pythonqt.html   by Florian Link  譯:  賴敬文 html

      將腳本語言嵌入C++ 程序已經變得很是廣泛。在許多主流的應用程序,如Microsoft Office 與Macromedia Director 中, 都有一種上升的趨勢,即提供小巧的,腳本給用戶以提供一些更加專用的功能。python

過去的幾年,對於Qt 程序嵌入腳本只有兩種主流的解決方案:由奇趣提供的 QSA (JavaScript 2.0) 和由Riverbank Computing  提供的PyQt (Python) 。在Qt Quarterly   的Scripting Qt   文章中已經給出了一個很好的關於QSA , PyQt  和其它解決方案。shell

自那篇文章寫完以後,還有許多方案正在開發中,到目前爲止,還有兩種方案值得參考:  api

  • QtScript,  一個自 Qt4.3 後的基於  ECMAScript 的解析器瀏覽器

  • PythonQt, MeVisLab 正在使用 ,  屬於一個動態地 Python 解析器。網絡

QtScript  與 PythonQt  出現使得在Qt 程序中嵌入腳本變得容易,這篇文章將集中描述PythonQt  app

腳本的好處

將一個C++ 程序腳本化有以下一些好處:  框架

  • 一個設計得好的應用程序能夠爲初級跟專家級用戶提供易於控制的能力。dom

  • 在不須要具備很是深厚的 C++  背景下,應用程序均可以很容易地擴展 .  函數

  • 腳本便於建立宏和批處理  

  • 自動化測試變得可能  

  • 腳本可跨平臺,若應用程序能夠運行於某個平臺,腳本一樣能夠運行。  

  • 腳本可使原型化的階段更快實現,好比 ,   你的支持團隊能夠經過腳原本增長特性,這比使用 C++  開發並從新布暑更方便。  

腳本的API 具備多種形式:能夠是一個對於能用任務的批處理,也能夠是一個能夠供用戶定製及擴展菜單及對話框的功能更全的版本,甚至是能夠訪問程序的能夠說功能(如,網絡瀏覽器中的JavaScript ).  

當針對Qt 程序增長腳本時,如下幾點被認爲是有益的:  

  • 易於集成進 Qt  程序中  

  • 基於你們都知道的腳本語言,以下降學習一門新語言的門檻。  

  • Qt  框架的良好集成 ,  如,它應該知道 signals,slots   properties.  

  • 支持腳本語言與 Qt  之間的類型轉換及處理,理想狀況下,最好支持全部的 QVariant  類型

  • 支持調試 , 當腳本程序變大時,調試功能也變得重要。

關於PythonQt  

與 PyQt 和Qt Jambi 不一樣, PythonQt 不是做爲編寫獨立的應用程序的支撐組件,而是提供嵌入python 解析器的能力,而且能夠很容易地將應用程序的部分功能導出到Python 中。  

PythonQt  擴展了Qt 4 meta-object  系統的功能。所以,PythonQt 可以在不知道任何QObject 的狀況下, 藉助QMetaObject 提供的信息,動態地腳本化QObject 的功能。 這種動態方法容許多種不一樣的腳本綁定並嵌入到同一個應用程序中,每一種綁定可使用同一個腳本API. 例如: JavaScript (QSA or QtScript)  和 Python.  

如下幾個部分會重點介紹PythonQt 的一些核心特性。更具體的關於PythonQt 介紹,歡迎訪問PythonQt 項目的主頁.  

如何開始

下面這個應用程序展現將PythonQt 嵌入到你的Qt 應用程序的步驟  

     #include "PythonQt.h"
     #include <  QApplication >
      
     int main(int argc, char **argv)
     {
         QApplication  qapp(argc, argv);
      
         PythonQt::init();
         PythonQtObjectPtr mainModule =
                           PythonQt::self()->getMainModule();
         QVariant  result = mainModule.evalScript(
                         mainModule, "19*2+4", Py_eval_input);
         return 0;
     }

咱們最早初始化一個PythonQt 的實例,這個實例初始化Python 解析器。而後咱們從Python 的__main__ 模塊中獲得一個智能指針(PythonQtObjectPtr ) 並在此模塊內測試一些Python 代碼.  

此處的結果由Python 進行解析會是42 。

建立應用程序腳本API  

      程序腳本化的方法是找到最適合你的用戶、開發者和員工的適合層面的API 。最基本的,你爲你的應用程序建立了domain-specific 的語言,它可使得你的用戶很方便地在沒有C++ 編譯器的狀況下獲得它想要的功能.  

一個典型的PythonQt 的應用是讓你的用戶、開發者或者支持人員建立一些小的腳本,經過腳原本改變應用程序的某些方面。  

典型的,你能夠建立一個新的基於QObject 的API 類,將之做爲一個適配器用於你的應用程序的其它類中。 你也能夠將全部的QObjects 都直接導出來,但一般狀況下,這種導出方式致使不少的細節暴露給了腳本使用者,而且迫使你保持全部導出接口的穩定,不然你的腳本API 的改變將致使使用者在之後使用舊的腳本的時候由於API 不一致而不可用。

建立特定的API 對象也許是最優的解決方案,這使得你能夠保持一個穩定的接口而且能夠聲明程序的哪部分是腳本可使用的。PythonQt  支持全部的 QVariant   類型,所以你能夠建立豐富的應用程序API ,好比返回值是QDateTime   與 QPixmap  類型, 甚至是包含了任意QVariant 值樹狀化的 QVariantMap   對象。

關於Python

Python (http://www.python.org )  是面向對象的程序語言,有持續增加的用戶社區和大量的標準模塊。Python  被設計成是「可擴展和可嵌入」的。用C/C++ 寫成的庫能夠包裝成Python 的包供Python 程序使用,而解析器也能夠嵌入到應用程序中提供腳本功能。

如下是一些提供Python 腳本的比較著名的應用程序:  

GUI  腳本化

下面讓咱們建立一個簡單的實例,包含Group box.  


 

 

C++  代碼以下定義用戶界面:  

     QGroupBox  *box = new QGroupBox ;
     QTextBrowser  *browser = new QTextBrowser (box);
     QLineEdit  *edit = new QLineEdit (box);
     QPushButton  *button = new QPushButton (box);
      
     button->setObjectName("button1");
     edit->setObjectName("edit");
     browser->setObjectName("browser");
      
     mainModule.addObject("box", box);


如今,咱們利用PythonQt, 使用Python 腳本存取GUI 。首先,咱們展現如何方便地存取Qt 的屬性及子對象:
  
     # Set the title of the group box via the title property.
     box.title = 'PythonQt Example'
  
     # Set the HTML content of the QTextBrowser.
     box.browser.html = 'Hello <b>Qt</b>!'
  
     # Set the title of the button.
     box.button1.text = 'Append Text'
  
     # Set the line edit's text.
     box.edit.text = '42'

注意,當屬性被設置成QString 屬性後,每一個Python string 會自動轉化成QString.  

C++ 中的Signals 對象也能夠轉化成Python 函數。咱們定義一個一般的函數,咱們鏈接按鈕的clicked() 信號與lineedit 的returnPressed() 槽:  

def  appendLine ():
         box.browser.append (box.edit.text)
  
     box.button1.connect ('clicked()' , appendLine)
     box.edit.connect ('returnPressed()' , appendLine)

group box  與一般狀況同樣,做爲一個最頂層的控件:

     box.show ()

爲了測試以上腳本,咱們須要在主模塊中調用一個特殊的函數。在這裏,咱們已經將這個文件在Qt 的資源系統中定義,所以,咱們像正常狀況下使用它:

     mainModule.evalFile (":GettingStarted.py" );

如今,只要你在Lineedit 按下了return 或者點擊按鈕,在lineedit 中的文字將會使用appendLine() 函數將文字添加到browser 中。

PythonQt  模塊

腳本一般須要作的事情比處理數據,創建鏈接,調用函數要多。例如,腳本一般須要具備爲程序建立特定類型的新對象的能力。  

爲知足這種需求,PythonQt  倉儲了一個叫PythonQt 的模塊,你可使用它來讀取全部書籍對象的consturctors 和靜態成員。   

這裏展現了一些使用PythonQt 模塊的例子:  

     from  PythonQt import  *
  
     # Access enum values of the Qt namespace.
     print  Qt .AlignLeft
  
     # Access a static QDate method.
     print  QDate .currentDate ()
  
     # Construct a QSize object
     a = QSize (1,2)

裝飾器與C++ 包裝

PythonQt 的動態方法的一個主要的問題是在Python 中只有slots 纔是可調用的.  在這種方法中沒有辦法對C++ 方法進行動態地腳本化,由於Qt 的meta-object compiler (moc ) 並不爲它們產生運行時信息。  

PythonQt 引入了"decorator slot" 這個概念,  它以一種直接的方式重用了支持constructors, destructors,  靜態方法和非靜態方法進行調用Qt slots 的機制。最基本的想法是引入了新的繼承於QObject 的 "decorator objects" (not to be confused with Python's own decorators) , 它的 slots  遵循decorator 的命令規範而且使得PythonQt 能夠將一般的constructors 可調用。

這使得任何C++ 類或者繼承於QObejct 的類容許其在現有的類結構中擴展一些額外的成員函數。

下面這個類定義顯示了一些decorators 例子:  

     class PyExampleDecorators : public QObject
     {
         Q_OBJECT
      
     public slots:
         QVariant  new_QSize (const QPoint  &p)
             { return QSize (p.x(), p.y()); }
      
         QPushButton  *new_QPushButton (const QString  &text,
                                      QWidget  *parent = 0)
             { return new QPushButton (text, parent); }
      
         QWidget  *static_QWidget_mouseGrabber ()
             { return QWidget::mouseGrabber() ; }
      
         void move(QWidget  *w, const QPoint  &p) { w->move(p); }
         void move(QWidget  *w, int x, int y) { w->move(x, y); }
      };

當將上面的示例decorator 在PythonQt 中註冊以後 ( 經過 PythonQt::addDecorators() ), PythonQt  如今提供了:  

  • 一個以 QPoint 爲參數的 QSize 構造函數

  • string 和父 widget 爲參數的 QPushButton 的構造函數

  • 一個新的 QWidget 的靜態成員 mouseGrabber();

  • Move() 自己在 QWidget 並非 slot, 所以,如今爲 QWidget 添加一個額外的 move()

  • 重載的可供調用的 QWidget      slot move()

這種裝飾器的方法很是強大,由於它能夠在你的類體系中隨時增長一些新的功能,而且不須要手動地處理參數的轉換。 ( 好比,  將constructor 參數從Python 轉化成Qt).  將一個非slot 方法暴露到PythonQt 中變成了一句語句便可完成的事.  

其它特性

PythonQt  還提供另一些更高級的特性,其中一些有趣的是:  

  • 對不是繼承於 QObject   C++  對象進行包裝

  • 支持自定義的 QVariant 類型

  • 建立你自有的導入方式的接口,好比, Python 腳本能夠在執行前先註冊或者校驗。

在PythonQt 源代碼的例子中,包含了許多咱們沒有在此文中提到的額外的特性。

將來方向

PythonQt  是爲了使MeVisLab 腳本化而寫的,當前已經達到了一個滿意的成熟度。 它使得在現有的Qt 應用程序中嵌入Python 腳本變得很是容易,而它並不須要像PyQt 同樣提供覆蓋面很廣的QtAPI 。  

我要謝謝個人公司 MeVis Research ,  是它容許我將PythonQt 做爲一個開源項目在source forge 存在。PythonQt 以LGPL 協議發佈,你能夠到pythonqt.sourceforge.net  得到更多信息。

我正在尋求更多的開發者參與到這個項目中來。假如你想貢獻的話,請聯繫我florian (at) mevis.de  if you would like to contribute!  

本文中所述的例子的源代碼是PythonQt 包的一部分,你能夠到sourceforge.net 下載。

相關文章
相關標籤/搜索