先学习一波

先来看看CMakeList.txt

cmake_minimum_required(VERSION 2.8.3)
list( APPEND CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS}")

# PACKAGE SETUP ########################################

project(status_panel)

find_package(catkin REQUIRED COMPONENTS std_msgs roscpp rviz)

catkin_package(
  INCLUDE_DIRS include
  LIBRARIES ${PROJECT_NAME}
  CATKIN_DEPENDS std_msgs
)

# QT ########################################

set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

## This plugin includes Qt widgets, so we must include Qt.
## We'll use the version that rviz used so they are compatible.
if(rviz_QT_VERSION VERSION_LESS "5")
  message(STATUS "Using Qt4 based on the rviz_QT_VERSION: ${rviz_QT_VERSION}")
  find_package(Qt4 ${rviz_QT_VERSION} EXACT REQUIRED QtCore QtGui)
  ## pull in all required include dirs, define QT_LIBRARIES, etc.
  include(${QT_USE_FILE})
  qt4_wrap_cpp(QT_MOC include/status_panel/status_panel.h)
else()
  message(STATUS "Using Qt5 based on the rviz_QT_VERSION: ${rviz_QT_VERSION}")
  find_package(Qt5 ${rviz_QT_VERSION} EXACT REQUIRED Core Widgets)
  ## make target_link_libraries(${QT_LIBRARIES}) pull in all required dependencies
  set(QT_LIBRARIES Qt5::Core Qt5::Widgets)
  qt5_wrap_cpp(QT_MOC include/status_panel/status_panel.h)
endif()

add_definitions(-DQT_NO_KEYWORDS)

# FILES ########################################

include_directories(
  ${catkin_INCLUDE_DIRS}
  include/
)
set(SOURCES src/status_panel.cpp src/status_logger.cpp ${QT_MOC})

# LIBRARY ########################################
add_library(${PROJECT_NAME} ${SOURCES})

target_link_libraries(${PROJECT_NAME} ${QT_LIBRARIES} ${catkin_LIBRARIES})


# INSTALL ########################################
install(TARGETS
  ${PROJECT_NAME}
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

install(FILES
  plugin_description.xml
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})

install(DIRECTORY icons/
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/icons)

看不懂的先在上面学习一波,主要分为5个部分:PACKAGE SETUP (自身包的配置)、QT(QT环境的配置)、FILES(文件引入)、LIBRARY(生成库文件设置)、INSTALL(打包发布的配置)

再来看看status_panel.h

///
//      Title     : Status panel
//      Project   : ROSSTEP
//      Created   : 7/15/2015
//      Author    : Adam Allevato
//      Platforms : Ubuntu 64-bit
//      Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved.
//                 
//          All files within this directory are subject to the following, unless an alternative
//          license is explicitly included within the text of each file.
//
//          This software and documentation constitute an unpublished work
//          and contain valuable trade secrets and proprietary information
//          belonging to the University. None of the foregoing material may be
//          copied or duplicated or disclosed without the express, written
//          permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY
//          AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION,
//          INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//          PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY
//          THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE.
//          NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF
//          THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the
//          University be liable for incidental, special, indirect, direct or
//          consequential damages or loss of profits, interruption of business,
//          or related expenses which may arise from use of software or documentation,
//          including but not limited to those resulting from defects in software
//          and/or documentation, or loss or inaccuracy of data of any kind.
//
///

#ifndef STATUS_PANEL_H
#define STATUS_PANEL_H

#ifndef Q_MOC_RUN
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <rviz/panel.h>
#endif

class QLineEdit;
class QLabel;

namespace status_panel {

class StatusPanel: public rviz::Panel
{
Q_OBJECT
public:
  StatusPanel( QWidget* parent = 0 );

  virtual void load( const rviz::Config& config );
  virtual void save( rviz::Config config ) const;

  void message_cb(std_msgs::String msg);

public Q_SLOTS:
  void setMessage( const QString& message );
  void setTopic();

protected:

  /// One-line text editor for entering the ROS topic to monitor for messages.
  QLineEdit* input_topic_editor;

  /// Where to display the status messages.
  QLabel* message_display;

  /// The current name of the input topic.
  QString input_topic;

  /// The ROS publisher for the incoming messages.
  ros::Subscriber subscriber;

  /// The ROS node handle.
  ros::NodeHandle nh;

};

} // end namespace

#endif

这里StatusPanel继承了rviz::Panel类,覆写了2个函数

  virtual void load( const rviz::Config& config );
  virtual void save( rviz::Config config ) const;

使用了2个Qt控件,用于接收和显示

  /// One-line text editor for entering the ROS topic to monitor for messages.
  QLineEdit* input_topic_editor;

  /// Where to display the status messages.
  QLabel* message_display;

其他没有什么了,具体实现的cpp如下

///
//      Title     : Status logger panel
//      Project   : ROSSTEP
//      Created   : 7/15/2015
//      Author    : Adam Allevato
//      Platforms : Ubuntu 64-bit
//      Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved.
//                 
//          All files within this directory are subject to the following, unless an alternative
//          license is explicitly included within the text of each file.
//
//          This software and documentation constitute an unpublished work
//          and contain valuable trade secrets and proprietary information
//          belonging to the University. None of the foregoing material may be
//          copied or duplicated or disclosed without the express, written
//          permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY
//          AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION,
//          INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//          PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY
//          THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE.
//          NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF
//          THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the
//          University be liable for incidental, special, indirect, direct or
//          consequential damages or loss of profits, interruption of business,
//          or related expenses which may arise from use of software or documentation,
//          including but not limited to those resulting from defects in software
//          and/or documentation, or loss or inaccuracy of data of any kind.
//


#include "status_panel/status_panel.h"

#include <stdio.h>

#include <QPainter>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QTimer>

#include <geometry_msgs/Twist.h>

namespace status_panel {

StatusPanel::StatusPanel( QWidget* parent ) :
  rviz::Panel( parent ),
  input_topic("/status")
{
  // Next we lay out the "output topic" text entry field using a
  // QLabel and a QLineEdit in a QHBoxLayout.
  QHBoxLayout* topic_layout = new QHBoxLayout;
  topic_layout->addWidget( new QLabel( "Status Topic:" ));
  input_topic_editor = new QLineEdit;
  topic_layout->addWidget( input_topic_editor );

  message_display = new QLabel("");
  message_display->setTextFormat(Qt::RichText);
  message_display->setAlignment(Qt::AlignCenter);

  // Lay out the topic field next to the control widrivzget.
  QGridLayout* layout = new QGridLayout();
  layout->setColumnStretch(1,100);
  layout->addWidget( message_display, 0,0,1,4);
  layout->addLayout( topic_layout, 0,4,1,1 );
  setLayout( layout );

  input_topic_editor->resize(150, input_topic_editor->height());

  // Next we make signal/slot connections.
  connect( input_topic_editor, SIGNAL( editingFinished() ), this, SLOT( setTopic() ));

  input_topic_editor->setText( input_topic );
  setTopic();
}

void StatusPanel::setTopic()
{
  if(subscriber) {
    subscriber.shutdown();
  }
  input_topic = input_topic_editor->text();
  subscriber = nh.subscribe(std::string(input_topic.toStdString()), 100, &StatusPanel::message_cb, this);
  Q_EMIT configChanged();
}

// Save all configuration data from this panel to the given
// Config object.  It is important here that you call save()
// on the parent class so the class id and panel name get saved.
void StatusPanel::save( rviz::Config config ) const
{
  rviz::Panel::save( config );
  config.mapSetValue( "topic", input_topic );
}

// Load all configuration data for this panel from the given Config object.
void StatusPanel::load( const rviz::Config& config )
{
  rviz::Panel::load( config );
  QString topic;
  if( config.mapGetString( "topic", &topic ))
  {
    input_topic_editor->setText( topic );
    setTopic();
  }
}

void StatusPanel::setMessage( const QString& msg) {
  message_display->setText(QString("<span style='font-weight: bold; font-size: 14pt;'>") + msg + "</span>");
}

void StatusPanel::message_cb(std_msgs::String msg)
{
  setMessage(QString(msg.data.c_str()));
}


} // end namespace

// Tell pluginlib about this class.  Every class which should be
// loadable by pluginlib::ClassLoader must have these two lines
// compiled in its .cpp file, outside of any namespace scope.
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(status_panel::StatusPanel,rviz::Panel )

最后一步编写plugin_description.xml文件

<library path="libstatus_panel">
  <class name="Status"
         type="status_panel::StatusPanel"
         base_class_type="rviz::Panel">
    <description>
      A simple panel to display info messages to the user.
    </description>
  </class>
</library>

我们可能还注意到了插件的icons图标,放一个png在路径/icons/classes/*.png即可。