ROS项目开发实战(三)——使用QT进行ROS的GUI界面设计(上)

    本篇博客主要介绍怎么使用qt对ros进行gui设计与调试,包括使用List View显示ROS话题发布与接收的消息,点击QT按钮pushbutton进行ros消息的发布

在阅读本文之前没有安装qt与配置环境可以参考博文:如何用Qt对ROS项目进行调试及创建GUI界面


一、ROS下创建QT_GUI文件

    (1)安装 catkin_create_qt_pkg ROS包,打开终端输入以下命令:

$ sudo apt-get install ros-<distro>-qt-ros     //distro是ros版本,例如LZ的就是 ros-hydro-qt-ros

    (2)进入工作空间使用如下命令创建基于qt的ros_gui文件:

$ cd catkin_ws/src        //进入自己的工作空间
$ catkin_create_qt_pkg ros_gui      //创建ros_gui包


    (3)如下图,在终端输入qtcreator打开qt(或者在桌面配置了QT-ROS启动快捷方式的直接点击启打开),选择打开项目进入ros_gui文件下选择CMakeList.txt文件打开:


    (4)进入CMake向导构建路径步骤点击下一步:


    (5)在执行Cmake步骤中,参数选项输入:-DCMAKE_BUILD_TYPE=DEBUG,然后点击执行CMake,最后点击完成:


    (6)如下图,可以看到打开的ros_gui项目(吐槽下这里居然不显示头文件目录,导致编辑头文件代码不方便,建议将头文件与cpp文件放在一起再导入,导入后修改cpp中头文件路径):



二、在QT视图中显示ROS话题发布与接收的消息

    (1)打开终端roscore启动ros_Master,在qt上直接编译运行ros_gui项目,填写自己的ROS信息(ROS_MASTER_Url可直接在终端中查看,ROS_IP可以在终端输入ifconfig命令查看),点击connect,如下图可以看到程序内置了一个文本视图与ros发送节点,视图上显示节点发送的信息:

    (2)下面在窗口空间中添加一个文本视图使其显示接收到的消息,打开main_window.ui文件,拖入一个List View控件并将其对象名更改为view_logging_sub,然后拖入两个Label控件到对应的视图空间上并分别改为sub与pub:


    (3)打开qnode.hpp(项目列表不显示的直接打开项目include文件夹拖入qt)与qnode.cpp,在其中添加代码(添加部分代码标记为红色//add):

<qnode.hpp>:
/*****************************************************************************
** Includes
*****************************************************************************/
#include <ros/ros.h>
#include <string>
#include <QThread>
#include <QStringListModel>

#include <std_msgs/String.h>  //add

public:
	/*********************
	** Logging
	**********************/
    QStringListModel* loggingModel() { return &logging_model; }
    void log( const LogLevel &level, const std::string &msg);

    QStringListModel* loggingModel_sub() { return &logging_model_sub; } //add
    void log_sub( const LogLevel &level, const std::string &msg);  //add
    void Callback(const std_msgs::StringConstPtr& submsg); //add

Q_SIGNALS:
    void loggingUpdated();
    void rosShutdown();

    void loggingUpdated_sub();  //add

private:
	int init_argc;
	char** init_argv;
	ros::Publisher chatter_publisher;
    QStringListModel logging_model;

    ros::Subscriber chatter_subscriber; //add
    QStringListModel logging_model_sub;  //add
};
}  // namespace ros_gui
#endif /* ros_gui_QNODE_HPP_ */
<qnode.cpp>:

bool QNode::init() {
	ros::init(init_argc,init_argv,"ros_gui");
	if ( ! ros::master::check() ) {
		return false;
	}
	ros::start(); // explicitly needed since our nodehandle is going out of scope.
	ros::NodeHandle n;
	// Add your ros communications here.
	chatter_publisher = n.advertise<std_msgs::String>("chatter", 1000);

        chatter_subscriber=n.subscribe("chatter",1000,&QNode::Callback,this);  //add
	start();
	return true;
}

bool QNode::init(const std::string &master_url, const std::string &host_url) {
	std::map<std::string,std::string> remappings;
	remappings["__master"] = master_url;
	remappings["__hostname"] = host_url;
	ros::init(remappings,"ros_gui");
	if ( ! ros::master::check() ) {
		return false;
	}
	ros::start(); // explicitly needed since our nodehandle is going out of scope.
	ros::NodeHandle n;
	// Add your ros communications here.
	chatter_publisher = n.advertise<std_msgs::String>("chatter", 1000);

        chatter_subscriber=n.subscribe("chatter",1000,&QNode::Callback,this);  //add
	start();
	return true;
}

void QNode::log_sub( const LogLevel &level, const std::string &msg) {  //add
    logging_model_sub.insertRows(logging_model_sub.rowCount(),1);
    std::stringstream logging_model_msg;
    switch ( level ) {
        case(Debug) : {
                ROS_DEBUG_STREAM(msg);
                logging_model_msg << "[DEBUG] [" << ros::Time::now() << "]: " << msg;
                break;
        }
        case(Info) : {
                ROS_INFO_STREAM(msg);
                logging_model_msg << "[INFO] [" << ros::Time::now() << "]: " << msg;
                break;
        }
        case(Warn) : {
                ROS_WARN_STREAM(msg);
                logging_model_msg << "[INFO] [" << ros::Time::now() << "]: " << msg;
                break;
        }
        case(Error) : {
                ROS_ERROR_STREAM(msg);
                logging_model_msg << "[ERROR] [" << ros::Time::now() << "]: " << msg;
                break;
        }
        case(Fatal) : {
                ROS_FATAL_STREAM(msg);
                logging_model_msg << "[FATAL] [" << ros::Time::now() << "]: " << msg;
                break;
        }
    }
    QVariant new_row(QString(logging_model_msg.str().c_str()));
    logging_model_sub.setData(logging_model_sub.index(logging_model_sub.rowCount()-1),new_row);
    Q_EMIT loggingUpdated_sub(); // used to readjust the scrollbar
}

void QNode::Callback(const std_msgs::StringConstPtr& submsg)  //add
{
    log_sub(Info,std::string("Success sub: ")+submsg->data.c_str());
}
}  // namespace ros_gui

    (4)再打开main_window.hpp(项目列表不显示的直接打开项目include文件夹拖入qt)与main_window.cpp,在其中添加代码(添加部分代码标记为红色//add):

<main_window.hpp>:

public Q_SLOTS:
    /******************************************
    ** Manual connections
    *******************************************/
    void updateLoggingView(); // no idea why this can't connect automatically

    void updateLoggingView_sub(); //add
/*****/
<main_window.cpp>:

    /*********************
    ** Logging
    **********************/
    ui.view_logging->setModel(qnode.loggingModel());
    QObject::connect(&qnode, SIGNAL(loggingUpdated()), this, SLOT(updateLoggingView()));

    ui.view_logging_sub->setModel(qnode.loggingModel_sub()); //add
    QObject::connect(&qnode, SIGNAL(loggingUpdated_sub()), this, SLOT(updateLoggingView_sub()));  //add

/*****************************************************************************
** Implemenation [Slots][manually connected]
*****************************************************************************/

void MainWindow::updateLoggingView() {
        ui.view_logging->scrollToBottom();
}
void MainWindow::updateLoggingView_sub() {  //add
        ui.view_logging_sub->scrollToBottom();
}
/*********/
    (5)如下图,保存修改后的文件并且编译运行可以实现在gui显示消息的接收与发送:


三、使用QT按钮进行消息的发布

下面实现通过点击按钮进行ros消息的发送:

    (1)如下图打开main_window.ui,拖入一个Push button控件,将对象名改为sent_cmd


    (2)打开qnode.hpp(项目列表不显示的直接打开项目include文件夹拖入qt)与qnode.cpp,在其中添加代码(添加部分代码标记为红色//add):

<qnode.hpp>:
class QNode : public QThread {
    Q_OBJECT
public:
	/*********************
	** Logging
	**********************/
    void sent_cmd(); //add
<qnode.cpp>:
/*****************************************************************************
** Implementation
*****************************************************************************/
void QNode::sent_cmd() //add
{
 if( ros::ok() ) {
      std_msgs::String msg;
      std::stringstream ss;
      ss << "clicked the pushbutton";
      msg.data = ss.str();
       chatter_publisher.publish(msg);
       log(Info,std::string("I sent: ")+msg.data);
       ros::spinOnce();
    }
}
(4)再打开 main_window.hpp(项目列表不显示的直接打开项目include文件夹拖入qt)与 main_window.cpp,在其中添加代码(添加部分代码标记为红色 //add):

 
 
<main_window.hpp>:
public Q_SLOTS:
    /******************************************
    ** Manual connections
    *******************************************/
    void pub_cmd();//add
<main_window.cpp>:
	/*********************
	** Logging
	**********************/
    QObject::connect(ui.sent_cmd, SIGNAL(clicked()), this, SLOT(pub_cmd()));//add

    /*********************
    ** Auto Start
    **********************/
    void MainWindow::pub_cmd(){ //add
    qnode.sent_cmd();
    }

      (5)最后编译运行,如下图,点击sent_cmd按钮可以看到发送的消息:


下一篇博客会详细讲解怎么在gui中显示rviz,怎么使用按钮运行ros启动文件launch。

猜你喜欢

转载自blog.csdn.net/Ricardo_lun/article/details/80025024