Вызов функции экземпляра объекта с использованием встроенного Python - PullRequest
0 голосов
/ 30 августа 2018

Я хочу иметь возможность запускать скрипты Python в своем приложении, чтобы позволить автоматизировать вещи и модифицировать существующие объекты / вызывать методы существующих объектов.

В моем приложении есть класс BasicWindow и класс MainWindow, производные от первого. А пока при запуске приложения я инициализирую один экземпляр MainWindow. Этот объект имеет много функций, среди которых есть одна, которая загружает файлы (LoadFile()), и я буду использовать ее в качестве примера здесь.

Допустим, я хочу вызвать эту конкретную функцию (но не ограничиваясь этой функцией, это всего лишь пример функциональности, которую я хочу добиться с помощью Python) этого конкретного экземпляра объекта.

Этот метод не является статическим. Для этого я использую Boost.Python и создаю модуль следующим образом:

BOOST_PYTHON_MODULE(MyModule)
{

    MainWindow::PythonExpose(); //not really sure how to operate here
    //more stuff
}

Идея состоит в том, что я мог бы вызвать из Python что-то вроде:

MainWindow.LoadFile()

или даже лучше, просто:

LoadFile()

Одним из решений может быть создание статических функций в рамках приложения, а затем просто предоставление этих функций. В C ++ я мог найти конкретный экземпляр MainWindow: (оба метода являются статическими)

void AppHelper::LoadFile()
{
    GetMainWindow()->LoadFile();
}


void AppHelper::PythonExposeGlobal()
{
    using namespace boost::python;
    def("LoadFile", &AppHelper::LoadFile);
}

Возможно ли этого добиться? Общий вопрос: можно ли вызывать методы существующих объектов (в C ++) из Python? Если да, то как это сделать? Если нет, что я могу сделать, чтобы имитировать это поведение?

Например, я мог бы легко включить возможности сценариев в своем приложении C # и обмениваться экземплярами существующих объектов. (Но, конечно, у C # есть отражение).

1 Ответ

0 голосов
/ 30 августа 2018

Если вы можете гарантировать, что объект будет жить так же долго, как и любые сценарии, использующие его, то я использую довольно простой подход.

Я буду использовать примитивный класс счетчика для демонстрации:

class counter
{
public:
    counter() : count(0) {}

    void increment() { ++count; }

    int count;
};

Теперь я предоставляю этот класс python, так что он считает, что он не подлежит копированию, и не позволяет создавать новые экземпляры. Я также выставляю всех участников, которых я хочу использовать из сценариев.

BOOST_PYTHON_MODULE(example)
{
    bp::class_<counter, boost::noncopyable>("Counter", bp::no_init)
        .def("increment", &counter::increment)
        ;
}

Следующий шаг - создать объект Python, который использует существующий экземпляр, и разрешить сценарию использовать его (например, добавить его в качестве атрибута какого-либо модуля, например основного).

counter c;

bp::object main_module(bp::import("__main__"));

main_module.attr("c") = bp::object(bp::ptr(&c));

Теперь ваши сценарии могут использовать этот экземпляр:

c.increment()

Пример программы:

#include <boost/python.hpp>
#include <iostream>

namespace bp = boost::python;

// Simple counter that can be incremented
class counter
{
public:
    counter() : count(0) {}

    void increment() { ++count; }

    int count;
};

// Expose the counter class to Python
// We don't need constructor, since we only intend to use instance
// already existing on the C++ side
BOOST_PYTHON_MODULE(example)
{
    bp::class_<counter, boost::noncopyable>("Counter", bp::no_init)
        .def("increment", &counter::increment)
        ;
}

int main()
{
    Py_InitializeEx(0);

    // Bind our class
    initexample();

    counter c;

    bp::object main_module(bp::import("__main__"));
    bp::object main_namespace(main_module.attr("__dict__"));

    // Add the current instance of counter to Python as attribute c of the main module
    main_module.attr("c") = bp::object(bp::ptr(&c));

    std::cout << "Before: " <<  c.count << '\n';

    // Increment the counter from Python side
    bp::exec("c.increment()", main_namespace);

    std::cout << "After: " << c.count << '\n';

    Py_Finalize();
    return 0;
}

Вывод на консоль:

Before: 0
After: 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...