From d08dbde4da1ec2c28619a9b91b228fa49cd2bbac Mon Sep 17 00:00:00 2001 From: NoError <2381910282@qq.com> Date: Wed, 13 Sep 2023 09:21:04 +0000 Subject: [PATCH] src Signed-off-by: NoError <2381910282@qq.com> --- src/doc/conf.py | 25 ++++ src/doc/index.rst | 67 +++++++++ src/doc/myviz.png | Bin 0 -> 13519 bytes src/doc/tutorialformatter.py | 132 +++++++++++++++++ src/main.cpp | 56 ++++++++ src/myviz.cpp | 270 +++++++++++++++++++++++++++++++++++ src/myviz.h | 79 ++++++++++ src/qthread.h | 20 +++ 8 files changed, 649 insertions(+) create mode 100644 src/doc/conf.py create mode 100644 src/doc/index.rst create mode 100644 src/doc/myviz.png create mode 100644 src/doc/tutorialformatter.py create mode 100644 src/main.cpp create mode 100644 src/myviz.cpp create mode 100644 src/myviz.h create mode 100644 src/qthread.h diff --git a/src/doc/conf.py b/src/doc/conf.py new file mode 100644 index 0000000..bb18b12 --- /dev/null +++ b/src/doc/conf.py @@ -0,0 +1,25 @@ +import sys, os + +sys.path += [ os.path.abspath( '.' )] + +extensions = [ 'sphinx.ext.extlinks', + 'tutorialformatter' ] + +# The master toctree document. +master_doc = 'index' + +# The suffix of source filenames. +source_suffix = '.rst' + +project = u'librviz_tutorial' + +copyright = u'2012, Willow Garage, Inc' + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +extlinks = {'codedir': ('https://github.com/ros-visualization/visualization_tutorials/blob/hydro-devel/librviz_tutorial/%s', '')} diff --git a/src/doc/index.rst b/src/doc/index.rst new file mode 100644 index 0000000..28910ab --- /dev/null +++ b/src/doc/index.rst @@ -0,0 +1,67 @@ +Librviz Tutorial +================ + +Overview +-------- + +RViz is not just a visualizer application, it is also a library! Much +of RViz's functionality can be accessed within your own application by +linking against librviz.so (or whatever your OS likes to call it). + +This tutorial shows a very simple example of creating a 3D visualizer +widget (rviz::RenderPanel), programmatically creating a new Grid +display within it, then using Qt slider controls to adjust a couple of +the grid's properties. The app is called "myviz". + +The source code for this tutorial is in the librviz_tutorial +package. You can check out the source directly or (if you use Ubuntu) +you can just apt-get install the pre-compiled Debian package like so:: + + sudo apt-get install ros-hydro-visualization-tutorials + +The running application looks like this: + +.. image:: myviz.png + +The Code +-------- + +The code for myviz is in these files: +:codedir:`src/main.cpp`, +:codedir:`src/myviz.h`, and +:codedir:`src/myviz.cpp`. + +main.cpp +^^^^^^^^ + +The full text of main.cpp is here: :codedir:`src/main.cpp` + +.. tutorial-formatter:: ../main.cpp + +myviz.h +^^^^^^^ + +The full text of myviz.h is here: :codedir:`src/myviz.h` + +.. tutorial-formatter:: ../myviz.h + +myviz.cpp +^^^^^^^^^ + +The full text of myviz.cpp is here: :codedir:`src/myviz.cpp` + +.. tutorial-formatter:: ../myviz.cpp + +Building +-------- + +The full text of CMakeLists.txt is here: :codedir:`CMakeLists.txt` + +.. tutorial-formatter:: ../../CMakeLists.txt + +Running +------- + +Just type:: + + rosrun librviz_tutorial myviz diff --git a/src/doc/myviz.png b/src/doc/myviz.png new file mode 100644 index 0000000000000000000000000000000000000000..ab623d2ac964cbe1cc0d95688c5d3d528b6a1097 GIT binary patch literal 13519 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 I)F|M80oj|XL;wH) literal 0 HcmV?d00001 diff --git a/src/doc/tutorialformatter.py b/src/doc/tutorialformatter.py new file mode 100644 index 0000000..8260942 --- /dev/null +++ b/src/doc/tutorialformatter.py @@ -0,0 +1,132 @@ +""" + tutorialformatter + =========================== + + This extension provides a directive to include a source code file + in a document, but with certain comments from the file formatted + as regular document text. This allows code for a tutorial to look like: + + /// BEGIN_TUTORIAL + /// This next line adds one. + i = i + 1; + /// Then we need to double it. + i = i * 2; + /// END_TUTORIAL + + And have it formatted as + + This next line adds one.:: + i = i + 1; + + Then we need to double it.:: + i = i * 2; + + The special-looking comment character sequence at the start of + each text line can be anything not starting or ending with + whitespace. tutorialformatter starts by scanning the file for the + string BEGIN_TUTORIAL. When it finds it, it takes all the + characters before BEGIN_TUTORIAL on that line, strips whitespace + from the left, and uses that as the text marker. So this would + also be fine: + + #My Tutorial# BEGIN_TUTORIAL + #My Tutorial# This next line adds one. + i = i + 1 + #My Tutorial# Then we need to double it. + i = i * 2 + #My Tutorial# END_TUTORIAL + + .. moduleauthor:: Dave Hershberger +""" + +__version__ = '0.1.0' + +import os +from docutils.parsers import rst +from docutils.parsers.rst.directives import flag, unchanged +from docutils.statemachine import string2lines +from pygments.lexers import get_lexer_for_filename + +class TutorialFormatterDirective(rst.Directive): + has_content = False + final_argument_whitespace = True + required_arguments = 1 + + option_spec = dict(shell=flag, prompt=flag, nostderr=flag, + in_srcdir=flag, extraargs=unchanged, + until=unchanged) + + def run(self): + filename = self.arguments[0] + text_tag = None + tag_len = 0 + + filepath = self.state.document.settings.env.srcdir + absfilename = os.path.join( filepath, filename ) + if absfilename.endswith('.h'): + language = 'c++' + elif absfilename.endswith('CMakeLists.txt'): + language = 'cmake' + else: + try: + language = get_lexer_for_filename( absfilename ).name.lower() + if language == 'text only': + language = 'none' + except: + language = 'none' + code_prefix = '\n.. code-block:: ' + language + '\n\n' + code_suffix = '\n' + + print "tutorial-formatter running on", absfilename + file_ = open( absfilename, 'r' ) + text_to_process = "" + current_block = "" + in_code = False + in_text = False + in_tutorial = False + for line in file_: + if not in_tutorial: + begin_pos = line.find( 'BEGIN_TUTORIAL' ) + if begin_pos != -1: + text_tag = line[:begin_pos].lstrip() + tag_len = len( text_tag ) + in_tutorial = True + continue + if line.find( 'END_TUTORIAL' ) != -1: + break + stripped = line.lstrip() + if stripped.startswith( text_tag.strip() ): + if in_code: + text_to_process += code_prefix + current_block + code_suffix + current_block = "" + in_code = False + in_text = True + addition = stripped[tag_len:] + if addition == '' or addition[-1] != '\n': + addition += '\n' + current_block += addition + else: + if in_text: + text_to_process += current_block + current_block = "" + in_text = False + in_code = True # Code to show begins right after tagged text + if in_code: + current_block += ' ' + line + if in_code: + text_to_process += code_prefix + current_block + code_suffix + elif in_text: + text_to_process += current_block + + # Debug writes... + #print 'text_to_process =' + #print text_to_process + #print '= text_to_process' + + lines = string2lines( text_to_process ) + self.state_machine.insert_input( lines, absfilename ) + + return [] + +def setup(app): + app.add_directive('tutorial-formatter', TutorialFormatterDirective) diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..c6ad557 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +// BEGIN_TUTORIAL + +// The main() for this "myviz" example is very simple, it just +// initializes ROS, creates a QApplication, creates the top-level +// widget (of type "MyViz"), shows it, and runs the Qt event loop. + +#include +#include +#include "myviz.h" + +int main(int argc, char **argv) +{ + if( !ros::isInitialized() ) + { + ros::init( argc, argv, "myviz", ros::init_options::AnonymousName ); + } + + QApplication app( argc, argv ); + + MyViz* myviz = new MyViz(); + myviz->setGeometry(0, 0, 2000, 1000); + myviz->show(); + + app.exec(); + + delete myviz; +} diff --git a/src/myviz.cpp b/src/myviz.cpp new file mode 100644 index 0000000..cee9b81 --- /dev/null +++ b/src/myviz.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rviz/visualization_manager.h" +#include "rviz/render_panel.h" +#include "rviz/display.h" + +#include "myviz.h" +#include "qthread.h" + +// BEGIN_TUTORIAL +// Constructor for MyViz. This does most of the work of the class. +MyViz::MyViz( QWidget* parent ) + : QWidget( parent ) +{ + + nh=ros::NodeHandle("~"); + sub=nh.subscribe("/test",1000,&MyViz::subCallback,this); + pub=nh.advertise("/test_pub",1000); + pub_thread=std::thread(&MyViz::pubThread,this); + //pub_thread.join(); + // Construct and lay out labels and slider controls. + QLabel* thickness_label = new QLabel( "Line Thickness" ); + QSlider* thickness_slider = new QSlider( Qt::Horizontal ); + thickness_slider->setMinimum( 1 ); + thickness_slider->setMaximum( 100 ); + QLabel* cell_size_label = new QLabel( "Cell Size" ); + QSlider* cell_size_slider = new QSlider( Qt::Horizontal ); + cell_size_slider->setMinimum( 1 ); + cell_size_slider->setMaximum( 100 ); + QLabel* Topic=new QLabel("TOPIC:"); + QLineEdit* Topic_text=new QLineEdit(); + QGridLayout* controls_layout = new QGridLayout(); + QLabel* Size=new QLabel("Cloud Size"); + QSpinBox* size=new QSpinBox(); + + /*button goto file begining*/ + QPushButton* but_reset = new QPushButton("reset", this); + /*button goto file ending*/ + QPushButton* but_ending = new QPushButton("end", this); + + //TODO: set button color + //button->setStyleSheet("background-color: red; color: white;"); + //TODO: set shortcut key + //button->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A)); + + + //enable button + but_reset->setEnabled(true); + but_ending->setEnabled(true); + + but_reset->setFixedSize(100, 30); + but_ending->setFixedSize(100, 30); + + controls_layout->addWidget( thickness_label, 0, 0 ); + controls_layout->addWidget( thickness_slider, 0, 1 ); + controls_layout->addWidget( cell_size_label, 0, 2 ); + controls_layout->addWidget( cell_size_slider, 0, 3 ); + + //button locatin + controls_layout->addWidget( but_reset, 1, 0 ); + controls_layout->addWidget( but_ending, 1, 1 ); + + controls_layout->addWidget(Topic,0,4); + controls_layout->addWidget(Topic_text,0,5); + controls_layout->addWidget(Size,0,6); + controls_layout->addWidget(size,0,7); + + + // Construct and lay out render panel. + render_panel_ = new rviz::RenderPanel(); + QVBoxLayout* main_layout = new QVBoxLayout; + main_layout->addLayout( controls_layout ); + main_layout->addWidget( render_panel_ ); + + // Set the top-level layout for this MyViz widget. + setLayout( main_layout ); + + // Make signal/slot connections. + connect( thickness_slider, SIGNAL( valueChanged( int )), this, SLOT( setThickness( int ))); + connect( cell_size_slider, SIGNAL( valueChanged( int )), this, SLOT( setCellSize( int ))); + //connect(Topic_text,SIGNAL(textChanged(const QString &)),this,SLOT(setCloudTopic(const QString &))); + + //topic change signal + connect(Topic_text,SIGNAL(textChanged(const QString &)),this,SLOT(setlaserTopic(const QString &))); + //button signal + connect(but_ending, &QPushButton::clicked, this, [&]() { + QStringList arguments, infoarg; + QString time; + QString file_path = "/home/noerror/123/catkin_for_ros/src/librviz_tutorial/data/result.bag"; + QProcess *rosbaginfoProcess = new QProcess(this); + + infoarg << "info" << file_path; + rosbaginfoProcess->start("rosbag", infoarg); + + if(rosbaginfoProcess->waitForFinished()){ + QByteArray output = rosbaginfoProcess->readAllStandardOutput(); + QString outputString = QString::fromLocal8Bit(output); + QString delimiter = "duration:"; + outputString = outputString.section(delimiter, 1, 1); + delimiter = "\n"; + outputString = outputString.section(delimiter, 0, 0); + outputString.remove(" "); + outputString.remove("s"); + double number = outputString.toDouble(); //qstring convert to double + time = QString::number(number-0.1); + qDebug() << time; + } + + arguments << "play" << file_path << "--start" << time; + QProcess *rosbagProcess = new QProcess(this); + //QProcess::execute("rosbag play /home/noerror/123/catkin_for_ros/src/librviz_tutorial/data/result.bag"); + + rosbagProcess->start("rosbag", arguments); + + }); + connect(but_reset, &QPushButton::clicked, this, [&]() { + QStringList arguments, infoarg; + QString time; + QString file_path = "/home/noerror/123/catkin_for_ros/src/librviz_tutorial/data/result.bag"; + + arguments << "play" << file_path; + QProcess *rosbagProcess = new QProcess(this); + //QProcess::execute("rosbag play /home/noerror/123/catkin_for_ros/src/librviz_tutorial/data/result.bag"); + + rosbagProcess->start("rosbag", arguments); + + }); + connect(size,SIGNAL(valueChanged(int)),this,SLOT(setCloudSize(int))); + // Next we initialize the main RViz classes. + // + // The VisualizationManager is the container for Display objects, + // holds the main Ogre scene, holds the ViewController, etc. It is + // very central and we will probably need one in every usage of + // librviz. + manager_ = new rviz::VisualizationManager( render_panel_ ); + render_panel_->initialize( manager_->getSceneManager(), manager_ ); + //manager_->setFixedFrame("world"); + + /*TODO: laser data*/ + manager_->setFixedFrame("laser"); + manager_->initialize(); + manager_->startUpdate(); + + // Create a Grid display. + grid_ = manager_->createDisplay( "rviz/Grid", "adjustable grid", true ); + ROS_ASSERT( grid_ != NULL ); + + cloud = manager_->createDisplay( "rviz/PointCloud2", "point cloud", true ); + ROS_ASSERT( cloud != NULL ); + + /*TODO: laser data display*/ + laser = manager_->createDisplay( "rviz/LaserScan", "laser", true ); + ROS_ASSERT( laser != NULL ); + //set defualt topic to /scan + laser->subProp("Topic")->setValue("/scan"); + laser->subProp("Decay Time")->setValue("0"); + + + cloud->subProp("Topic")->setValue("/pose_graph/octree"); + cloud->subProp("Style")->setValue("Points"); + cloud->subProp("Size (Pixels)")->setValue("2"); + cloud->subProp("Color Transformer")->setValue("Intensity"); + cloud->subProp("Invert Rainbow")->setValue("true"); + cloud->subProp("Decay Time")->setValue("1"); + // Configure the GridDisplay the way we like it. + grid_->subProp( "Line Style" )->setValue( "Billboards" ); + grid_->subProp( "Color" )->setValue( QColor( Qt::yellow ) ); + + // Initialize the slider values. + thickness_slider->setValue( 5 ); + cell_size_slider->setValue( 10 ); +} + +// Destructor. +MyViz::~MyViz() +{ + delete manager_; +} + +// This function is a Qt slot connected to a QSlider's valueChanged() +// signal. It sets the line thickness of the grid by changing the +// grid's "Line Width" property. +void MyViz::setThickness( int thickness_percent ) +{ + if( grid_ != NULL ) + { + grid_->subProp( "Line Style" )->subProp( "Line Width" )->setValue( thickness_percent / 100.0f ); + } +} + +// This function is a Qt slot connected to a QSlider's valueChanged() +// signal. It sets the cell size of the grid by changing the grid's +// "Cell Size" Property. +void MyViz::setCellSize( int cell_size_percent ) +{ + if( grid_ != NULL ) + { + grid_->subProp( "Cell Size" )->setValue( cell_size_percent / 10.0f ); + } +} +void MyViz::setCloudTopic(const QString &newTopic){ + if(cloud!=NULL){ + cloud->subProp( "Topic" )->setValue(newTopic); + //ROS_INFO_STREAM("cloud topic changed to => "<subProp("Size (Pixels)")->setValue(cloudsize); + } +} +void MyViz::subCallback(const std_msgs::String& msg){ + ROS_INFO_STREAM("receive message!"); +} +void MyViz::pubThread(){ + while(ros::ok()){ + ROS_INFO_STREAM_ONCE("here is in publish process!"); + } +} +void MyViz::setlaserTopic(const QString & lasertopic){ + if(laser!=NULL){ + laser->subProp("Topic")->setValue(lasertopic); + manager_->startUpdate(); + } + +} +void MyViz::PushButtonBegining(const QString & things){ + + QProcess::execute("rosbag play /home/noerror/123/catkin_for_ros/src/librviz_tutorial/data/result.bag "); + +} \ No newline at end of file diff --git a/src/myviz.h b/src/myviz.h new file mode 100644 index 0000000..be8d46f --- /dev/null +++ b/src/myviz.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2012, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef MYVIZ_H +#define MYVIZ_H + +#include +#include +#include +#include"std_msgs/String.h" +#include +namespace rviz +{ +class Display; +class RenderPanel; +class VisualizationManager; +} + +// BEGIN_TUTORIAL +// Class "MyViz" implements the top level widget for this example. +class MyViz: public QWidget +{ +Q_OBJECT +public: + MyViz( QWidget* parent = 0 ); + virtual ~MyViz(); + void subCallback(const std_msgs::String& msg); + void pubThread(); +private Q_SLOTS: + void setThickness( int thickness_percent ); + void setCellSize( int cell_size_percent ); + void setCloudTopic(const QString &newTopic); + void setCloudSize(int cloudsize); + void setlaserTopic(const QString &lasertopic); + void PushButtonBegining(const QString & things); + +private: + rviz::VisualizationManager* manager_; + rviz::RenderPanel* render_panel_; + rviz::Display* grid_; + rviz::Display* cloud; + + /*TODO: laser data*/ + rviz::Display* laser; + /*TODO: create new thread*/ + QProcess *rosbagProcessStart, *rosbagProcessEnd, *rosbagProcessReset; + + ros::NodeHandle nh; + ros::Subscriber sub; + ros::Publisher pub; + std::thread pub_thread; +}; +// END_TUTORIAL +#endif // MYVIZ_H diff --git a/src/qthread.h b/src/qthread.h new file mode 100644 index 0000000..2575075 --- /dev/null +++ b/src/qthread.h @@ -0,0 +1,20 @@ +#include +#include + +class RosbagThread : public QThread { + Q_OBJECT + +public: + void run() override { + // 在这里执行您的rosbag任务 + // 例如,记录数据到ROS bag文件或播放ROS bag文件 + // 使用与前面示例中相似的方式启动rosbag进程 + + QStringList arguments; + QProcess rosbagProcess; + + arguments << "play" << "/home/noerror/123/catkin_for_ros/src/librviz_tutorial/data/result.bag"; + //QProcess::execute("rosbag play /home/noerror/123/catkin_for_ros/src/librviz_tutorial/data/result.bag"); + rosbagProcess.start("rosbag", arguments); + } +}; \ No newline at end of file