Не удается получить обработчик событий matplotlib, работающий с Boost.Python - PullRequest
0 голосов
/ 05 января 2019

Я занимаюсь разработкой программы на 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;
}
...