如何使用从外部脚本调用的回调发送信号?
How to send signal using callback called from external script?
简介
我正在尝试根据嵌入式 python 脚本中的计算状态更新 QT GUI 元素。我能够从 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 脚本中计算的阶段。它从 python 脚本 (MyModule.py):
循环调用
while r.successful() and k < num_steps:
r.integrate(r.t + delta_t)
callback(r.t, t_final)
但是编译失败并出现以下错误:
illegal call to nonstatic member function
nonstatic member reference must be relative to specific object
与emit inprogress(progr);
有关
问题
我认为我应该将对对象 的引用从我的 c++ 传递到 python,然后使用回调 返回到 c++。但我找不到如何做到这一点的方法。执行此操作的正确方法是什么?
经过测试的想法(仍然无效)
- 我试图像这样简单地传递它:
void class_name::callback(double t_prog, double t_final, class_name &cssd)
但是 python 好像不能自动转换。
正在创建新的 class 对象:
class_name cs;
emit cs.inprogress(progr);
编译没有错误,但信号永远不会到达插槽 - 它创建新对象而不是引用现有对象。
也许问题在于您正在调用 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
我们需要向回调添加一些额外的状态,即对 class 我们要调用其成员函数的实例的引用。
为此,我们可以使用 class。为了使功能等同于仅使用简单的静态回调函数,让我们定义 operator()
(即使其成为仿函数),并将此运算符暴露给 Python.
假设我们有以下应用程序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_;
};
在run()
中执行我们的Python脚本,我们希望它调用当前实例的成员函数callback
。
让我们定义以下回调处理程序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_;
};
我们需要将此 class 公开给 Python,但不希望能够从 Python 创建新实例,我们也不希望它被复制(尽管在这里并不重要,因为我们的状态只包含引用)。
BOOST_PYTHON_MODULE(cbtest)
{
bp::class_<callback_handler, boost::noncopyable>("callback_handler", bp::no_init)
.def("__call__", &callback_handler::operator())
;
};
在我们的应用程序开始时,我们需要确保在使用之前初始化我们的模块——在初始化 Python 解释器之后立即调用 initcbtest();
。
现在我们可以按以下方式使用我们的回调处理程序(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 脚本
文件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
简介
我正在尝试根据嵌入式 python 脚本中的计算状态更新 QT GUI 元素。我能够从 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 脚本中计算的阶段。它从 python 脚本 (MyModule.py):
while r.successful() and k < num_steps:
r.integrate(r.t + delta_t)
callback(r.t, t_final)
但是编译失败并出现以下错误:
illegal call to nonstatic member function
nonstatic member reference must be relative to specific object
与emit inprogress(progr);
问题
我认为我应该将对对象 的引用从我的 c++ 传递到 python,然后使用回调 返回到 c++。但我找不到如何做到这一点的方法。执行此操作的正确方法是什么?
经过测试的想法(仍然无效)
- 我试图像这样简单地传递它:
void class_name::callback(double t_prog, double t_final, class_name &cssd)
但是 python 好像不能自动转换。 正在创建新的 class 对象:
class_name cs; emit cs.inprogress(progr);
编译没有错误,但信号永远不会到达插槽 - 它创建新对象而不是引用现有对象。
也许问题在于您正在调用 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
我们需要向回调添加一些额外的状态,即对 class 我们要调用其成员函数的实例的引用。
为此,我们可以使用 class。为了使功能等同于仅使用简单的静态回调函数,让我们定义 operator()
(即使其成为仿函数),并将此运算符暴露给 Python.
假设我们有以下应用程序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_;
};
在run()
中执行我们的Python脚本,我们希望它调用当前实例的成员函数callback
。
让我们定义以下回调处理程序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_;
};
我们需要将此 class 公开给 Python,但不希望能够从 Python 创建新实例,我们也不希望它被复制(尽管在这里并不重要,因为我们的状态只包含引用)。
BOOST_PYTHON_MODULE(cbtest)
{
bp::class_<callback_handler, boost::noncopyable>("callback_handler", bp::no_init)
.def("__call__", &callback_handler::operator())
;
};
在我们的应用程序开始时,我们需要确保在使用之前初始化我们的模块——在初始化 Python 解释器之后立即调用 initcbtest();
。
现在我们可以按以下方式使用我们的回调处理程序(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 脚本
文件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