• 在Qt(C++)中与Python混合编程


    一、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):
        return a+b");
    

    这样就定义了一个Python中的函数,名为add,接受两个参数ab,返回两个数的和。
    后面第三部分介绍如何调用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——调用函数

    前面介绍了,使用evalScriptevalFile都能定义Python函数。定义的函数会保存,之后可以在代码的任意位置调用。要调用这些函数,可使用call。例如第一点介绍中定义了一个Python中的函数,名为add,接受两个参数ab,返回两个数的和。调用该函数的方法如下:

    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内的切换功能快速定位)。

  • 相关阅读:
    警惕 InnoDB 和 MyISAM 创建 Hash 索引陷阱
    从头认识java-18.2 主要的线程机制(5)-守护线程与非守护线程
    leetcode
    算法学习笔记(五) 递归之 高速幂、斐波那契矩阵加速
    No WebApplicationContext found: no ContextLoaderListener registered?报错解决
    poj 3041 Asteroids (最小点覆盖)
    C语言函数--E
    APDU命令的结构和处理【转】
    Linux ALSA声卡驱动之一:ALSA架构简介【转】
    Linux 系统内核空间与用户空间通信的实现与分析
  • 原文地址:https://www.cnblogs.com/xia-weiwen/p/7932379.html
Copyright © 2020-2023  润新知