PythonQt简明教程

PythonQt是Qt框架的Python动态绑定,是一种将Python脚本语言嵌入C++ Qt应用程序的简便方法。与PyQt、PySide不同,PythonQt侧重点在于将Python嵌入到现有的C++应用程序,而不是使用Python编写应用程序。


接口

PythonQt的主要接口通过PythonQt::self()单例提供,并由PythonQt::init()初始化。完整的Qt绑定通过PythonQt_QtAll::init()启用。


数据类型映射

Qt/C++ Python
bool bool
double float
float float
char/uchar,int/uint,short,ushort,QChar integer
long integer
ulong,longlong,ulonglong long
QString unicode string
QByteArray QByteArray封装器
char* str
QStringList unicode字符串元组
QVariantList objects元组
QVariantMap objects字典
QVariant 视类型而定
QSize, QRect及其它所有标准Qt变体类型 变体封装器,支持对应Qt类完整接口
OwnRegisteredMetaType C++封装器,registerCPPClass提供额外的信息、封装
QList<AnyObject*> 转换为C++封装器列表
QVector<AnyObject*> 转换为C++封装器列表
枚举类型 枚举封装器
QObject (及其子类) QObject封装器
C++对象 C++封装器,可由PythonQtCppWrapperFactory封装,或由装饰器装饰
PyObject PyObject
  • QStringRef(Qt5)、QStringViewQAnyStringView(Qt6)的处理方式与QString类似
  • QByteArrayView(Qt6)的处理方式类似于QByteArray
  • Python bytes 类型可以自动转换为QByteArray。而QByteArray则通过data()方法转换为bytes
  • QVariant类型将递归映射
  • PyObject是直接作为指针传递的,这允许将任何Python对象直接传递、返回到使用PyObject *作为参数、返回值的Qt槽函数

PythonQt实现了所有Qt QVariant类型,并支持这些对象的完整Qt API。


QObject封装

所有在Python解释器中可见的QObject子类,都会自动用一个Python类封装。即

  • 调用PythonQt::addObject
  • 槽函数在python中返回一个QObject子类
  • 信号函数包含一个QObject子类并连接到python函数

任何在Python可见的QObject子类,除了通过PythonQt::addObject()添加之外,还可以调用PythonQt::registerClass()添加。PythonQt::registerClass()将注册该类完整的父类层次结构,亦即当注册子类时,子类的所有父类都将被注册。 注:PythonQt::addObject()也通过调用PythonQt::registerClass()注册类型。 在Python中,不仅可以调用QObject对象的槽函数,还可以访问对象所有的属性。除此之外,还支持:

  • className() 返回QObject对象类名字符串
  • help() 打印对象的所有属性、槽、枚举、构造函数、封装器槽
  • delete() 删除对象
  • connect(signal, function) 连接信号与python函数
  • connect(signal, qobject, slot) 连接信号与另一QObject对象槽
  • disconnect(signal, function) 断开信号与python函数
  • disconnect(signal, qobject, slot) 断开信号与另一QObject对象槽
  • children() 返回子对象列表
  • setParent(QObject) 设置父对象
  • QObject *parent() 获取父对象

使用信号

# 定义信号处理函数
def someFunction(flag):
  print flag
 
# button1是通过addObject()添加到Python的QPushButton对象
# 连接clicked信号到Python函数
button1.connect("clicked(bool)", someFunction)

定义信号、槽

class MySender(QtCore.QObject):
  emitProgress = QtCore.Signal(float)  # 此float在C++中对应double
  
class MyReceiver(QtCore.QObject):
  @QtCore.Slot(float)
  def progress(self, value):
    print(f"progress: {value}")
 
sender = MySender()
receiver = MyReceiver()
# 通过有效签名连接
sender.connect("emitProgress(double)", receiver, "progress(double)")
sender.emitProgress(2.0)

C++封装

通过如下步骤,实现对C++类的封装:

  • 继承QObject并封装C++对象
class CObjectWrapper : public QObject
{
public:
    CObjectWrapper(void *object) : object(object) {}

public slots:
    // 方法

private:
    CObject *object;
};
  • 派生PythonQtCppWrapperFactory,实现create()方法
class CObjectFactory : public PythonQtCppWrapperFactory 
{
public:
    virtual QObject* create(const QByteArray& name, void *ptr)
    {
        if (name == "CObject")
            return new CObjectWrapper(ptr);
        return nullptr;
    }
};
  • 注册工厂
PythonQt::self()->addWrapperFactory(new CObjectFactory);

对于未知的C++ 类对象,PythonQt将创建一个通用的C++ 封装器。如果是支持的C++ 类对象,将通过封装器工厂创建对应的QObject封装器。除此之外,还可以通过装饰器实现。


元对象、元类型

对于每个已知的C++类,PythonQt都提供了对应的Python类,这些类在PythonQt模块中可见。如果注册类时给出了包名,则在子包中也可见。 元类型支持:

  • 访问所有已声明的枚举值
  • 构造函数
  • 静态方法
  • 非绑定非静态方法
  • help()className()

在Python中,可以通过导入PythonQt模块来访问这些类和Qt命名空间。

from PythonQt import QtCore
 
# 访问命名空间
print QtCore.Qt.AlignLeft
 
# 构造函数
a = QtCore.QSize(12,13)
b = QtCore.QFont()
 
# 静态方法
QtCore.QDate.currentDate()
 
# 枚举值
QtCore.QFont.UltraCondensed
# 或
QtCore.QFont.Stretch.UltraCondensed

装饰器

装饰器是PythonQt引入的一种扩展封装QObject、C++对象的方法,是提供了具有构造函数、析构函数等功能,并按约定命名的槽函数的QObject子类。

命名约束如下:

  • 构造函数 Class *new_Class(Args... args)
  • 析构函数 void delete_Class(Class *object)
  • 静态方法 ReturnType static_Class_method(Args... args)
  • 方法 ReturnType method(Class *object, Args... args)
// C++类实例
class CObject {
public:
    CObject(int arg1, float arg2) { a = arg1; b = arg2; }
 
    float method(int arg1) { return arg1 * a * b; };

private: 
    int a;
    float b;
};
 
// 装饰器实例,可以同时装饰多个C++类
class Decorator : public QObject
{
    Q_OBJECT
public slots:
  // 构造器
  CObject* new_CObject(int arg1, float arg2) { return new CObject(arg1, arg2); }
 
  // 析构器
  void delete_CObject(CObject* obj) { delete obj; }
 
  // 方法
  int method(CObject* obj, int arg1) { return obj->method(arg1); }
};
 
PythonQt::self()->addDecorators(new Decorator());
PythonQt::self()->registerCPPClass("CObject");
from PythonQt import QtCore, QtGui, CObject
 
# 调用构造函数
object = CObject(1,11.5)
 
# 调用方法
print object.method(1);
 
# 调用析构函数
object = None

所有权管理

在PythonQt中,当一个对象由Python构造函数创建是,默认归Python所有;当它从C++ 接口(如槽)返回时,默认归C++ 所有。 由于Qt接口中存在各种将所有权传递给其它C++对象的方法,因此PythonQt提供了如下接口来跟踪这些接口调用。

  • PythonQtPassOwnershipToCPP 传递所有权给C++
  • PythonQtPassOwnershipToPython 传递所有权给Python
  • PythonQtNewOwnerOfThis 建立新的所有权

这些注解模板适用于所有C++指针类型及QList<AnyObject *>

public slots:
    PythonQtPassOwnershipToPython<QGraphicsItem*> createNewItemOwnedByPython();
 
    void addItemToCPP(PythonQtPassOwnershipToPython<QGraphicsItem*> item);
    void addItemToCPP(PythonQtPassOwnershipToPython<QList<QGraphicsItem*>> items);
    void addItemParent(QGraphicsItem* wrappedObject, PythonQtNewOwnerOfThis<QGraphicsItem*> parent);