将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 下载。

相关文章
相关标签/搜索