Как отправить сигнал с помощью обратного вызова, вызванного из внешнего скрипта? - PullRequest
1 голос
/ 05 марта 2019

Введение

Я пытаюсь обновить элемент графического интерфейса QT, основываясь на состоянии вычислений во встроенном скрипте Python.Я могу извлечь необходимые значения из Python, но не могу установить ссылку на объект C ++, чтобы он работал.

Подробности

Давайте предположим, что код Pythonвызывается (в calc.cpp) следующим образом:

void class_name::transfer(varA, varB, varC)
{
    Py_Initialize();
    emit inprogress(70); //HERE IT WORKS
    object module = import("__main__");
    object name_space = module.attr("__dict__");
    exec_file("MyModule.py", name_space, name_space);

    object MyFunc = name_space["MyFunc"];
    object result = MyFunc(varA, varB, varC, callback);

    double ret = extract<double>(result);
    Py_Finalize();
}

void class_name::callback(double t_prog, double t_final)
{
    progr = (double)t_prog / t_final * 100;
    cout << progr; //To check if value is updating (It is)
    emit inprogress(progr); //HERE IT FAIL
}

callback - это static функция-член (в calc.cpp), которую я использую для извлечения некоторых значений, указывающих, на какой стадии находится вычисление внутрискрипт на питоне.Он вызывается в цикле из скрипта python (MyModule.py):

while r.successful() and k < num_steps:
    r.integrate(r.t + delta_t)
    callback(r.t, t_final)

Однако компиляция завершается ошибкой:

недопустимый вызов функции-члена нестатического

ссылка на нестатический член должна относиться к конкретному объекту

Это связано с emit inprogress(progr);

Вопрос

Я думаю, что ядолжен передать ссылку на объект из моего c ++ в python и обратно в c ++ с обратным вызовом .Но я не могу найти способ сделать это.Какой правильный способ сделать это?

Проверенные идеи (все еще не работают)

  1. Я пытался передать это просто, как: void class_name::callback(double t_prog, double t_final, class_name &cssd), но Python, кажется, не может преобразовать его автоматически.
  2. Создание нового объекта класса:

    class_name cs;
    emit cs.inprogress(progr);
    

    Компиляция без ошибок, но сигнал никогда не достигает слота - он создает новый объект вместо ссылки на существующий.

Ответы [ 2 ]

1 голос
/ 08 марта 2019

Нам нужно добавить некоторое дополнительное состояние к обратному вызову, а именно ссылку на экземпляр класса, функцию-член которого мы хотим вызвать.

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

Предположим, у нас есть следующий класс приложения:

class app
{
public:
    explicit app(std::string name) : name_(std::move(name)) {}

    int run();

    void callback(double t_prog, double t_final);

private:
    std::string name_;
};

В run() мы выполняем наш скрипт Python и хотим, чтобы он вызывал функцию-член callback текущего экземпляра.

Давайте определим следующий класс обработчика обратного вызова:

class callback_handler
{
public:
    explicit callback_handler(app& a) : app_(a) {}

    void operator()(double t_prog, double t_final)
    {
        app_.callback(t_prog, t_final);
    }

private:
    app& app_;
};

Нам нужно представить этот класс Python, но мы не хотим иметь возможность создавать новые экземпляры из Python, и при этом мы не хотим, чтобы он копировался (хотя это не имеет большого значенияздесь, так как наше состояние состоит только из ссылок).

BOOST_PYTHON_MODULE(cbtest)
{
    bp::class_<callback_handler, boost::noncopyable>("callback_handler", bp::no_init)
        .def("__call__", &callback_handler::operator())
        ;
};

В начале нашего приложения мы должны обязательно инициализировать наш модуль перед его использованием - вызовите initcbtest(); сразу после инициализацииИнтерпретатор Python.

Теперь мы можем использовать наш обработчик обратного вызова следующим образом (код Python остается неизменным, так как объект вызывается):

    callback_handler cbh(*this);
    bp::object result = MyFunc(1, 10, 2, boost::ref(cbh));
    std::cout << "result = " << bp::extract<double>(result) << "\n";

Пример кода

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

#include <iostream>
// ============================================================================
namespace bp = boost::python;
// ============================================================================
class app
{
public:
    explicit app(std::string name) : name_(std::move(name)) {}

    int run();

    void callback(double t_prog, double t_final);

private:
    std::string name_;
};
// ============================================================================
class callback_handler
{
public:
    explicit callback_handler(app& a) : app_(a) {}

    void operator()(double t_prog, double t_final)
    {
        app_.callback(t_prog, t_final);
    }

private:
    app& app_;
};
// ----------------------------------------------------------------------------
BOOST_PYTHON_MODULE(cbtest)
{
    bp::class_<callback_handler, boost::noncopyable>("callback_handler", bp::no_init)
        .def("__call__", &callback_handler::operator())
        ;
};
// ============================================================================
void app::callback(double t_prog, double t_final)
{
    std::cout << "CB(" << name_ << ") " << t_prog << " " << t_final << "\n";
}
// ----------------------------------------------------------------------------
int app::run()
{
    Py_Initialize();
    initcbtest();

    try {
        bp::object module = bp::import("__main__");
        bp::object name_space = module.attr("__dict__");
        bp::exec_file("MyModule.py", name_space, name_space);

        bp::object MyFunc = name_space["MyFunc"];

        callback_handler cbh(*this);
        bp::object result = MyFunc(1, 10, 2, boost::ref(cbh));
        std::cout << "result = " << bp::extract<double>(result) << "\n";
    } catch (bp::error_already_set&) {
        PyErr_Print();
    }

    Py_Finalize();

    return 0;
}
// ============================================================================
int main()
{
    app a("TestApp");

    return a.run();
}
// ============================================================================

Python Script

Файл MyModule.py:

def MyFunc(a, b, c, callback):
    result = 0
    for i in range(a, b, c):
        result += i
        callback(i, b)
    return result

Консольный вывод

CB(TestApp) 0 10
CB(TestApp) 2 10
CB(TestApp) 4 10
CB(TestApp) 6 10
CB(TestApp) 8 10
result = 20
0 голосов
/ 07 марта 2019

Возможно, проблема в том, что вы вызываете emit c_n.inprogress(progr);, где аргумент сигнала progr имеет тип double, тогда как в connect(sender, SIGNAL( inprogress(int) ), ui->progressBar, SLOT( setValue(int) ) ); сигнал принимает целое число в качестве аргумента.В более старых версиях Qt (старше, чем Qt5) сигналы и слоты должны использовать точно такие же типы, что означает, что неявное преобразование может не произойти.

https://forum.qt.io/topic/23302/connect-diferent-signals-and-slost-each-other/4

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