分析FastCAE代码之前先看看C++与Python如何相互调用的。
一、C++调用Python
先写个C++调用Python的例子,然后再来看FastCAE集成Python就比较简单了。直接上代码:
#include <iostream>
#include "python.h"
int main()
{
Py_Initialize();
PyRun_SimpleStringFlags("print('hello world') ", NULL);
Py_Finalize();
}
代码功能很简单,就是执行打印hello world结束。其中,Py_Initialize()初始化Python环境。PyRun_SimpleStringFlags这个函数可以执行Python代码,Py_Finalize()关闭Python解析器。这个例子要跑起来麻烦的地方在项目配置:
- 配置头文件:Python安装目录下的include目录加入头文件目录。Visual Studio中操作路径是:属性–》 C/C++ --》 常规 --》 附加包含目录
- 配置lib库目录:要将Python37.lib加入编译链接。Visual Studio中操作路径是:属性–》 链接器 --》 常规 --》 附加包含目录
同时,在“输入”–》“附加依赖项”中输入Python37.lib - 配置运行目录:配置dll文件所在目录,我这里直接更改的工作目录,这种方式不推荐。比较好的方式是配置"调试"–》"环境"中增加PATH变量。但这种方式我没有配成功,用的是更改工作目录,将工作目录直接指到python所在目录,如下图:
运行起来就能看到打印的hello world了!
二、Python调用c++ dll库
Python可通过ctypes调用动态库。ctypes的使用资料很多。我这里就不再讲了。
三、FastCAE Python模块分析
FastCAE中与Python相关的代码同样分成两块:C++调用Python、Python调用C++。
C++调用Python相关的代码在PythonModule模块,Python调用C++是在预先导入的python脚本中,这些脚本会调用底层dll库,最总形成如下图所示的调用关系:
- PythonModule模块:
PythonModule模块总共只有5个类。其中PyAgent、PyInterpreter是核心,封装这C++调用Python的逻辑。
先看一下PyAgent类头文件:
class PYTHONMODULEAPI PythonAgent : public QObject
{
Q_OBJECT
public:
static PythonAgent *getInstance(); // 获取单例接口
void initialize(GUI::MainWindow *m); // 初始化
void finalize();
void submit(QString code, bool save = true); // 提交执行Python代码
//后台执行,不在界面显示,也不保存
void backstageExec(QString code);
void submit(QStringList codes, bool save = true);
void saveScript(QString fileName);
bool execScript(QString fileName);
void appCodeList(QString code);
void lock();
void unLock();
bool isLocked();
void appendOn();
void appendOff();
void execMessWinCode(QString code);
QStringList getcodelist();
void setNoGUI(bool nogui);
signals:
void printInfo(Common::Message type, QString m);
void closeMainWindow();
private:
PythonAgent();
~PythonAgent() = default;
void connectSignals();
private slots:
void readerFinished();
private:
static PythonAgent *_instance;
PyInterpreter *_interpreter{};
RecordThread *_recordScript{};
GUI::MainWindow *_mainWindow{};
ScriptReader *_reader{};
bool _islock{false};
bool _append{true};
bool _noGUI{false};
};
这个类是个单例类,用的时候用getInstance获取实例。initialize()、submit()是主要的方法。initialize是主要是调用Py_Initialize初始化Python解析器,代码如下:
void PythonAgent::initialize(GUI::MainWindow* m)
{
_mainWindow = m;
connectSignals();
Py_SetProgramName(L"FastCAE");
Py_Initialize();
if(!_interpreter->init(this))
emit printInfo(Common::Message::Error, tr("Python Initialize failed!"));
else
emit printInfo(Common::Message::Normal, tr("Python Initialized"));
_recordScript = new RecordThread;
_recordScript->start();
}
PyInterpreter类主要是import预先写好的Python脚本,这些脚本在该模块的py文件夹下。同时提供执行Python代码的入口,最终使用PyRun_SimpleStringFlags执行Python代码。
预制的Python脚本
- Python脚本
Python脚本中使用ctypes调用dll库。dll为了支持Python调用需声明调用的接口,并且为了防止C++编译器更改函数名,需用extern "C"
进行修饰。以几何模块为例,其python接口声明在GeoCommandPy.h文件中,代码如下:
//声明为C接口,供Python脚本调用
extern "C"
{
void GEOMETRYCOMMANDAPI CreateBox(char* name, double x, double y, double z, double l, double w, double h);
void GEOMETRYCOMMANDAPI EditBox(int id, double x, double y, double z, double l, double w, double h);
void GEOMETRYCOMMANDAPI CreateCylinder(char* name, double x, double y, double z, double l, double w, double h, double radius, double length);
void GEOMETRYCOMMANDAPI EditCylinder(int id, double x, double y, double z, double l, double w, double h, double radius, double length);
void GEOMETRYCOMMANDAPI CreateCone(char* name, double x, double y, double z, double l, double w, double h, double radius, double radius2, double length);
void GEOMETRYCOMMANDAPI EditCone(int id, double x, double y, double z, double l, double w, double h, double radius, double radius2, double length);
void GEOMETRYCOMMANDAPI CreateSphere(char* name, double x, double y, double z, double r);
void GEOMETRYCOMMANDAPI EditSphere(int id, double x, double y, double z, double r);
void GEOMETRYCOMMANDAPI CreatePoint(char* name, double x, double y, double z, double p1, double p2, double p3);
void GEOMETRYCOMMANDAPI EditPoint(int id, double x, double y, double z, double p1, double p2, double p3);
void GEOMETRYCOMMANDAPI CreateLine(char* name, double startpoint0, double startpoint1, double startpoint2,
int method, double coor0, double coor1, double coor2, double len, double dir0, double dir1, double dir2, int reverse);
void GEOMETRYCOMMANDAPI EditLine(int id, double startpoint0, double startpoint1, double startpoint2,
int method, double coor0, double coor1, double coor2, double len, double dir0, double dir1, double dir2, int reverse);
void GEOMETRYCOMMANDAPI CreateFace(char* edges,char* name, int editId);
void GEOMETRYCOMMANDAPI CreateChamfer(char* edges, int editId,double d1,double d2,int typeindex);
void GEOMETRYCOMMANDAPI EditChamfer(int id, char*setidStr, char* indexListStr, double d1, double d2, int sym);
void GEOMETRYCOMMANDAPI CreateFillet(char* edges, double rad, int editID);
void GEOMETRYCOMMANDAPI CreateVariableFillet(char*edges, double basicrad, int editId, int setid, int edgeindex);
void GEOMETRYCOMMANDAPI CreateBooLOperation(char* booltype, int set1, int body1, int set2, int body2);
void GEOMETRYCOMMANDAPI EditBooLOperation(int id, char* booltype, int set1, int body1, int set2, int body2);
void GEOMETRYCOMMANDAPI CreateMirrorFeature(char* bodys, char* method,int faceindex, int facebody,char* planemethod, double random0, double random1, double random2, double base0, double base1, double base2,char* saveori);
void GEOMETRYCOMMANDAPI EditMirrorFeature(int id, char* bodys, char* method, int faceindex, int facebody, char* planemethod, double random0, double random1, double random2, double base0, double base1, double base2, char* saveori);
void GEOMETRYCOMMANDAPI RotateFeature(char* body, double basicx, double basicy, double basicz, //体 & 基准点
int method, int edgeBoby, int edgeIndex, double axisx, double axisy, double axisz, int reverse, //轴线
double angle, int saveOri);
void GEOMETRYCOMMANDAPI EditRotateFeature(int bodyid, char* body, double basicx, double basicy, double basicz, //体 & 基准点
int method, int edgeBoby, int edgeIndex, double axisx, double axisy, double axisz, int reverse, //轴线
double angle, int saveOri);
void GEOMETRYCOMMANDAPI CreateMoveFeature(char * bodys, char* method,double startpt0, double startpt1, double startpt2, double endpt0, double endpt1, double endpt2, char* save, char*reverse,
double length, double dir0, double dir1, double dir2);
void GEOMETRYCOMMANDAPI EditMoveFeature(int id, char * bodys, char* method, double startpt0, double startpt1, double startpt2, double endpt0, double endpt1, double endpt2, char* save, char* reverse,
double length, double dir0, double dir1, double dir2);
void GEOMETRYCOMMANDAPI MakeMatrix(char * bodys, int optionindex, double dir10, double dir11, double dir12, int reverse1, double dis1, int count1, int showdir2,
double dir20, double dir21, double dir22, int reverse2, double dis2, int count2, double basept0, double basept1, double basept2, double axis0, double axis1, double axis2,
int wirereverse, int wirecount, double degree);
void GEOMETRYCOMMANDAPI EditMatrix(int id, char * bodys, int optionindex, double dir10, double dir11, double dir12, int reverse1, double dis1, int count1, int showdir2,
double dir20, double dir21, double dir22, int reverse2, double dis2, int count2, double basept0, double basept1, double basept2, double axis0, double axis1, double axis2,
int wirereverse, int wirecount, double degree);
void GEOMETRYCOMMANDAPI CreateExtrusion(int id,char* name,char *edges,double dis,double pt0,double pt1,double pt2,char* reverse,char* solid);
void GEOMETRYCOMMANDAPI CreateRevol(int id, char* name, char *edges, double basept0, double basept1, double basept2, double degree, char* optionindex, int axissetid, int edgeindex, double coor0, double coor1, double coor2, char* reverse, char* solid);
void GEOMETRYCOMMANDAPI CreateLoft(int id, char* name, char* solid, char* sec);
void GEOMETRYCOMMANDAPI CreateSweep(int id, char*edges, char* solid, int pathset,int pathedge);
void GEOMETRYCOMMANDAPI MakeGeoSplitter(char* bodystr, char* method, int facebody, int faceid, char* planemethod, double random0, double random1, double random2, double base0, double base1, double base2);
void GEOMETRYCOMMANDAPI EditGeoSplitter(int editid, char* bodystr, char* method, int facebody, int faceid, char* planemethod, double random0, double random1, double random2, double base0, double base1, double base2);
void GEOMETRYCOMMANDAPI MakeFillHole(char* faces, int editID);
void GEOMETRYCOMMANDAPI MakeRemoveSurface(char* faces, int editID);
void GEOMETRYCOMMANDAPI CreateFillGap(char* type, int set1, int body1, int set2, int body2);
void GEOMETRYCOMMANDAPI EditFillGap(int id, char*type, int set1, int body1, int set2, int body2);
}
借鉴这个例子,自己的程序也能集成Python了吧~~