python作为一门高级语言,在各种开发中相较于C++占据了很多优势,然而计算效率上却大打折扣,这对于需要做科学计算的人来说真是一个两难的抉择,较好的解决方案是python与C/C++的混合编程。

我最近需要写一个程序,把一个科学计算过程图形化展现出来,想到的方案就是使用pyqt来实现GUI,再调用C++进行大量的计算。其中遇到一个棘手问题,就是当我在调用C++进行运算的时候,如何将计算循环中的一些信息展示到GUI上。经过长时间的尝试,终于拼凑出了一套可以用的方案。

该方案的流程略微复杂,但思路相当简单:先在python中将stdout重定向到pyqt控件显示,调用C++,在C++执行过程中,调用python的print函数。

为了实现该方案,你需要了解至少以下知识点:
1. python中stdout重定向
2. swig包装C++为python模块
3. C++中调用python

下面分块介绍下整个流程:

一.stdout重定向到pyqt控件

class mystream:

    def __init__(self,button):
        self.btn = button
        self.txt = ''
    def write(self,text):
        self.txt += text
        self.btn.setText(self.txt+'\n')

    def flush(self):
        self.txt = ''

新建一个类,里面有三个函数,这三个函数是重定向时必需的:
init()是初始化函数,python中特有,self.btn是我们要传入的pyqt控件,self.txt就是我们用来操作的文本。
write()是print()函数实际调用的函数,即python中,当执行print()函数时,实际是调用了stdout.write()函数,并将print()中的字符串传递给这里的text。然后再将text的内容传递给self.txt。这里请注意,必需先在_init_()中定义一个self.txt,再在write()中将text赋值给self.txt,然后操作self.txt实现输出,我也不知道为什么,试了很多种其他写法均无效。后面的三句话实现将self.txt的内容输出到控件上,根据不同的控件类型可以灵活更改,甚至可以添加更多操作。
flush(),print()调用write()之后,再调用flush(),目的是清空self.txt中的内容,当然其实也完全可以自定义其功能。

定义完这个类后,就可以重定向了,代码如下:

import sys
from PyQt5.QtWidgets import QTextBrowser
info = QTextBrowser(self)
sys.stdout = mystream(info)

这里首先定义info,它是QTextBrowser控件,然后将sys.stdout重定向到我们自己定义的类中。
此后,所有print()函数都会把其中的字符串输出显示到info控件上。

二.swig包装C++为python模块

swig的使用较为复杂,网上有各种教程,不在此赘述。
这里谈一谈swig在这套方案中的重要性。
一开始,我是将C++编译成dll,然后在python中用ctype调用的。虽然可以调用成功,但是如果在dll中调用python,就会报错。我后面抱着试一试的心态学习使用了swig,没想到居然成功了。
另外,numpy为ctype和swig都写了传参的接口,可以直接将numpy中的array作为参数,传递到C++中,真的很强大!
至于其他混合编程的工具,比如boost,我就没用过了。

三.在C++中调用python

python为C/C++写了很多接口,这里只用到冰山一角。
首先需要在C++项目中设置好include和lib路径,然后就是直接调用了。

#include<Python.h>
char *s;
sprintf(s, "print('%d, x=%d, y=%d')", ID, indx, indy);
PyRun_SimpleString(s);

PyRun_SimpleString()函数会执行字符串s的内容,这里s是python中的语句。
比如,ID = 1;indx = 2; indy = 3;那么s = “print(‘1, 2, 3’)”,“”内的就是python的print()语句了,将会在python中打印‘1, 2, 3’。
这里还要特别说明一下,一般C++调用python时,执行语句前要加Py_Initialize();进行初始化,执行语句后要加Py_Finalize();终止。然而在此方案中,两个都不需要。

总结:这套方案可以灵活地运用到python和C++的交互中,特别是write()函数的自定义,一定可以实现很多意想不到的效果。

更多推荐

python调用C++,并在C++代码执行时,实时返回信息到pyqt的控件上显示