有个新任务,需要显示sub到的数据内容,看起来挺简单的,毕竟原来那个工程采用zmq数据流的发送与接收时用到了启动新线程接收数据的方法。照搬肯定不行,因为有区别:原来工程直接在新线程里面持续接收数据,与界面的主线程没有关系。现在这种要用界面sub数据必须在主线程上启ROS节点不停的sub,与主线程本身的阻塞显示任务就冲突了,而且还要实现将收到的数据在界面上不断刷新的任务。
看来问题只能一个一个解决了。
解决第一个问题:主线程启动ROS节点持续sub和界面持续显示存在的冲突
解决办法:在ROS节点的cpp函数中创建界面实例并将其设置为主线程,将界面运行的代码放到这里。将callback函数放在实例化界面时实现,在界面的构造函数中启个新线程,将ROS的spin函数放在里面,搞定。
// show_node.cpp 节点名称就是 show_node
#include <ros/ros.h>
#include "form.h"
#include <QApplication>
#include "std_msgs/Int32.h"
int main(int argc, char **argv)
{
ros::init(argc, argv, "show_node");//初始化 show_node节点
QApplication a(argc, argv);
// 创建 Form 实例
Form form(nullptr);
ros::NodeHandle nh;
ros::Subscriber subdata = nh.subscribe<std_msgs::Int32>("pubdata", 10, boost::bind(&Form::staticShowCallback, _1, &form));//订阅话题 pubdata
form.show();
return a.exec();
}
界面Form的构造函数中添加callback函数的实现内容:
创建新线程,在头文件中定义
#include <thread>
…
class Form : public QWidget
{
Q_OBJECT
public:
...
private:
Ui::Form *ui;
std::thread rosthread;
};
在cpp文件中的构造函数中使用
Form::Form(QWidget *parent) :
QWidget(parent),
ui(new Ui::Form)
{
…
rosthread =std::thread(&Form::getrosdata,this);
}
将spin放在这个函数里面,就可以让sub运行在新的线程里面一直sub不停。
void Form::getrosdata()
{
ros::spin(); // Handle ROS callbacks in the separate thread
}
当然还是要停,什么时候界面退出了就停止sub,所以放这里
Form::~Form()
{
rosthread.join(); // Wait for the ROS thread to finish
delete ui;
}
show_node.cpp 节点名称就是 show_node
#include <ros/ros.h>
#include "form.h"
#include <QApplication>
#include "std_msgs/Int32.h"
int main(int argc, char **argv)
{
ros::init(argc, argv, "show_node");//初始化 show_node节点
QApplication a(argc, argv);
// 创建 Form 实例
Form form(nullptr);
ros::NodeHandle nh;
ros::Subscriber subdata = nh.subscribe<std_msgs::Int32>("pubdata", 10, boost::bind(&Form::staticShowCallback, _1, &form));//订阅话题 pubdata
form.show();
return a.exec();
}
界面Form的构造函数中添加callback函数的实现内容:
创建新线程,在头文件中定义
#include <thread>
…
class Form : public QWidget
{
Q_OBJECT
public:
...
private:
Ui::Form *ui;
std::thread rosthread;
};
在cpp文件中的构造函数中使用
Form::Form(QWidget *parent) :
QWidget(parent),
ui(new Ui::Form)
{
…
rosthread =std::thread(&Form::getrosdata,this);
}
将spin放在这个函数里面,就可以让sub运行在新的线程里面一直sub不停。
void Form::getrosdata()
{
ros::spin(); // Handle ROS callbacks in the separate thread
}
当然还是要停,什么时候界面退出了就停止sub,所以放这里
Form::~Form()
{
rosthread.join(); // Wait for the ROS thread to finish
delete ui;
}
解决第二个问题:用sub到的数据不停的刷新界面的显示数据内容
不停的刷新?难道又要启动一个线程?可是界面显示是在主线程里面做的啊。
解决办法:用QT自带的信号槽机制。信号槽机制的本质是当事件产生或者信号到来的时候就运行槽函数,不管什么时候运行多少次,别忘了这个运行的程序是在主线程中的,正好能够完美解决我们的问题。信号就用QT自带的定时器QTimer,简单好用,指定个结束时间让定时器不停的启动结束启动结束,每次结束就去调槽函数,用接收到的数据刷新界面显示的数据。
生成界面的主程序中添加:
timers = new QTimer(this);
timers->start(200);
connect(timers, SIGNAL(timeout()), this, SLOT(updateSlot()));
timers = new QTimer(this);
timers->start(200);
connect(timers, SIGNAL(timeout()), this, SLOT(updateSlot()));
定义void Form::updateSlot()
{
timeshow->setText(QString::number(ourtime)+" 秒");
countshow->setText(QString::number(outcount));
othertime->setText(QString::number(theirtime)+" 秒");
othercount->setText(QString::number(thericount));
}
timeshow->setText(QString::number(ourtime)+" 秒");
countshow->setText(QString::number(outcount));
othertime->setText(QString::number(theirtime)+" 秒");
othercount->setText(QString::number(thericount));
}
perfect!