I am currently developing a C++ application using Qt and I have to plot some data which is generated by C++ (vectorfields, time-signals correlation matrices etc...). I found the Matplotlib from Python quite nice which is why I want to pass the data from the C++ program to a Python function which will do the plotting. The problem is that the call to the Python function is blocking e.g. it stops the Qt GUI. I currently solve this issue by forking a new process and calling the Python function from the child process.
My question is whether there is a more efficient way of opening a Python Plot from C++ or if forking a new process is pretty much all I can do?
C-Testprogram that calls Python-Plotting-Function:
#include <python3.6/Python.h>
#include <python3.6/listobject.h>
#include <stdlib.h>
#include <fstream>
#include <unistd.h>
#include <math.h>
#define N_LIST 100
using namespace std;
//define some constants for plotting sin(2*pi*50*t)
double f=50;
double ang_frequency=2*M_PI*f;
double dt=1/(f*N_LIST);
int main(int argc,char** argv){
//set the PYTHONPATH variable to that python can find the Python script containing the plotting function
char currentdir[200];
getcwd(currentdir,200);
printf("%s\n",currentdir);
if(setenv("PYTHONPATH",currentdir,0)!=0){
printf("Could not set PYTHONPATH\n");
};
//start the python interpreter
Py_Initialize();
//load the python module
PyObject* modulename=PyUnicode_FromString("Module");
PyObject* module=PyImport_Import(modulename);
Py_DECREF(modulename);
if(module!=NULL){
//load the python function which should do the plotting
PyObject* py_function=PyObject_GetAttrString(module,"plotFunction");
//create two python lists for holding the time points and signal points
PyObject* py_listy=PyList_New(N_LIST);
PyObject* py_listx=PyList_New(N_LIST);
//fill the lists with values
for(int i=0; i<N_LIST; i++){
double c_currentdouble=sin(i*dt*ang_frequency);
double c_currenttime=dt*i;
PyObject* py_currentdoubley=Py_BuildValue("d",c_currentdouble);
PyObject* py_currenttime=Py_BuildValue("d",c_currenttime);
PyList_SetItem(py_listy,i,py_currentdoubley);
PyList_SetItem(py_listx,i,py_currenttime);
}
//call the python and pass the data generated in C
//------------------------------------------------------------------------------------------------------
if(py_function&&PyCallable_Check(py_function)){
pid_t pid=fork();
if(pid==0){
PyObject* res=PyObject_CallFunctionObjArgs(py_function,py_listx,py_listy,NULL);
return EXIT_SUCCESS;
}
}
//------------------------------------------------------------------------------------------------------
else{
printf("Could not create function object.");
printf("\n%d",py_function);
return EXIT_FAILURE;
}
}
else{
printf("Cannot find module.");
return EXIT_FAILURE;
}
printf("Exiting program\n");
return EXIT_SUCCESS;
}
Python Script containing plotFunction(argx,argy):
import matplotlib.pyplot as plt
import numpy as np
def plotFunction(argx,argy):
if type(argx)==list or type(argx)==np.ndarray and type(argy)==list or type(argy)==np.ndarray:
plt.plot(argx,argy)
plt.grid()
plt.show()
else:
print("Not all arguments passed are lists. Call failed.")
If you want to stick to python you can call the function in a new thread instead of a whole new process. This is quite easy if you have C++11 std::thread but you can also use QtThread.
An easier way would be to use QtCharts though and plot directly in C++.
A third way would be to save the plot from python to an image and display that image in Qt.