第六篇 在ROS工程中使用QT开发界面笔记之--混用线程和信号槽机制

有个新任务,需要显示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("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("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!


你可能感兴趣的:(命令模式,qt,c++,笔记)