Я занимаюсь разработкой программы на C ++ 11 с интерпретатором (написанным на C ++), который вызывает различные методы класса для выполнения работы. Я хочу добавить возможность создавать графики данных, используя Python 3.5 с matplotlib (backend - TkAgg). Я также хочу, чтобы пользователи могли запускать интерпретатор Python или запускать скрипты Python изнутри программы, чтобы вносить подробные изменения / улучшения в графики в реальном времени.
До сих пор я успешно создавал и сохранял графики, используя Boost.Python 1.65.1 в качестве моего интерфейсного уровня, и запустил интерпретатор Python с использованием модуля кода Python. Однако циклы событий графиков matplotlib не запускаются в моем приглашении интерпретатора C ++ (то есть, если я помещаю другое окно поверх графика, рисунок становится пустым и не перерисовывается). Циклы событий работают только тогда, когда пользователь запускает интерпретатор Python, и цифры перерисовываются. Мне бы хотелось, чтобы программа вела себя так, как будто пользователь создал графики в собственном интерпретаторе Python после выполнения команды matplotlib.pyplot.ion (). Я добавил вызов plt.ion () в код Python, используемый для создания графиков, но это, похоже, не влияет на итоговое поведение.
Я пытался это исправить, выполняя построение Python и интерпретацию C ++ в разных потоках, однако, похоже, это ничего не помогает. Я хотел бы знать, должен ли я обращаться с Python GIL иначе, чем я в настоящее время, или есть что-то еще, что я могу сделать, чтобы запустить циклы событий matplotlib в фоновом потоке. Я опубликовал простой пример, воссоздающий основную проблему, с которой я столкнулся. Любая помощь приветствуется.
Еще одна вещь, которую я пробовал, - это вручную вызвать метод canvas.start_event_loop () matplotlib. Кажется, это работает, но мешает мне создавать новые графики до тех пор, пока не вернется / тайм-аут, что не идеально.
Вот код Python для создания графиков
# Import matplotlib and pyplot and turn on interactive mode
import matplotlib
import matplotlib.pyplot as plt
plt.ion()
def PlotData(num):
""" Create a simple plot and return the figure.
"""
data_list = [[1,2,3,4],[3,2,4,1],[4,3,2,1]]
data = data_list[num]
f = plt.figure()
ax = f.gca()
ax.set_xlabel('x label')
ax.set_ylabel('y label')
ax.plot([1,2,3,4],data,'bo-')
title = 'Data Set ' + str(num+1)
f.suptitle(title)
print(title)
f.show()
return f
Вот код C ++
#include <boost/python.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/chrono.hpp>
#include <iostream>
#include <string>
PyThreadState* mainThreadState = nullptr;
PyThreadState* pts = nullptr; /*!< Pointer for the current thread state */
/*! Initialize Python */
void init()
{
Py_Initialize();
PyEval_InitThreads();
mainThreadState = PyEval_SaveThread();
}
void init_thread()
{
pts = PyThreadState_New(mainThreadState->interp);
}
/*! Calls Python to create a simple plot interactively */
void PlotData(const int& inp)
{
using namespace boost::python;
// Acquire the GIL
std::cout << "Python Thread GIL State (before): " << PyGILState_Check() << std::endl;
PyEval_RestoreThread(pts);
object background = import("sto");
object fig = background.attr("PlotData")(inp);
// The code below will show the plot, but the plot won't update in real time at the C++ command line
object plt = import("matplotlib.pyplot");
fig.attr("show")();
plt.attr("pause")(.1);
std::cout << "Python Thread GIL State (during): " << PyGILState_Check() << std::endl;
// Release the GIL
pts = PyEval_SaveThread();
std::cout << "Python Thread GIL State (after): " << PyGILState_Check() << std::endl;
}
int main(int argc, char* argv[])
{
// Create a thread pool with 1 thread for all Python code
boost::asio::io_service ioService;
boost::thread_group threadpool;
// Start the service
auto work = std::make_shared<boost::asio::io_service::work>(ioService);
// Add a single thread to the thread pool for Python operations
threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioService));
// Initialize Python
init();
std::cout << "Submitting init_thread" << std::endl;
ioService.post(boost::bind(init_thread));
// Create some plots on the background thread
std::cout << "Submitting PlotData calls" << std::endl;
ioService.post(boost::bind(PlotData,0));
ioService.post(boost::bind(PlotData,1));
// Pause to allow plots to update in realtime
boost::this_thread::sleep_for(boost::chrono::seconds(4));
// Receive inputs from command line (the plots should update during this time but they don't)
std::string inp{"1"};
std::cout << "Enter to quit\n";
while (!inp.empty())
{
std::cout << ">> ";
std::getline(std::cin,inp);
std::cout << "GIL State: " << PyGILState_Check() << std::endl;
}
// Finalize Python
std::cout << "Submitting Py_Finalize" << std::endl;
ioService.post(boost::bind(PyEval_RestoreThread,pts));
ioService.post(boost::bind(Py_Finalize));
// Allow jobs to complete and shut down the thread pool
work.reset();
threadpool.join_all();
ioService.stop();
return 0;
}