问题
Introduction
I'm trying to update QT GUI element basing on the state of calculations in embedded python script. I'm able to extract required values from python, but can't set a reference to c++ object to make it work.
The details
Let's assume python code is called (in calc.cpp) this way:
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
is a static
member function (in calc.cpp) I use to extract some values, indicating on which stage is calculation inside python script. It is called in loop from python script (MyModule.py):
while r.successful() and k < num_steps:
r.integrate(r.t + delta_t)
callback(r.t, t_final)
However the compilation fails with following error:
illegal call to nonstatic member function
nonstatic member reference must be relative to specific object
It is related to emit inprogress(progr);
The Question
I think I should pass a reference to the object from my c++ to python, and back to c++ with callback. But I can't find a way how to do this. What's the correct way to do this?
Tested ideas (still not working)
- I was trying to pass it simple like:
void class_name::callback(double t_prog, double t_final, class_name &cssd)
but python seems to can't convert it automatically. Creating of new class object:
class_name cs; emit cs.inprogress(progr);
Compile without error, but signal never reach the slot - it creates new object instead of refer to existing.
回答1:
We need to add some additional state to the callback, namely a reference to the instance of the class whose member function we want to invoke.
To do this, we can use a class. To make the functionality equivalent to just using a simple static callback function, let's define operator()
(i.e. make it a functor), and also expose this operator to Python.
Let's suppose we have the following application class:
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_;
};
In run()
we execute our Python script, and we want it to call the member function callback
of the current instance.
Let's define the following callback handler class:
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_;
};
We need to expose this class to Python, but don't want to be able to create new instances from Python, nor do we really want it to be copied (although it doesn't really matter here, since our state only consists of references).
BOOST_PYTHON_MODULE(cbtest)
{
bp::class_<callback_handler, boost::noncopyable>("callback_handler", bp::no_init)
.def("__call__", &callback_handler::operator())
;
};
At the start of our application, we need to make sure to initialize our module before we use it -- call initcbtest();
right after initializing the Python interpreter.
Now we can use our callback handler in the following manner (The Python code stays the same, since the object is callable):
callback_handler cbh(*this);
bp::object result = MyFunc(1, 10, 2, boost::ref(cbh));
std::cout << "result = " << bp::extract<double>(result) << "\n";
Sample Code
#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
File MyModule.py
:
def MyFunc(a, b, c, callback):
result = 0
for i in range(a, b, c):
result += i
callback(i, b)
return result
Console Output
CB(TestApp) 0 10
CB(TestApp) 2 10
CB(TestApp) 4 10
CB(TestApp) 6 10
CB(TestApp) 8 10
result = 20
回答2:
Maybe the problem is that you are calling emit c_n.inprogress(progr);
, where the argument of the signal progr
is of type double
, whereas in the connect(sender, SIGNAL( inprogress(int) ), ui->progressBar, SLOT( setValue(int) ) );
the signal takes an integer as argument. In older Qt versions(older than Qt5), signals and slots must use exactly the same types, meaning that the implicit conversion may not occur.
https://forum.qt.io/topic/23302/connect-diferent-signals-and-slost-each-other/4
来源:https://stackoverflow.com/questions/55010451/how-to-send-signal-using-callback-called-from-external-script