From 6271c3ec6793d9383c9b649829c835341d02a313 Mon Sep 17 00:00:00 2001 From: wubw1656 <879367232@qq.com> Date: Mon, 16 Oct 2023 21:07:33 +0800 Subject: [PATCH] version3 --- README.md | 11 +- build.sh | 28 + librviz_tutorial/launch/laser.launch | 10 - librviz_tutorial/src/myviz.cpp | 1105 --------- .../CHANGELOG.rst | 0 .../CMakeLists.txt | 26 +- point_visual/launch/laser.launch | 10 + .../launch/rplidar.launch | 2 +- .../package.xml | 2 +- .../rosdoc.yaml | 0 .../sdk/README.txt | 0 .../sdk/include/rplidar.h | 0 .../sdk/include/rplidar_cmd.h | 0 .../sdk/include/rplidar_driver.h | 0 .../sdk/include/rplidar_protocol.h | 0 .../sdk/include/rptypes.h | 0 .../sdk/src/arch/linux/arch_linux.h | 0 .../sdk/src/arch/linux/net_serial.cpp | 0 .../sdk/src/arch/linux/net_serial.h | 0 .../sdk/src/arch/linux/net_socket.cpp | 0 .../sdk/src/arch/linux/thread.hpp | 0 .../sdk/src/arch/linux/timer.cpp | 0 .../sdk/src/arch/linux/timer.h | 0 .../sdk/src/arch/macOS/arch_macOS.h | 0 .../sdk/src/arch/macOS/net_serial.cpp | 0 .../sdk/src/arch/macOS/net_serial.h | 0 .../sdk/src/arch/macOS/net_socket.cpp | 0 .../sdk/src/arch/macOS/thread.hpp | 0 .../sdk/src/arch/macOS/timer.cpp | 0 .../sdk/src/arch/macOS/timer.h | 0 .../sdk/src/arch/win32/arch_win32.h | 0 .../sdk/src/arch/win32/net_serial.cpp | 0 .../sdk/src/arch/win32/net_serial.h | 0 .../sdk/src/arch/win32/net_socket.cpp | 0 .../sdk/src/arch/win32/timer.cpp | 0 .../sdk/src/arch/win32/timer.h | 0 .../sdk/src/arch/win32/winthread.hpp | 0 .../sdk/src/hal/abs_rxtx.h | 0 .../sdk/src/hal/assert.h | 0 .../sdk/src/hal/byteops.h | 0 .../sdk/src/hal/event.h | 0 .../sdk/src/hal/locker.h | 0 .../sdk/src/hal/socket.h | 0 .../sdk/src/hal/thread.cpp | 0 .../sdk/src/hal/thread.h | 0 .../sdk/src/hal/types.h | 0 .../sdk/src/hal/util.h | 0 .../sdk/src/rplidar_driver.cpp | 0 .../sdk/src/rplidar_driver_TCP.h | 0 .../sdk/src/rplidar_driver_impl.h | 0 .../sdk/src/rplidar_driver_serial.h | 0 .../sdk/src/sdkcommon.h | 0 .../sources/images/button/approach.png | Bin 0 -> 12926 bytes point_visual/sources/images/button/back.png | Bin 0 -> 17487 bytes point_visual/sources/images/button/pause.png | Bin 0 -> 14885 bytes point_visual/sources/images/button/play.png | Bin 0 -> 14702 bytes .../sources/images/color}/black.png | Bin .../sources/images/color}/blue.png | Bin .../sources/images/color}/green.png | Bin .../sources/images/color}/lightgreen.png | Bin .../sources/images/color}/orange.png | Bin .../sources/images/color}/pink.png | Bin .../sources/images/color}/purple.png | Bin .../sources/images/color}/red.png | Bin .../sources/images/color}/skyblue.png | Bin .../sources/images/color}/white.png | Bin .../sources/images/color}/yellow.png | Bin .../sources/user/input.txt | 0 .../sources/user/user.txt | 0 .../src/doc/conf.py | 0 .../src/doc/index.rst | 0 .../src/doc/myviz.png | Bin .../src/doc/tutorialformatter.py | 0 .../src/loginDialog.cpp | 69 +- .../src/loginDialog.h | 1 + .../src/main.cpp | 0 point_visual/src/myviz.cpp | 2038 +++++++++++++++++ .../src/myviz.h | 90 +- .../src/node.cpp | 0 .../tools/color.cpp | 0 .../tools/color.h | 0 81 files changed, 2194 insertions(+), 1198 deletions(-) create mode 100755 build.sh delete mode 100644 librviz_tutorial/launch/laser.launch delete mode 100755 librviz_tutorial/src/myviz.cpp rename {librviz_tutorial => point_visual}/CHANGELOG.rst (100%) rename {librviz_tutorial => point_visual}/CMakeLists.txt (83%) create mode 100644 point_visual/launch/laser.launch rename {librviz_tutorial => point_visual}/launch/rplidar.launch (81%) rename {librviz_tutorial => point_visual}/package.xml (96%) rename {librviz_tutorial => point_visual}/rosdoc.yaml (100%) rename {librviz_tutorial => point_visual}/sdk/README.txt (100%) rename {librviz_tutorial => point_visual}/sdk/include/rplidar.h (100%) rename {librviz_tutorial => point_visual}/sdk/include/rplidar_cmd.h (100%) rename {librviz_tutorial => point_visual}/sdk/include/rplidar_driver.h (100%) rename {librviz_tutorial => point_visual}/sdk/include/rplidar_protocol.h (100%) rename {librviz_tutorial => point_visual}/sdk/include/rptypes.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/linux/arch_linux.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/linux/net_serial.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/linux/net_serial.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/linux/net_socket.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/linux/thread.hpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/linux/timer.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/linux/timer.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/macOS/arch_macOS.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/macOS/net_serial.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/macOS/net_serial.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/macOS/net_socket.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/macOS/thread.hpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/macOS/timer.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/macOS/timer.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/win32/arch_win32.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/win32/net_serial.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/win32/net_serial.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/win32/net_socket.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/win32/timer.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/win32/timer.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/arch/win32/winthread.hpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/abs_rxtx.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/assert.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/byteops.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/event.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/locker.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/socket.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/thread.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/thread.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/types.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/hal/util.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/rplidar_driver.cpp (100%) rename {librviz_tutorial => point_visual}/sdk/src/rplidar_driver_TCP.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/rplidar_driver_impl.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/rplidar_driver_serial.h (100%) rename {librviz_tutorial => point_visual}/sdk/src/sdkcommon.h (100%) create mode 100644 point_visual/sources/images/button/approach.png create mode 100644 point_visual/sources/images/button/back.png create mode 100644 point_visual/sources/images/button/pause.png create mode 100644 point_visual/sources/images/button/play.png rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/black.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/blue.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/green.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/lightgreen.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/orange.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/pink.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/purple.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/red.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/skyblue.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/white.png (100%) rename {librviz_tutorial/sources/images => point_visual/sources/images/color}/yellow.png (100%) rename {librviz_tutorial => point_visual}/sources/user/input.txt (100%) rename {librviz_tutorial => point_visual}/sources/user/user.txt (100%) rename {librviz_tutorial => point_visual}/src/doc/conf.py (100%) rename {librviz_tutorial => point_visual}/src/doc/index.rst (100%) rename {librviz_tutorial => point_visual}/src/doc/myviz.png (100%) rename {librviz_tutorial => point_visual}/src/doc/tutorialformatter.py (100%) rename {librviz_tutorial => point_visual}/src/loginDialog.cpp (71%) rename {librviz_tutorial => point_visual}/src/loginDialog.h (97%) rename {librviz_tutorial => point_visual}/src/main.cpp (100%) create mode 100644 point_visual/src/myviz.cpp rename {librviz_tutorial => point_visual}/src/myviz.h (66%) rename {librviz_tutorial => point_visual}/src/node.cpp (100%) rename {librviz_tutorial => point_visual}/tools/color.cpp (100%) rename {librviz_tutorial => point_visual}/tools/color.h (100%) diff --git a/README.md b/README.md index 5f1767b..665a3b3 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,23 @@ #### 安装教程 -1. xxxx -2. xxxx +1. chmod +x build.sh +2. ./build.sh 3. xxxx #### 使用说明 +<<<<<<< Updated upstream 在最外层目录 catkin_make 可能需要 source ./devel/setup.bash 1. roslaunch librviz_tutorial laser.launch 2. xxxx +======= +在工作目录下(src上一层) +记得source(可能需要) +1. source ./devel/setup.bash +2. roslaunch point_visual laser.launch +>>>>>>> Stashed changes 3. xxxx #### 参与贡献 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..5a8e36b --- /dev/null +++ b/build.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# 创建工作目录 +mkdir ~/PointVisual + +cd .. +#将当前src移动到PointVisual +cp -r src ~/PointVisual + +# 切换到工作目录 +cd ~/PointVisual + +# 在工作目录make +catkin_make + +if [ $? -eq 0 ]; then + +# 执行source命令 + source ./devel/setup.bash + +# 运行其他命令 +# roslaunch point_visual laser.launch + roscore & + sleep 2 + rosrun point_visual myviz +else + exit 1 +fi diff --git a/librviz_tutorial/launch/laser.launch b/librviz_tutorial/launch/laser.launch deleted file mode 100644 index 0a19978..0000000 --- a/librviz_tutorial/launch/laser.launch +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/librviz_tutorial/src/myviz.cpp b/librviz_tutorial/src/myviz.cpp deleted file mode 100755 index 547cc8a..0000000 --- a/librviz_tutorial/src/myviz.cpp +++ /dev/null @@ -1,1105 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "rviz/properties/property_tree_widget.h" -#include "rviz/selection/selection_manager.h" -#include "rviz/panel.h" - -#include "rviz/visualization_manager.h" -#include"rviz/tool_manager.h" -#include "rviz/render_panel.h" -#include "rviz/display.h" - -#include "myviz.h" -#include "../tools/color.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); - - // 全局变量存放处 - // 颜色名称,可添加,待设置rgb类型设置颜色 - color_name = { - "white","red","green","black","blue","yellow", - "pink","purple","skyblue","lightgreen","orange", - }; - // 颜色路径名 - color_icon_filename = QString("../Rviz/librviz_ws/src/librviz_tutorial/sources/images/%1.png"); - speed_name = {"2.0","1.5","1.25","1.0","0.75","0.5",}; - duration = 0; - isclicked = true; - ispauseclick = true; - sliderreleased = false; - playagain = false; - isthreadplaying = false; - abs = false; - stored = false; - - dlg = new LoginDialog(this); - landed = 0; - cur_fixed_frame = "world"; - - // 网格结构 - QGridLayout* controls_layout = new QGridLayout(); - int ctl_index = 0; - - // 登录按钮 - QPushButton* login_button = new QPushButton("登录"); - QPushButton* quit_login_button = new QPushButton("退出"); - - //select按钮 - QPushButton* selection_button = new QPushButton("选择坐标点"); - QPushButton* move_camera_button = new QPushButton("移动"); - - controls_layout->addWidget( login_button, ctl_index, 0); - controls_layout->addWidget( quit_login_button, ctl_index++, 1); - - - // offline 布局 - // QPushButton setting!//////////////////////////////////////////////////////////////////////////////////////////////// - QPushButton* play = new QPushButton("播放", this); - QPushButton* pause = new QPushButton("暂停", this); - QPushButton* but_save = new QPushButton("保存一帧", this);// buttonn: save one frame - QPushButton* but_saveMul = new QPushButton("保存多帧", this);// button: save multiple frames - QPushButton* but_file = new QPushButton("打开文件", this);// button: select file - - // QLabel setting!///////////////////////////////////////////////////////////////////////////////////////////////////// - QLabel* label_speed = new QLabel("播放速度:");//speed combobox - QLabel* label_split = new QLabel("");//speed combobox - - // QSlider setting!//////////////////////////////////////////////////////////////////////////////////////////////////// - progress = new QSlider(Qt::Horizontal); - // QComboBox setting!////////////////////////////////////////////////////////////////////////////////////////////////// - QComboBox * speed_combobox = new QComboBox(); - //enable button//////////////////////////////////////////////////////////////////////////////////////////////////////// - play->setEnabled(true); - pause->setEnabled(true); - but_save->setEnabled(true); - but_saveMul->setEnabled(true); - but_file->setEnabled(false); - - // setting button size!//////////////////////////////////////////////////////////////////////////////////////////////// - play->setFixedSize(120, 30); - pause->setFixedSize(120, 30); - but_save->setFixedSize(120, 30); - but_saveMul->setFixedSize(120, 30); - but_file->setFixedSize(120, 30); - - for(int i = 0; i < speed_name.size(); i++) - speed_combobox->addItem(speed_name[i] + "x"); - speed_combobox->setCurrentIndex(3); - - controls_layout->addWidget( label_split, ctl_index++, 0 ); - controls_layout->addWidget( but_file, ctl_index, 0 ); - controls_layout->addWidget( but_save, ctl_index++, 1 ); - controls_layout->addWidget( but_saveMul, ctl_index++, 0 ); - - controls_layout->addWidget( move_camera_button, ctl_index, 0); - controls_layout->addWidget( selection_button, ctl_index++ ,1); - - controls_layout->addWidget(label_speed,ctl_index,0); - controls_layout->addWidget(speed_combobox,ctl_index++,1); - - controls_layout->addWidget( play, ctl_index, 0 ); - controls_layout->addWidget( pause, ctl_index++, 1 ); - controls_layout->addWidget(progress,ctl_index++,0, 1, 2); // addwidget(widget,row,col,m,n);//m是占几行,n是占几列, - - controls_layout->setSpacing(5); - - // 是否选择显示坐标轴 - QLabel* label_axes = new QLabel("参考坐标系:"); - QCheckBox *axes_checkbox = new QCheckBox("", this); - // QComboBox * axesBox = new QComboBox(); - // axesBox->addItem("不显示"); - // axesBox->addItem("显示"); - - // 选择圆圈范围 - QLabel* label_circle = new QLabel("参考圆(m): "); - QLineEdit* circleLineEdit = new QLineEdit(); - circleLineEdit->setPlaceholderText("请输入数字"); - circleLineEdit->setFixedWidth(120); - - controls_layout->addWidget(label_axes, ctl_index,0); - controls_layout->addWidget(axes_checkbox, ctl_index++,1); - controls_layout->addWidget(label_circle, ctl_index,0); - controls_layout->addWidget(circleLineEdit, ctl_index++,1); - - // 第二个layout - //QGridLayout* display_layout = new QGridLayout(); - //int display_index = 0; - - // Tree 全局变量存放地方 - Tree_Display(controls_layout, ctl_index++); - - - // 待解锁子组件设置父组件,方便寻找,登陆后解锁可编辑属性 - login_button->setObjectName("login_button"); - login_button->setParent(this); - quit_login_button->setObjectName("quit_login_button"); - quit_login_button->setParent(this); - quit_login_button->setEnabled(false); - - - but_file->setObjectName("open_file"); - but_file->setParent(this); - progress->setObjectName("bag_progress"); - progress->setParent(this); - progress->setEnabled(false); - - // controls_layout->setRowStretch(3, 0); - // Make signal/slot connections. 进行信号/插槽连接 - - // 增加登录按钮 - connect(login_button, SIGNAL(clicked()), this, SLOT(login_button_clicked())); - // 增加退出按钮 - connect(quit_login_button, SIGNAL(clicked()), this, SLOT(quit_login_button_clicked())); - - // 增加切换select - connect(selection_button, SIGNAL(clicked()), this, SLOT( slot_select())); - connect(move_camera_button, SIGNAL(clicked()), this, SLOT( slot_move_camera())); - - // 增加坐标变换 - // connect(axesBox, SIGNAL(currentIndexChanged(int)), this, SLOT(AxesDisplayChanged(int))); - connect(axes_checkbox, SIGNAL(stateChanged(int)), this, SLOT(AxesDisplayChanged(int))); - // 增加圆圈变换 - connect(circleLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(CircleDisplayChanged(const QString&))); - //off - connect(progress,&QSlider::valueChanged,this,[=](){// 变动会触发 - if(!ismoved){ - ischanged = true;// “戳”这一个动作会使ischange变成true - // qDebug()<<"CLICK"<setSliderPosition(0); - std::thread rosBagPlay(&MyViz::openFile, this);// 新开线程执行操作 - rosBagPlay.detach(); - - } - } - - }); - connect(pause, &QPushButton::clicked, this, [&]() { - if(isthreadplaying){ - if(ispauseclick) - { - ispauseclick = false; - } - else - { - ispauseclick = true; - } - } - }); - - // Construct and lay out render panel. - render_panel_ = new rviz::RenderPanel(); // RVIZ在QT上显示的类 - QHBoxLayout* main_layout = new QHBoxLayout; // 主要layout QV垂直,QH水平 - - // 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_ ); // 是实现rviz功能的类 - // 初始化camera 这行代码实现放大 缩小 平移等操作 - render_panel_->initialize( manager_->getSceneManager(), manager_ ); - manager_->setFixedFrame("laser"); - // 初始化tool_manager_ - tool_manager_ = manager_->getToolManager(); - - // 在初始化之后再进行更改操作 - manager_->initialize(); - manager_->startUpdate(); - - manager_->setFixedFrame(cur_fixed_frame); - // qDebug()<< cur_fixed_frame; - - // 创建select panel - QLabel *select_label = new QLabel("坐标点显示:"); - tree_widget_ = new rviz::PropertyTreeWidget(); - tree_widget_->setModel(manager_->getSelectionManager()->getPropertyModel()); - ctl_index++; - controls_layout->addWidget(select_label, ctl_index++, 0); - // controls_layout->addWidget(tree_widget_, ctl_index++, 0); - controls_layout->addWidget(tree_widget_, ctl_index++, 0, 1, 2); - - // 设置网格某一行伸缩比例,放最后一行 - // controls_layout->setRowStretch(--ctl_index, 6); - - // 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 ); - - laser_ = manager_->createDisplay( "rviz/LaserScan", "Qlaser", true ); - ROS_ASSERT( laser_ ); - // 新增雷达 - laser_->subProp("Topic")->setValue("/scan"); - laser_->subProp("Size (Pixels)")->setValue("2"); - laser_->subProp("Color Transformer")->setValue("FlatColor"); - laser_->subProp("Color")->setValue(color_name[1]); - // 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::white ) ); - - // 新增坐标系 - axes_display= manager_->createDisplay("rviz/Axes", "Axes", false); - ROS_ASSERT( axes_display != NULL ); - axes_display->subProp("Length")->setValue(0.2); // 设置透明度 - axes_display->subProp("Radius")->setValue(0.05); // 设置坐标系的缩放大小 - // 新增Marker_ 显示圆圈 - marker_ = manager_->createDisplay("rviz/Marker", "Marker", false); - ROS_ASSERT( marker_ != NULL ); - marker_->subProp("Topic")->setValue("visualization_marker"); - - // Set the top-level layout for this MyViz widget. - main_layout->addLayout( controls_layout ); - //main_layout->addLayout( controls_layout ); - main_layout->addWidget( render_panel_,2); // 存放rviz的位置, stretch 拉伸系数 - setLayout( main_layout ); - -} - -void MyViz::Tree_Display(QGridLayout* controls_layout, int index){ - QTreeWidget *menu = new QTreeWidget(); - // menu->setColumnCount(2); - // 列头自适应大小 - menu->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - - menu->setHeaderLabels(QStringList()<<"key"<<"value"); - menu->setHeaderHidden(true); - // menu->setColumnCount(2); - - // 设置不同层次菜单的缩进 - // menu->setIndentation(10); - // global-一层树 - QTreeWidgetItem *global= new QTreeWidgetItem(menu, QStringList("全局变量")); - menu->addTopLevelItem(global); - global->setExpanded(true); - - // fix frame - QTreeWidgetItem *fixed_frame = new QTreeWidgetItem(global, QStringList("固定参考系")); - - QLineEdit* fixed_frame_text = new QLineEdit(); - fixed_frame_text->setText("world"); - fixed_frame_text->setStyleSheet("background:transparent;border-width:0;border-style:inset"); - // 初始化固定坐标系 - cur_fixed_frame = fixed_frame_text->text(); - // 设置事件 - connect(fixed_frame_text,SIGNAL(textChanged(const QString&)),this,SLOT(setFixedFrame(const QString&))); - // 将其他控件(非TreeWidgetItem)放入treeItem中 - menu->setItemWidget(fixed_frame, 1, fixed_frame_text); // 将container 放到 Item ,数字为列数 - - QTreeWidgetItem *bg_color = new QTreeWidgetItem(global, QStringList("背景颜色")); - QLineEdit* bg_color_text = new QLineEdit(); - bg_color_text->setText("48;48;48"); - bg_color_text->setStyleSheet("border-width:0;border-style:outset"); - connect(bg_color_text,SIGNAL(textChanged(const QString&)),this,SLOT(setBackgroundColor(const QString&))); - menu->setItemWidget(bg_color, 1, bg_color_text); // 将container 放到 Item ,数字为列数 - - // global->addChild(fixed_frame); - // global->setTextAlignment(1, 2); // (列数,对齐方式) - // grid - 一层树 - QTreeWidgetItem * grid = new QTreeWidgetItem(menu, QStringList("栅格")); - // 二层 - QTreeWidgetItem *grid_color = new QTreeWidgetItem(grid, QStringList("颜色")); - QComboBox * grid_color_cbox = new QComboBox(); - for(int i =0 ; i < color_name.size(); ++i){ - QString color_path = color_icon_filename.arg(color_name[i]); - QIcon color_icon(color_path); - grid_color_cbox->addItem(color_icon, color_name[i]); - } - connect(grid_color_cbox, SIGNAL(currentIndexChanged(int)), this, SLOT(GridColorChanged(int))); - menu->setItemWidget(grid_color, 1, grid_color_cbox); - - // grid cell size - QTreeWidgetItem *grid_cell = new QTreeWidgetItem(grid, QStringList("网格大小")); - QSlider* cell_size_slider = new QSlider( Qt::Horizontal ); - cell_size_slider->setMinimum( 1 ); - cell_size_slider->setMaximum( 100 ); - connect( cell_size_slider, SIGNAL( valueChanged( int )), this, SLOT( setCellSize( int ))); - // cell_size_slider->setValue( 1 ); - menu->setItemWidget(grid_cell, 1, cell_size_slider); - - // grid Line Thickness - QTreeWidgetItem *grid_thickness = new QTreeWidgetItem(grid, QStringList("线条粗细")); - QSlider* thickness_slider = new QSlider( Qt::Horizontal ); - thickness_slider->setMinimum( 1 ); - thickness_slider->setMaximum( 100 ); - connect( thickness_slider, SIGNAL( valueChanged( int )), this, SLOT( setThickness( int ))); - // thickness_slider->setSliderPosition(1); - menu->setItemWidget(grid_thickness, 1, thickness_slider); - - // laser - 一层树 - QTreeWidgetItem * laser = new QTreeWidgetItem(menu, QStringList("雷达")); - // 二层 - QTreeWidgetItem *laser_topic = new QTreeWidgetItem(laser, QStringList("话题")); - // Topic_Laser 输入雷达 - QLineEdit* laser_topic_text = new QLineEdit(); - - // 方便寻找,登陆后解锁 - laser_topic_text->setObjectName("laser_topic"); - laser_topic_text->setParent(this); - laser_topic_text->setEnabled(false); - // 增加雷达话题 - connect(laser_topic_text, SIGNAL(textChanged(const QString &)), this, SLOT(setLaserTopic(const QString &))); - menu->setItemWidget(laser_topic, 1, laser_topic_text); - - // Laser size - QTreeWidgetItem *laser_size = new QTreeWidgetItem(laser, QStringList("大小")); - QSpinBox* laser_size_text=new QSpinBox(); - connect(laser_size_text,SIGNAL(valueChanged(int)),this,SLOT(setLaserSize(int))); - menu->setItemWidget(laser_size, 1, laser_size_text); - - // Laser color - QTreeWidgetItem *laser_color = new QTreeWidgetItem(laser, QStringList("颜色")); - QComboBox * laser_color_comboBox = new QComboBox(); - for(int i =0 ; i < color_name.size(); ++i){ - QString color_path = color_icon_filename.arg(color_name[i]); - QIcon color_icon(color_path); - laser_color_comboBox->addItem(color_icon, color_name[i]); - } - // 设置初始化雷达颜色为红色 - laser_color_comboBox->setCurrentIndex(1); - - // 增加颜色变换 - connect(laser_color_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(LaserColorChanged(int))); - menu->setItemWidget(laser_color, 1, laser_color_comboBox); - - //controls_layout->addWidget(menu, index, 0 ); - controls_layout->addWidget(menu, index, 0, 1, 2); -} - - -void MyViz::quit_login_button_clicked(){ - landed = 0; - // 退出设置登录按钮可用 - QWidget * login_button = this->findChild("login_button"); - if (login_button) { - login_button->setEnabled(true); - }else{ - qDebug() << "login_button组件未找到" ; - } - // 退出设置退出按钮不可用 - QWidget * quit_button = this->findChild("quit_login_button"); - if (quit_button) { - quit_button->setEnabled(false); - }else{ - qDebug() << "quit_login_button组件未找到" ; - } - // 退出设置其他组件不可用 - if(dlg != NULL){ - for (int i=0; i< dlg->setting_name.size(); i++){ - QWidget *foundChild = this->findChild(dlg->setting_name[i]); - if (foundChild) { - // 找到了指定的子组件 在这里可以对子组件进行操作 - foundChild->setEnabled(false); - }else{ - qDebug()<<"第" << i+1 <<"组件未找到" ; - } - } - } -} - -void MyViz::login_button_clicked(){ - dlg->setWindowTitle(tr("登录")); - // dlg->setFixedSize(400, 300); - // dlg->open();// 非模态级别对话框 - if(dlg->exec() == QDialog::Accepted){ - // 登录成功后 - landed = 1; - qDebug() << "login accepted"; - // 登录设置其他组件和退出可用 - QWidget * quit_button = this->findChild("quit_login_button"); - if (quit_button) { - quit_button->setEnabled(true); - }else{ - qDebug() << "quit_login_button组件未找到" ; - } - // 登录设置登录不可用 - QWidget * login_button = this->findChild("login_button"); - if (login_button) { - login_button->setEnabled(false); - }else{ - qDebug()<<"login_button组件未找到" ; - } - }else{ - // 登录失败 - qDebug() << "login refused"; - } -} - -void MyViz::PlayCircle(double index){ - int argc =0; - char** argv; - ros::init(argc, argv, "basic_shapes"); - ros::NodeHandle n; - ros::Rate r(1); - ros::Publisher marker_pub = n.advertise("visualization_marker", 1); - uint32_t shape = visualization_msgs::Marker::LINE_STRIP; - - if (ros::ok()) - { - visualization_msgs::Marker marker; - // Set the frame ID and timestamp. See the TF tutorials for information on these. - marker.header.frame_id = cur_fixed_frame.toStdString(); - // marker.header.frame_id = "laser"; - marker.header.stamp = ros::Time::now(); - - // Set the namespace and id for this marker. This serves to create a unique ID - // Any marker sent with the same namespace and id will overwrite the old one - marker.ns = "basic_shapes"; - marker.id = 0; - - // Set the marker type. Initially this is CUBE, and cycles between that and SPHERE, ARROW, and CYLINDER - marker.type = shape; - - // Set the marker action. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) - marker.action = visualization_msgs::Marker::ADD; - - // Set the pose of the marker. This is a full 6DOF pose relative to the frame/time specified in the header - marker.pose.position.x = 0; - marker.pose.position.y = 0; - marker.pose.position.z = 0; - marker.pose.orientation.x = 0.0; - marker.pose.orientation.y = 0.0; - marker.pose.orientation.z = 0.0; - marker.pose.orientation.w = 1.0; - - // Set the scale of the marker -- 1x1x1 here means 1m on a side - // 设置线段的宽度 - marker.scale.x = 0.02; - marker.scale.y = 1.0; - marker.scale.z = 1.0; - - // Set the color -- be sure to set alpha to something non-zero! - marker.color.r = 0.0f; - marker.color.g = 1.0f; - marker.color.b = 0.0f; - marker.color.a = 1.0; - // points - // 绘制同心圆 - // double r_c= std::stod(argv[1]); - double radius = index; - int num_points = 360; - // Create the vertices for the points and lines 画圆 - for (uint32_t i = 0; i < num_points; ++i) - { - // float y = sin(f + i / 100.0f * 2 * M_PI); - // float z = cos(f + i / 100.0f * 2 * M_PI); - double angle = 2 * M_PI * i / num_points; - geometry_msgs::Point point; - point.x = radius * cos(angle); - point.y = radius * sin(angle); - point.z = 0.0; - marker.points.push_back(point); - } - marker.lifetime = ros::Duration(); - - // Publish the marker - while (marker_pub.getNumSubscribers() < 1) - { - if (!ros::ok()) - { - std::cout<< "publish return 0" << std::endl; - } - ROS_WARN_ONCE("Please create a subscriber to the marker"); - // sleep(1); - } - marker_pub.publish(marker); - - r.sleep(); - } - -} - -void MyViz::CircleDisplayChanged(const QString& text){ - double index = text.toDouble(); - if(index <= 0){ - marker_->setValue(false); - }else - { - marker_->setValue(true); - std::thread ThreadCircle(&MyViz::PlayCircle, this, index);// 新开线程执行操作 - ThreadCircle.detach();// 执行完成后自动回收资源 - } -} - -void MyViz::AxesDisplayChanged(int stage) -{ - // 0未被选中,1被部分选中,2 被选中 - switch (stage) - { - case 0: // 未被选中 - if (axes_display != NULL) - axes_display->setValue(false); - break; - - case 2: // 被选择 - if (axes_display != NULL) - axes_display->setValue(true); - break; - } -} - -void MyViz::slot_move_camera(){ - rviz::Tool* current_tool_= tool_manager_->getDefaultTool(); - tool_manager_->setCurrentTool(current_tool_); - manager_->startUpdate(); -} - - -void MyViz::slot_select(){ - // 获得工具类 - // tool_manager_ = manager_->getToolManager(); - rviz::Tool* current_tool_ = tool_manager_->addTool("rviz/Select"); - tool_manager_->setCurrentTool(current_tool_); - manager_->startUpdate(); -} - -void MyViz::setBackgroundColor(const QString& color_string){ - if(manager_!=NULL){ - const QString t = color_string; - QColor new_color = parseColor(t); - if(new_color.isValid()){ - // rviz源码中修改背景颜色的方式 - render_panel_->setBackgroundColor(qtToOgre(new_color)); - manager_->queueRender(); - } - else - { - ROS_INFO_STREAM("Color invalid !!!"); - } - }else{ - ROS_INFO_STREAM("manager_ failed !!!"); - } -} - - -void MyViz::LaserColorChanged(int index){ - if(laser_!=NULL) laser_->subProp("Color")->setValue(color_name[index]); - -} - -void MyViz::GridColorChanged(int index){ - if(grid_!=NULL) grid_->subProp("Color")->setValue(color_name[index]); -} - -// 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( "Topic" )->setValue(newTopic); - ROS_INFO_STREAM("laser topic changed to => "<setEnabled(true); - // laser->subProp( "Color" )->setValue( QColor( Qt::yellow ) ); - // manager_->setFixedFrame("laser"); - manager_->startUpdate(); -} - -void MyViz::setFixedFrame(const QString & fix_frame){ - manager_->setFixedFrame(fix_frame); - cur_fixed_frame = fix_frame; - manager_->startUpdate(); -} - -void MyViz::setCloudSize(int cloudsize){ - if(cloud!=NULL){ - cloud->subProp("Size (Pixels)")->setValue(cloudsize); - } -} - -void MyViz::setLaserSize(int lasersize){ - if(laser_!=NULL){ - laser_->subProp("Size (Pixels)")->setValue(lasersize); - } -} - -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::openFile() -{ - ros::Rate r(10.0); - rosbag::Bag bag,newBag; - std::string pcdFilePathStr = filePath.toStdString(); - int frameEnd; - int frameStart; - bool saveFlag = false; - if(stored) - { - char** argv;// 字符串内容(输入) - int argc=0;// 字符串个数(输入) - countbag = 0; - ros::init(argc, argv, "laser_scan_publisher");// "laser"???????????重新启动一下roscore可以解决 - // qDebug()<<"11111"; - - ros::NodeHandle n; - scan_pub = n.advertise("scan", 5);// "topic" - // sleep time - - bag.open(pcdFilePathStr, rosbag::bagmode::Read); - - rosbag::View view1(bag, rosbag::TopicQuery("/scan")); - view = &view1; - std::cout<size()<begin(); it != view->end(); ++it)// 用于预先存储bag包的信息 - { - auto m = *it; - sensor_msgs::LaserScan::ConstPtr input = m.instantiate(); - indx.push_back(input); - } - stored = false; - indxsize = indx.size(); - bag.close(); - qDebug()<=indxsize) - { - scan_pub.publish(*indx[indxsize-1]); - end = true;// 停在当前位置 - countbag = 0;// 从头开始 - qDebug()<<"publish end"; - } - // progressmove = false; - - if(is_rateChanged){ - qDebug() << QString::number(speed_rate); - r = ros::Rate(speed_rate); - r.reset(); - is_rateChanged = false; - } - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if(is_save){ - if(newBag.isOpen()) - newBag.close(); - std::string temFileStr = pcdFilePathStr; - temFileStr.resize(pcdFilePathStr.length() - 4); - std::string saveFilePathStr = temFileStr + "frame" \ - + std::to_string(countbag) + ".bag"; - std::cout << saveFilePathStr; - std::ifstream file(saveFilePathStr.c_str()); - if (!file.good()) { - // std::ofstream outputFile(saveFilePathStr); //create a file - // outputFile.close(); - - QFile newFile(QString::fromStdString(saveFilePathStr)); - - newBag.open(saveFilePathStr, rosbag::bagmode::Write); - // for(int i=0;i<10;i++){ - newBag.write("/scan", ros::Time::now(), indx[countbag]); - // } - newBag.close(); - }else{ - // file exist - } - - is_save = false; - r.sleep(); - countbag++; - continue; - } - if(is_saveMul || is_saveMulFinish){ - if(ispauseclick && is_saveMul) - { - is_saveMul = false; - QMessageBox msgBox; - msgBox.setWindowTitle("Tips"); - msgBox.setText("Pause First Please!"); - msgBox.resize(2000, 1000); - msgBox.exec(); - continue; - } - - if(is_saveMulFinish){ - frameEnd = countbag; - std::string temFileStr = pcdFilePathStr; - temFileStr.resize(pcdFilePathStr.length() - 4); - std::string saveFilePathStr = temFileStr + "frame" \ - + std::to_string(frameStart) + "to" + std::to_string(frameEnd) + ".bag"; - std::cout << saveFilePathStr; - std::ifstream file(saveFilePathStr.c_str()); - if (!file.good()) { - - - newBag.open(saveFilePathStr, rosbag::bagmode::Write); - - - for(int i = frameStart; i < frameEnd; i++){ - - newBag.write("/scan", ros::Time::now(), indx[i]); - - - } - is_saveMulFinish = false; - is_saveMul = false; - saveFlag = false; - newBag.close(); - }else{ - - } - } - else{ - qDebug()<setSliderPosition(ceil(countbag/indxsize*100)); - qDebug()<value()/100)*indxsize; - double valuedouble = progress->value(); - countbag = round((valuedouble/100)*indxsize); - if(countbag >= indxsize)// 直接重新开始for循环 - { - countbag = indxsize-1; - progress->setSliderPosition(100); - ispauseclick = false; - } - ismoved = false; - } - else if(ischanged)// 手戳动时改变进度条 - { - end = false; - int value = (progress->value()/100)*indxsize; - double valuedouble = progress->value(); - countbag = round((valuedouble/100)*indxsize); - if(countbag >= indxsize)// 直接重新开始for循环 - { - countbag = indxsize-1; - progress->setSliderPosition(100); - ispauseclick = false; - } - scan_pub.publish(*indx[countbag]); - countbag++; - ischanged = false; - r.sleep(); - continue; - // ismoved = false; - } - if(!ispauseclick || end) - { - qDebug()<<"pause"; - if(countbagsliderPosition()<=100 && countbagvalue/indxsize*100<=100) - { - qDebug()<<"pause"; - progress->setSliderPosition(ceil(countbagvalue/indxsize*100)); - qDebug()<setSliderPosition(0); - isthreadplaying = false; - playagain = false; - filePath = QFileDialog::getOpenFileName(nullptr, "select file", "/home", "bag(*.bag);;所有文件 (*)"); - qDebug()<< filePath; - if(filePath.isEmpty()){ - QMessageBox msgBox; - msgBox.setWindowTitle("Tips"); - msgBox.setText("No file is selected"); - msgBox.resize(2000, 1000); - // 设置样式表,包括透明度设置 - msgBox.setStyleSheet("QMessageBox {" - "background-color: rgba(255, 255, 255, 0.9);" // 设置背景颜色和透明度 - "border: 2px solid rgba(0, 0, 0, 0.9);" // 设置边框颜色和透明度 - "}" - "QLabel {" - "color: rgba(0, 0, 0, 0.9);" // 设置文本颜色和透明度 - "font-size: 16px;" - "}"); - QTimer selectTimer; - selectTimer.setSingleShot(true); // 设置定时器只触发一次 - selectTimer.start(2000); // 3秒后触发定时器,单位是毫秒 - - //连接定时器的超时信号到关闭消息框的槽函数 - QObject::connect(&selectTimer, &QTimer::timeout, [&]() { - msgBox.done(QMessageBox::Ok); // 关闭消息框 - }); - msgBox.exec(); - }else{ - - QMessageBox msgBox; - msgBox.setWindowTitle("Tips"); - msgBox.setText("File selected"); - msgBox.resize(2000, 1000); - // 设置样式表,包括透明度设置 - msgBox.setStyleSheet("QMessageBox {" - "background-color: rgba(255, 255, 255, 0.9);" // 设置背景颜色和透明度 - "border: 2px solid rgba(0, 0, 0, 0.9);" // 设置边框颜色和透明度 - "}" - "QLabel {" - "color: rgba(0, 0, 0, 0.9);" // 设置文本颜色和透明度 - "font-size: 16px;" - "}"); - QTimer selectTimer; - selectTimer.setSingleShot(true); // 设置定时器只触发一次 - selectTimer.start(2000); // 3秒后触发定时器,单位是毫秒 - - //连接定时器的超时信号到关闭消息框的槽函数 - QObject::connect(&selectTimer, &QTimer::timeout, [&]() { - msgBox.done(QMessageBox::Ok); // 关闭消息框 - }); - msgBox.exec(); - isthreadplaying = true; - } -} - - -void MyViz::SpeedChanged(int index){ - // if(isthreadplaying) - bool ok; - double number = speed_name[index].toDouble(&ok); - if(ok){ - speed_rate = 10.0 * number; - qDebug() << "*************func test" << QString::number(speed_rate); - is_rateChanged = true; - } - - else{ - qDebug() << "failed"; - } -} \ No newline at end of file diff --git a/librviz_tutorial/CHANGELOG.rst b/point_visual/CHANGELOG.rst similarity index 100% rename from librviz_tutorial/CHANGELOG.rst rename to point_visual/CHANGELOG.rst diff --git a/librviz_tutorial/CMakeLists.txt b/point_visual/CMakeLists.txt similarity index 83% rename from librviz_tutorial/CMakeLists.txt rename to point_visual/CMakeLists.txt index 5d360fe..d3a9c55 100644 --- a/librviz_tutorial/CMakeLists.txt +++ b/point_visual/CMakeLists.txt @@ -4,7 +4,7 @@ ## ## First start with some standard catkin stuff. cmake_minimum_required(VERSION 3.0.2) -project(librviz_tutorial) +project(point_visual) set(SDK_PATH "./sdk/") @@ -17,7 +17,15 @@ FILE(GLOB SDK_SRC find_package(catkin REQUIRED COMPONENTS rviz roscpp std_msgs rosconsole sensor_msgs # node.cpp -rosbag +rosbag +pcl_ros +) + +find_package(PCL REQUIRED) +include_directories( + include + ${catkin_INCLUDE_DIRS} + /usr/include/pcl-1.8 ) @@ -68,8 +76,8 @@ set(SRC_FILES add_executable(myviz ${SRC_FILES} ) -add_executable(rplidarNode src/node.cpp ${SDK_SRC}) # 创建启动laser -target_link_libraries(rplidarNode ${catkin_LIBRARIES}) +# add_executable(rplidarNode src/node.cpp ${SDK_SRC}) # 创建启动laser +# target_link_libraries(rplidarNode ${catkin_LIBRARIES}) target_link_libraries(myviz -lcrypto) @@ -85,11 +93,11 @@ target_link_libraries(myviz ${QT_LIBRARIES} ${catkin_LIBRARIES}) ## Install install(TARGETS myviz DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) -install(TARGETS rplidarNode - ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -) +# install(TARGETS rplidarNode +# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) install(DIRECTORY launch sdk DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} diff --git a/point_visual/launch/laser.launch b/point_visual/launch/laser.launch new file mode 100644 index 0000000..425c158 --- /dev/null +++ b/point_visual/launch/laser.launch @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/librviz_tutorial/launch/rplidar.launch b/point_visual/launch/rplidar.launch similarity index 81% rename from librviz_tutorial/launch/rplidar.launch rename to point_visual/launch/rplidar.launch index 0b2c7e2..1c88615 100644 --- a/librviz_tutorial/launch/rplidar.launch +++ b/point_visual/launch/rplidar.launch @@ -1,5 +1,5 @@ - + diff --git a/librviz_tutorial/package.xml b/point_visual/package.xml similarity index 96% rename from librviz_tutorial/package.xml rename to point_visual/package.xml index b6183b9..a1961ef 100644 --- a/librviz_tutorial/package.xml +++ b/point_visual/package.xml @@ -1,5 +1,5 @@ - librviz_tutorial + point_visual 0.11.0 Tutorial showing how to compile your own C++ program with RViz displays and features. diff --git a/librviz_tutorial/rosdoc.yaml b/point_visual/rosdoc.yaml similarity index 100% rename from librviz_tutorial/rosdoc.yaml rename to point_visual/rosdoc.yaml diff --git a/librviz_tutorial/sdk/README.txt b/point_visual/sdk/README.txt similarity index 100% rename from librviz_tutorial/sdk/README.txt rename to point_visual/sdk/README.txt diff --git a/librviz_tutorial/sdk/include/rplidar.h b/point_visual/sdk/include/rplidar.h similarity index 100% rename from librviz_tutorial/sdk/include/rplidar.h rename to point_visual/sdk/include/rplidar.h diff --git a/librviz_tutorial/sdk/include/rplidar_cmd.h b/point_visual/sdk/include/rplidar_cmd.h similarity index 100% rename from librviz_tutorial/sdk/include/rplidar_cmd.h rename to point_visual/sdk/include/rplidar_cmd.h diff --git a/librviz_tutorial/sdk/include/rplidar_driver.h b/point_visual/sdk/include/rplidar_driver.h similarity index 100% rename from librviz_tutorial/sdk/include/rplidar_driver.h rename to point_visual/sdk/include/rplidar_driver.h diff --git a/librviz_tutorial/sdk/include/rplidar_protocol.h b/point_visual/sdk/include/rplidar_protocol.h similarity index 100% rename from librviz_tutorial/sdk/include/rplidar_protocol.h rename to point_visual/sdk/include/rplidar_protocol.h diff --git a/librviz_tutorial/sdk/include/rptypes.h b/point_visual/sdk/include/rptypes.h similarity index 100% rename from librviz_tutorial/sdk/include/rptypes.h rename to point_visual/sdk/include/rptypes.h diff --git a/librviz_tutorial/sdk/src/arch/linux/arch_linux.h b/point_visual/sdk/src/arch/linux/arch_linux.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/linux/arch_linux.h rename to point_visual/sdk/src/arch/linux/arch_linux.h diff --git a/librviz_tutorial/sdk/src/arch/linux/net_serial.cpp b/point_visual/sdk/src/arch/linux/net_serial.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/linux/net_serial.cpp rename to point_visual/sdk/src/arch/linux/net_serial.cpp diff --git a/librviz_tutorial/sdk/src/arch/linux/net_serial.h b/point_visual/sdk/src/arch/linux/net_serial.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/linux/net_serial.h rename to point_visual/sdk/src/arch/linux/net_serial.h diff --git a/librviz_tutorial/sdk/src/arch/linux/net_socket.cpp b/point_visual/sdk/src/arch/linux/net_socket.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/linux/net_socket.cpp rename to point_visual/sdk/src/arch/linux/net_socket.cpp diff --git a/librviz_tutorial/sdk/src/arch/linux/thread.hpp b/point_visual/sdk/src/arch/linux/thread.hpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/linux/thread.hpp rename to point_visual/sdk/src/arch/linux/thread.hpp diff --git a/librviz_tutorial/sdk/src/arch/linux/timer.cpp b/point_visual/sdk/src/arch/linux/timer.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/linux/timer.cpp rename to point_visual/sdk/src/arch/linux/timer.cpp diff --git a/librviz_tutorial/sdk/src/arch/linux/timer.h b/point_visual/sdk/src/arch/linux/timer.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/linux/timer.h rename to point_visual/sdk/src/arch/linux/timer.h diff --git a/librviz_tutorial/sdk/src/arch/macOS/arch_macOS.h b/point_visual/sdk/src/arch/macOS/arch_macOS.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/macOS/arch_macOS.h rename to point_visual/sdk/src/arch/macOS/arch_macOS.h diff --git a/librviz_tutorial/sdk/src/arch/macOS/net_serial.cpp b/point_visual/sdk/src/arch/macOS/net_serial.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/macOS/net_serial.cpp rename to point_visual/sdk/src/arch/macOS/net_serial.cpp diff --git a/librviz_tutorial/sdk/src/arch/macOS/net_serial.h b/point_visual/sdk/src/arch/macOS/net_serial.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/macOS/net_serial.h rename to point_visual/sdk/src/arch/macOS/net_serial.h diff --git a/librviz_tutorial/sdk/src/arch/macOS/net_socket.cpp b/point_visual/sdk/src/arch/macOS/net_socket.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/macOS/net_socket.cpp rename to point_visual/sdk/src/arch/macOS/net_socket.cpp diff --git a/librviz_tutorial/sdk/src/arch/macOS/thread.hpp b/point_visual/sdk/src/arch/macOS/thread.hpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/macOS/thread.hpp rename to point_visual/sdk/src/arch/macOS/thread.hpp diff --git a/librviz_tutorial/sdk/src/arch/macOS/timer.cpp b/point_visual/sdk/src/arch/macOS/timer.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/macOS/timer.cpp rename to point_visual/sdk/src/arch/macOS/timer.cpp diff --git a/librviz_tutorial/sdk/src/arch/macOS/timer.h b/point_visual/sdk/src/arch/macOS/timer.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/macOS/timer.h rename to point_visual/sdk/src/arch/macOS/timer.h diff --git a/librviz_tutorial/sdk/src/arch/win32/arch_win32.h b/point_visual/sdk/src/arch/win32/arch_win32.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/win32/arch_win32.h rename to point_visual/sdk/src/arch/win32/arch_win32.h diff --git a/librviz_tutorial/sdk/src/arch/win32/net_serial.cpp b/point_visual/sdk/src/arch/win32/net_serial.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/win32/net_serial.cpp rename to point_visual/sdk/src/arch/win32/net_serial.cpp diff --git a/librviz_tutorial/sdk/src/arch/win32/net_serial.h b/point_visual/sdk/src/arch/win32/net_serial.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/win32/net_serial.h rename to point_visual/sdk/src/arch/win32/net_serial.h diff --git a/librviz_tutorial/sdk/src/arch/win32/net_socket.cpp b/point_visual/sdk/src/arch/win32/net_socket.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/win32/net_socket.cpp rename to point_visual/sdk/src/arch/win32/net_socket.cpp diff --git a/librviz_tutorial/sdk/src/arch/win32/timer.cpp b/point_visual/sdk/src/arch/win32/timer.cpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/win32/timer.cpp rename to point_visual/sdk/src/arch/win32/timer.cpp diff --git a/librviz_tutorial/sdk/src/arch/win32/timer.h b/point_visual/sdk/src/arch/win32/timer.h similarity index 100% rename from librviz_tutorial/sdk/src/arch/win32/timer.h rename to point_visual/sdk/src/arch/win32/timer.h diff --git a/librviz_tutorial/sdk/src/arch/win32/winthread.hpp b/point_visual/sdk/src/arch/win32/winthread.hpp similarity index 100% rename from librviz_tutorial/sdk/src/arch/win32/winthread.hpp rename to point_visual/sdk/src/arch/win32/winthread.hpp diff --git a/librviz_tutorial/sdk/src/hal/abs_rxtx.h b/point_visual/sdk/src/hal/abs_rxtx.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/abs_rxtx.h rename to point_visual/sdk/src/hal/abs_rxtx.h diff --git a/librviz_tutorial/sdk/src/hal/assert.h b/point_visual/sdk/src/hal/assert.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/assert.h rename to point_visual/sdk/src/hal/assert.h diff --git a/librviz_tutorial/sdk/src/hal/byteops.h b/point_visual/sdk/src/hal/byteops.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/byteops.h rename to point_visual/sdk/src/hal/byteops.h diff --git a/librviz_tutorial/sdk/src/hal/event.h b/point_visual/sdk/src/hal/event.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/event.h rename to point_visual/sdk/src/hal/event.h diff --git a/librviz_tutorial/sdk/src/hal/locker.h b/point_visual/sdk/src/hal/locker.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/locker.h rename to point_visual/sdk/src/hal/locker.h diff --git a/librviz_tutorial/sdk/src/hal/socket.h b/point_visual/sdk/src/hal/socket.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/socket.h rename to point_visual/sdk/src/hal/socket.h diff --git a/librviz_tutorial/sdk/src/hal/thread.cpp b/point_visual/sdk/src/hal/thread.cpp similarity index 100% rename from librviz_tutorial/sdk/src/hal/thread.cpp rename to point_visual/sdk/src/hal/thread.cpp diff --git a/librviz_tutorial/sdk/src/hal/thread.h b/point_visual/sdk/src/hal/thread.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/thread.h rename to point_visual/sdk/src/hal/thread.h diff --git a/librviz_tutorial/sdk/src/hal/types.h b/point_visual/sdk/src/hal/types.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/types.h rename to point_visual/sdk/src/hal/types.h diff --git a/librviz_tutorial/sdk/src/hal/util.h b/point_visual/sdk/src/hal/util.h similarity index 100% rename from librviz_tutorial/sdk/src/hal/util.h rename to point_visual/sdk/src/hal/util.h diff --git a/librviz_tutorial/sdk/src/rplidar_driver.cpp b/point_visual/sdk/src/rplidar_driver.cpp similarity index 100% rename from librviz_tutorial/sdk/src/rplidar_driver.cpp rename to point_visual/sdk/src/rplidar_driver.cpp diff --git a/librviz_tutorial/sdk/src/rplidar_driver_TCP.h b/point_visual/sdk/src/rplidar_driver_TCP.h similarity index 100% rename from librviz_tutorial/sdk/src/rplidar_driver_TCP.h rename to point_visual/sdk/src/rplidar_driver_TCP.h diff --git a/librviz_tutorial/sdk/src/rplidar_driver_impl.h b/point_visual/sdk/src/rplidar_driver_impl.h similarity index 100% rename from librviz_tutorial/sdk/src/rplidar_driver_impl.h rename to point_visual/sdk/src/rplidar_driver_impl.h diff --git a/librviz_tutorial/sdk/src/rplidar_driver_serial.h b/point_visual/sdk/src/rplidar_driver_serial.h similarity index 100% rename from librviz_tutorial/sdk/src/rplidar_driver_serial.h rename to point_visual/sdk/src/rplidar_driver_serial.h diff --git a/librviz_tutorial/sdk/src/sdkcommon.h b/point_visual/sdk/src/sdkcommon.h similarity index 100% rename from librviz_tutorial/sdk/src/sdkcommon.h rename to point_visual/sdk/src/sdkcommon.h diff --git a/point_visual/sources/images/button/approach.png b/point_visual/sources/images/button/approach.png new file mode 100644 index 0000000000000000000000000000000000000000..910bda238eaf8d1e261747bb5b88ffa6b9d80a6b GIT binary patch literal 12926 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 literal 0 HcmV?d00001 diff --git a/point_visual/sources/images/button/back.png b/point_visual/sources/images/button/back.png new file mode 100644 index 0000000000000000000000000000000000000000..9e89b4f05b135f0e52dd3a3d1d78b64b6c2d1f09 GIT binary patch literal 17487 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 t&Y1MXxMeBYp`peP3oIsYGR`Fp?#^jC~vRmV^un zjb+Hb46^U@d-eJLet-VP!@TBo@44r7?z!jO<(%j1xrvc3D-$mh0Dx8R=C#`Zz@eXT zz(5CGHiLfdLlwl_{#vW=mdaD8UWm0?;1?ipb8o{Eki8; zD&xZrOcSAdnD1>}ZBX`ue*wCoyJw(#4N(96;hGEnRWo|swDARi?Z1Eju;fc@e9+^7 z-Zd@rAQEK^R*_{9KKHlvs*U2(cy*vmZs6$4jrg~HFI`D5-`=y73%O+a{@U}C7r2tF zE0EFf>%1Rte-~yxea0ygW?86(dm{2u=IMzCpPKP)bbJr-Ij0^Bj+gbZ@{U=XU9JAO zvKRbADm86%bSFG}N13{zM(*uS=ST|8-f8MR$fElGpdP;BfkgcO`Fxud@q?Ls_?^?k z>S46+8}ywTFi*bki8?6Q3#Qi@QJ-0D9B4%#0dPXfJH2|MC|%u0j1WIk-fQjAm|pe& zol0^+%51G_-imC$wtq0b*$NW|z}9n}(LQSDF;OL_Sq;CpuzG$07>-ofZoKDrr(4SKR#QyQcI_kQHnYD*ZCpQCAIH94%S>~c~j%P@J7kW+U_DLS>9&ei~l5nl7Id=bQnX1@%mbrNb zcE+RNJ!}uN)_bS901Y6RiYt3AB(+6UDqP|z_t?&}d0e19lEAA>3t(Vv<3e*gn&6m1 zcckoR;EY})XTT$S#Fd7PYoVqndc(($GRXmTn0GU&9DQ_3 zhp(zGi=F?+w!G?^SBAJ#J=6*rX}lNGF!b}-Cu_YAA;PY4l9Y%}apJaz!vUed2vw}D z+VjU&!$f3;dDPSUqtn$f7#s*ovu?UQsUiJw2*a`2N(`SSsdLrx!Xa*kQO6$_&=`%e z$nJpr^vHvk7qmG5NH#l{ExXfOllF~1=);}JMx7DV1&+r6%>Q1NpU+M?zVL=U&%`5g z3+;+*WGcMD00&NVDqKUG)itfy735^nd9Q-@X{{d;kPf`k3c@{x8i)U6g+HL=KF61? zDaJBBfhuf098f~cl+DdvBwt}Zp@-`fz4G80NG-|9=!u~L4fg}oCD!Amff6vns0FZJ zdG(CM1r-2*tyBs^L5>{FEO(1DJ@E-Af*> zO^6gK6yY$5AaK&aA%>Tkbs9(i#c&uKEK9-1%*oCo5uuk3#<{Z-$mOe)nP8zcHZ6&S$}Iz(!N{$=U$wIpIVp z1Eqn;MdU17)<^7TZO-isXbRm#97JyJM425Q;kq+Va&#MNJtlPxkmvGRrDc4stNrFB z<>kZ;a6-+^Gl`G%U|_LYv|3;RO&LKGvJZDcnIk;RqMq!DEA1L{)xI{OV=hcJ5^B0Y zn*dH&kx{4D+`7fROs;G>fE>97BeJwaYs$WmLx5+YyAV z?LYU=VF6C$t9D~*K}BR({fd6nfgH=GI*(B#79Mr=gC?g~#Rn59+7ENKcLV_gutg$T z2KTJ~M%zArP84LPEXWV9%G5ZreqKIJQ<&*gN#h^$=;M=6k%6fb-pD@@r(JF}_kX(6|(?9vf4S~PEHsD7K17QPx zbCiVzA?f`0DjB3!P1gNe6zBeT^5=15w8e3yl`*-~L%xttK&HCQxTaMV&)1SwCJeCI(#0bK1Dby_S_apJjEoP}7IKC5?__#Z zKB7}b^aW+G4(nNQ0O4!}^qS{^8MSRQZ_|nQP#pERh_#@1ghzb-QD{WZ@oAdTj#5ZL zA?UdEubbxmuHTJXM(&Bo(S`rq+f%*N!}7AGNqX~l<2SKq*8$eHgoPqExKgcWNFl$@ z+P4|fy0qE1O9#w57X`gL#r;cAJ<6v2&xlxYTF0#i2~y+`Rn z4q`EO`-$j?B-H&3PHR_+KJU+mW4e-$njzhWge91~CkIP~^zhCnw_}n`y=Z!J(m`fN z<@@WASdFf^m;icDO&Dm%?fw%ZX({TCFr<{%TD!Ph_0;6m4~APo6J*W9F$*MB%Xu#z z0J%@{tj8Y14j)Q&YM%yFwcl<=@`>r0Sg7{8>j~q=V74c5UK}uxpCkRGxO8vnC=o%e z{;H*h3KaILZ&%CC$INaiW3t;z8zGN89l}H$KOW%OjU42uOgm59Wkr@1AJL+7};V-o>q^hDxnSK6qiD)BI&v;}$Rx|k(_iX;GPBr`{TgeFn@QdX*WF2Fp)-SSow9tac9mL`oatlAR&usmlfd z+M(f-Ro}SVb}suYB96Wp}*uDdcnV9kX`G*mbufGk$%32g!%pzcZ3(p;kD(f8kQx41i<1jgPHPhaH9VTa9^GZ00`I zBns{~kx!9&Zws3|7*9w+WXaWto&qjs4$CEX_mlOCCG1olZxl3TBA4cUH6MAuYF#gqqyg0DUt4T|?$x`0`4cON4yMD^ zy?X2q9M3U8{Jtk_Mv8fpmm7#ij2_aS=dnEfJJH`60<{ljK9|{daTjZcCatQ&u@O=? z`&`fk`)EVsf}M{Qy;|cpfPMeY*EyX~4g8Fyr32O#ZkbPCE3{^L5^TOtq@!AK28?Cw z_a{-a*SO&G12x2BSz4IL4DEalC6giXp-1kSb4&@`xXS{5^}+5s z;;prV9%&7gy8kMpv(Rw4m;{_|_}xo+;ha>nU3~?=6TL0)=q}(qC~HSVC!P8wKHuOd zK>AD+R183#0Q}LP4y#wT20xdDX10F`+x9(xpLvZCXCSU%+iC4X~ zdvs{MvRdojZ1Kw=D!*X9L;FB9){fRdk4^2e{RY>#HXQrKG`Wd^I0gPuc~%Qs)X<_%3;W4{LIWHnLSl1+q+Mo38!_(6AIwO|Ud@mB7--jBw_~Kr zlXRxgo^{%!S_YVWrK&R54+<=FX6Igp1?7qR3uM$3y%xqfSN1P)2Q85}0c3QmTt20C z{S&@ePD}HWds>_-?W#CcPOgJC5O!#`sA9)*Uile>0kuynmNVvGmPvyDdBfM;mF z+<@5XvYRG}aBx58vUfyWQC|4tc3vtVx@1hgljKud-c{Km7TQep(182>jF{G`Ltats zPy-ZsG8Be1I27)oDN!W5(Mwiy%_8K_0%8LdF&jGO*dA^^9kbp7NH@ANG3k#%uP);y zH!WqCQoZiW>L6`&bya!9obgJlt2admkWlCFZnrrms%48^#kj2R7(KeKfn0zQSh+xgpLRMxR_jBolp$# zL-l^0*#RzJZeDa@#S-O7K<3oaK-{`uY&sQS&kqEsdc4}cbB5y30n=Y{1$Z(hXTW{H zfjXsgKn^Q|Io&XqGU41cx|&^m2d}Wk$>ny50m$C2Ze~i-N1hBG zrEmVSgQFl>d~MA3gP5UwsX0=^?%qZJAVxi`O8Ia1K8E{;m~Z#mH34#UQ@!ZIgK{&< zce69+L1AwbQDbV8v|Z4a{SpHTpC)|NoZrpS?*d;87apz}fWQp$W6c6yJq$3Z?)*UD zWGgp}YW3;Ma-wsp_$p~c4gDb7`f5#?78_aj<=%CRS3j!iR^h7s73^20PywATmGUwzQ&pO4E|spywh6ic4R?*5LozR^I!q9Tvvf3a{=AfF zpPEt>d->F|2c6UX2jdT`f|{fOE<)%^cENh7w~8w#HxR4*Jf(*ry@F)hK0qJ^4+eHB zI`)B8!EfxJAY1q@g%~k*SIFM!;T{4z+JgT3AE8#I@$wAp&>3Zy2Agk2NCysUzLi|j zr3KRkyppQ?4nv|+mSrMfKJt^e?z^ox{oA+aguo$j=dU;S4H#V&P8tWdGP5IJ+%l1N~x-Uz%cDF@m*#JKL^N49{ z$f9OyFE^;m7a@iJDAUuW8eo6=Lh2oQ(M1ICPz0x|5(HgIe`o9dsF8&)^rfcTa%mYy#7J-uUU2kj6K zKuNd^_qkjBA**Oe1EQG3sb%rACaU9iX5l|MK~UJf)n|s#V;IgRi5PyO3PixMzVFnJ z$Zp3)x-d|bQ&cwf!xd&(dj**M+_>V_H<=fv0dRdk0t3`n0%(4o0PmbzijL9YsH@qm z0449q3Fi)ZOMAu-$c+3RXw^S*=$eBATdk3%9o2>`X3}X;QfbZF8GGs77z@C*X>10o z$Od_%x=1I%o#n!*YN=@r4R%13xEftE)VEdQp#v&D|9O5>?Fa#UOe<@P`R!l10xku0 zu!i3*m%=RV@dMP<3P$1o7n4If>e4;h?|FcG{4D!w27R2w8w&yVf^ zu#wD+iDg{qq6$^O{p=n+0^Nfr0A$<$a;+ zJ@8R3l0Wnm!QQPd$J>8<2L@ZEl|uHZ(lj9LEUQfM0KJb^>P|YmhjpQgc2hHKAZqoh z_L5S4mSgp;n1LQ%!l~NS@0g6|yDB>>u1!0p{10fkO=qTNn>+2sm|X&57-Ks_$F^IPIlwUD*jjDlCK%$V*M;?6P-a0Jg|5 z?Uf5Mu1dfY;_yfFl}|Pt&`n$s9jg4#4tRZvGBRz?@2H1n5d>~#2EkV5jGOYPg&{k_ zZc)iI(>*XC-|MjP#<}J*U63Ndgq?fp?mqR(iZMWb>EOv^t>`uYUQJPKrq%hSqi+Eg zMnY+@G4Kx8jTizP`q>Pd%2dSEgb*M&q0@t-qLxg4K~deNz*C@5wvV4a^Do1W2*A+^ zrojB-+mS|9%NK!CpO)@2-ujpuV6`?ru~eK31Qq>Sq7_^0z(kb&dL-RC&=>$tk$XzU z2U;uF0WxkS(2La;UU+zODV+`&@cR)2r>3FZK=W5A%ovXd%+evsEgwc;WE-b3q$VD* zgTetQa5QbX7eIjMo!K1Js;Mxb95l>ovZth$2yo7OL)5M5*5{!d;C0)LE=@$K8g{Y) z0odCy^1G{Zf;7NI=B_tih*4E>7HygySW~3WY)wC42e0?E`NhT-*2H)e{GdQ?}h(2iUnqnWUoiGM5j#ePp$XC zu~(IYDvf=~Fu))5wN0TZuM7~rumM*9fMC!!T{}x%fNS_tze8P>5fZ%u0~G^pX&*+L zen+tZE~WWp@0F|2=B>UKA;DN7HxHcBqvBnunt+g4z<)u-w=B)%z_N&vtPZGlhep3N(g+;uPfN$G#puQ~(v{q1sz=Y|F z8uqt^+6N(VuvUR^;@dd`2xr-+1_*HMC?^Qy-Ga2L5odR-hHdFsI8O@<nZ)eB;|B3{_1po(W zurvop_GPH0j}`c-#T;5!3qbrw>#?@(A>t6?U=ohq`?m_olp<0zPptV{(1ZF^U`?e5 zNy0k;Tl#L`#!CT+b@#QE7uDeunlnY~F;@@(N)_M~!`1OZykL49UPms3VwqxJ0_cZ` z8+G9*K+#cTNKw5d2yaY(WdkyAQPkyrIY(Q$pxq!xaNC0zC-Oc4pQN`cecv zVT7T&fA_!N?(JBmAYO+uPdr)?0Z1($%{t3ZG&nQh>j}F+4}eL+=#*uz^UhD;WID@T znF{5xkavAv+4+`q+{b=|Ay*ct&7-;i3wHzCs%f-hD$ukb3~<-#jVmbV!z^CVba{i> zi4J1)ezY2dkeHMdz&w;L0Vbm?NaJ6CeT???Ky7^tl8FVl`wEc*klcOHD zB!KD|FNaxff;bjnPtFLo&hDH7FKv&{A*Uf0`bnXOi|*V6F#!gEtemnM>H>){g4Zl_ z!9Nkq1;195b^PrFPDZoLqEG_DuO!uY&bJ5x z3jjEm`i{!1RB-Gu%S`TQM*LI$F%S-9Be4H#)eSaF=0vcg*Rg3Zs98y!_e-cb_IM_% zb0tKa+;tg1vHyQmWlP?pT`XV(x}yOKynoaY02kiW{f~Iu-@c!0z(#zS2mjOr85N}Qe?J)heL;_eav9J^mj(E&eknM81puCZ2K{n& z-4Xz%lu-Vq5dLhaa_CR?7f5)qVXs*-dCP|8pTdaR4nEKY`4nLJp=-$1guNZ^w+NYT zh)@{HRB3MWg5}<@$%hCUfD5$O{ANrC$k2UmQy{=TZkF9xT@7Xwu1)oYL}W@)O)Xt= ziI7$X=SNi`IkEc)ta`fiy9-Ew?DTo%Z2xM?VG+FkKkJQ_0~>Y!bl#;Nzomw#klu){ zV*P)IWN4}mGOH9wpuBaNr&~>|GJ)bw*Nv8G{%K%Sad)Shbq(323T2)grj5Wdng{dX zEN-+5FxNp=Lz|He4WfJm`xc*;yx3zX>&Pvb0`{4J&m63R6MDjXY8ph)Oe6DVkG3fx zlmQS}kV64Gd_XOAkG2AOhM4IJe~sG#EASD8U7i+%0~rLdx+M|#(nF&N8Ck9XsAq>S zsxo>tMgi;uFEO((oFfH8L7A)dv|nIoYYX~ha!d9ykw zItXm0)ap$D8s4a4O6R;s>_EjcC`4`YdQ^2di?XGuCJ%x%Hki-;pSLLh2|IY&w?l3D zWZAa|5E)>19$ayt>UbJ}FJC`GejpoUT~j7W@02=2X1aMDl84bpvtp40_Z{l@{$0+! z-cd8iADhIX(OSw0pO>LNqL+g@QkM;zcW)9@(Z2R(;NFTj!20?^vnSHAU{~-m2vXc& zzHK=pw!;MoS3K-`n-REm9;^VHN{S6+uWkm=tn8?|^R(zVlJhJ(029BLWT_FEa3xcJ zP%#vuW>=^EZ@6R-UM}IDBSI>lTZP;ppU0m64l!Ew-oFVW3_!eIU;%jbRgx->g{D_s zCZIMA^O6+?g!AlGKcSK=1Xzaz6>RQoQ(%z0}};;db;C$+6>v95nzJ~O>>v2Q5J`| zoZQ3JSE}<~{}U~!(75Vjk&quAOrYtdx38@z11v&Rx$bGfA`|#3&}998RA*jE@TI=u z0;tlv=Qd)0og|3u2ms;p$yAC<%Ts+O&?67h4x{T!|F;C=N81!0eq4{~xd4Lt+hR4c z{;j}oV#4tM-%<@PTv_K|fwl~Er1%#34F%K^IS4w&J(($|m$`QV0SD`6m^}h!-A#_D zM;6C~9G+t;BnnK_mmLmd*JO?yp?$*uv^>{?OZ?ZI05%{!^K15~;v6&p%QW+6EMPFO zb(_t0C1KN+N=~^1Ow_UJY~#-o>$qV+VZGw8WQ*Ed4+p>Jai(xXtNH`|q?P&kZ3z>9do&C3`%v&oL57BM5j5nfke1dR>T)6d2r zv(Pg8TYrUqsIu+eNvA=8R8*aRxo0$f8X$d3WrYT)2)@8ZR-mXH>@3Da1I}DvZ~P%4 zO&u|YB9z#X%xkXiSOGXg18v;Jj&oyHnxpn7b<_{9vTw{nD1c=@QTB9%fG6Z&g5K+N zl@JOEK0Y$OwwVT~cI#RYuEZA5z=26}{GOO2^JVYHAr)^h0ZlqeA03~uljEdi3I2n%oDyc7K^Y&;C_z`<{(*-I;Uk+TS z1@zs$Fs->WcJM3B2JT@@7EWPu|2tFl@Rs}dNG+Al@_fp95fPs{2K4_}e^HOiz4klx z*Jk67>WrEVB6U+U#3wR0<}0bAuLzFy-Z160PU>z;LEf6cccZJAj)LOaUpi0Rlvh$aO{&Zhu$t`sMcB}z6Z&^1O0k|${EO0)PItMmwYBE0s7PUvObYtet`&Z2M9s25B%_-`U%fUYu6Bm{eGfkYE25-bwMw7 z#4)hXepB%$#vz1!{nU$Y2jkE% z!Zh=S&tbkREX8*ffh^9vf2H6*L{9?w&A~Z3P9S!r!;G`6E1ec#2m47>Tiv0lKd+0l zxF~=97-FFK&jt^`TK1DL9i7$uo3a{gvS65(#N}26zJ##>oCTgty{9{r`}^Rkxqf@;j)M`YzL)%SEov#5Ze|H3Th?r{M?a{dWLhZgPX(s|0KCbIaTXpsBf zEhv<8XJI*KRl+#aOczxAxYve|HmCCu%w`5Cd~{B!x1YMIl<#CVKXm9(9iN$3a992I zD@+jzM?I2v9BHlI#t_WtS$}PYmia3(<6H>s|3X!7{L+5Px*1aggO@%uz_CYcj)#;R z-ogOI+q3R4x12Gmegp-{o+ZF`ltU;PFzYXFw1D?|umdx{eeV-xv4gj&V0fV zRB3#m>S7EO)s@bP+fasM|DZC%>#N-NQ7Ff)QM68a#HzsT(o104Ja4L@3rtQ+O=it3 zXP9qLPpk^0LhHbd54CrI;o0z~y(htGo$5PkgMRhj-?<(`yQpi?kpap}uQ=Jx>jA=K z_&I#uk1}+zvg5%H2M|+f_;xoNWjAq0!wO7fu=zI*Px@y%ejdQA0K)Age7jAy82dzk zh8>t7HP4p6emi(no7^9%!&WxQ6m#_H`=3ZH05Ik&f!&k-_NKQ*Ll}T!-fWN5*D?r> zcEkTz-O|gRn-l=Yu84CJMlzn~rwnrf5YcT!?iy#RrASj299@;`<5!;sJVY71+v;`K z2_0v$9e`o!4vvT#%WDmnYqe#m>Oa6Y3-K50H$>@v%t4zyn+1lQYPNFsKvZqYa84E+ zWU_mG{GxF?5=Z~C3c`cU?W{E$wZ=&M?5~BGSG}|EwUTrV2zY1D4GlF?L(u)GE1b)% zJXU^sE#tBe$;x)hre}@=Jn+AZnp0{jLvX!1kV@$Q$PuxovVuY+haCcj6C|w%Rzul} zV8v53;Fpe+(eiV$ zx>*A+^t2|qR~l^IqV?BU-XtZh1-{_4{ad~XkikHc$s>AT$t=3aP5Z!3;|m~GS7{&U zbPNSPRai5r%XwV36;Bil3=1zZ8=LfH0roa7Ed_%^(c3pqsYXC)AoZa`qqf4E#8mCK zT~G$c^;)pGV@1&LD_sZnkH!C$d@i#xfRULg%|_*`!^Cl35|n~U>Qw~%Z|IIR*(nO~ z<$r$?TlB$1%s7i7kCYdGUyBM}cP|VRrp6p=M<~nTc|y zV5k5@`?LYCAeHf*FY)EZp-0JE)KhSPiQp&IYKOK?x$`qqR!h%{oQL7gWiwEA_#LyE z8Z%FmuD(6K(Jiq{YJKG0a1<9`ZaTu%dV<8Ju>qX8Z6!CBlwn8uvDY!Yo`q{mn&hz5 z`#)8N+#nbSK)(Ix@ZEnQwC@t9-+`aq_-mVJ$JYD@S)YO)Rt&w>AF5WtLz(5vP!z@% z)%_=+)tTQhx8^W2?f3!^dt&6aUqBpR*yZ*2A~x!T5_v84Pl9-D)f&H$#jbabbLgD# zdPC3dlBZ2<&1eJdx2wCn7GgK&>@=@I_#u(a%#x}*4wHu}tDoxB`Xs7&H*MwHBN{fu z%%UHs*r@$o>Ovi$N2OlSTjY->d3gSSCgXC!4O~3YTw9vPV{Lowd zr7%FAip!7rG^E;UWRV&k)1EgynEtC`DEB-cRayOhX#O+n5$%59y3m zDN-T?)Xc8lip%{ZI-qiL^Y6=6zc(B6xV?0)y61ojU-Rhmxe8 zp?U`)Qe2YwKdpS&CO?#w-I{If5!?y@B|Vajf5My1p?yH;H(YBbTAuc4&L}0Ces*N`;I6%deVKHtv^3&8 zt^G!4j>n%;D1(HT!vD@`u716gnfMs%X`_aRgT4YwQ}G|H6pe8eI0P%TZGPg@ zmWX%B38DM4K+utr$=@^XX!$>qkE0~U;+0S7aur*9tm z{{2z-^%p;==kQ}qaX_L%EnKLjp<>DM@#gcdFijeuzN_^jNa564|Kl>d5gKE5N7i=Vu67h(?6&fz=n+^U{FOEqRljt&6ECKz*6t^QZ1v zQ22WBM)CZQ7w{--uy|14t*?PWmteP?yYHRzE+Dp1A(Jy6hrm)r;_nYlU%W72h9_~a z@#P#z;-&dQ@4}kWQdlvX_+IN^2rCyw)zvu=YD%U7QJ)m>RA=u-d$bm|Y=^>C%Pdj< zM!gbTQ_VD|PM5%2hc0IRHvvuQG3EK93$@%KN2K0^h}gM+#Iwx!k(04YN3qNitRd@; zy>@!QBvq4{;>g4X_j_27LUSyRFF#`dM~>XeT+g;!TOv+sP%~>2fv_&e+R0cwxx5Hi zlN^4k{~>6X#Rkk%%D2Fv4O&R85K4q8jn?kAbvI3t$4 zy+P(6ZE?9rCTZ*ze`B{hVgRG0@z=u0l&3ZQe>xwB2DwMRM}=lDhD&-90^fX3D{hkJ zJZ1nd7~sc0{+NEw!>su=RU|w;R>pgD(}C-1TaH{LA)DVL2l;Hrs}*(jJPJXK_9l$U zED_)CC6`_tzVAKKqvRDy=U5>$@E!UNMm9b6!Zr8D{bE zvuHN1_enAnQG&9M*eR{Hd+tYxeqf|Lx+bbNl?R)!fa_`@H*2WJzMwMsrXmDz6bpAs{!o=4384?D z*KfRGb21?d2(eQV7w*1Cv!4jT);+CF2RPQU8ta6r=Sg^9J=FhTki;DIR6DiWT=cfx zyLIxnkz0UxQGdL?GP3XRgCY6`R?-1?RpL)BO=~o3c)0#W%THv*@dt3M;_chG0ETXl zHTR!60vY3PCn$8TE_%GXsfxP_MNXu~mlC-&Kx(M6PRHqPl;f1k`w5G%iAZ!SGhS`@ zKbqE$Nek2y=h4u6B$@^HA?!-oqur&SA>`5=`>q(FT`STTOkVr+#N^Y~K?5m4qigw4 zfSm2`-%T2s#>sNCNP?j_$8hgRd`f>mc}9FKdt_}WL|A>Re9l7@5Z)-1X6@X9N4||K zazqUgFy5%S>WSx$7bOQb$v^Cj0XEnD!3<#`i!JFquBt0=V>>rzwm>wZ^lvY^(|tUF z4g(K^13#@Ukp0Pe*Eztzp=ywJC=ZEU|BQOkzTn%`bRE$erh>opaw`H3?U@{TRV;(* z?w8v|KY66c#FTUAl8&le_*1vqd4G5Gst#Ax5j2l#pcmI*?!5^FbG!bRgd15GOt8Pv z6OJSOy-RiNF0G7@XyL$I*DOY5Bh=8qvDF8$!bZ7#-WX^^`KeQiKhKRqfMD9WqK%x8 z<9E|g6dz`j36FfkwG-)zz^hd}AAzxDZTGTybdwZ%G?QP1hCk~)4?*CsNaI5XQjOkH zm?%cE&!b#;a)0!aGdBMtqk|n$nbbSEpG9HdLBVIOB4NdXnIkV5^>DQ_B)Z%>j-rAK zqXPrD*4MqGy_(K*c4|veqx^8-@=3mj&Tsw&=IGvSjg-IsA}jdJ@y2u`}JT+&Gi&sIzsfFRQ^uX9~u_)%~vUm;zt;`hCCXMcD1 zb;gMzv-QoQl zDV%}pp>mxheyN@cg?Kf>|BfZJ(DaQhW7p}jj9@Th?PDoJ!==3pTE2XPhW7{d?nf@r z^ohHRH<)1&`6GUBB;exrEyEG?5%&O_zVM!Zv1^O z1$$FQb#d{)3GrA1I!Qj3r}-P)A7E4~$+Aglg_N423J8!DcgW2F@L$u~rJ_n=Ee%GP z1M_(vm*?5QU6~r>-eoN+d}}>>q_@?)=wWPp^pJvpP-)pk!?M=oTW#B?`PGb_)_l*4 zgI|EEAFFD~m8V%RM9=r32-j{yKXP^IQc-&}}4~vM{PyC=JB;Ql+f9%}=y``gcQvD6OmSCH!V*5P6 zn2{m>;`x>I$(NrCn=1w*<|pFU=k=&0;)-m78RSNn;gP<#g=Jo?fERqb)jxW@o1y(` z@8)J;FcV$c58AMSPkpRh#k-XoG&G>_zb1J;ulP50v_ZZykP|&webj(}qLefufgfKJ z^&8+`SJsL{$UR){#sDf-@}M*%Ai+1+(R*XZ6h8>T|2pb>rNcvS=CcL}!o-48QL$u} z0hFB*ZfqCln0N_ulJUEpEqjo@NDl)HG}xyDywvktJL__5P;~n`6gTK34xTYUAZyL> z3v98S;s1LD$K;%c&|z9TCF;B%y^NH_LudiElS&DrgDh5!^|-+9j^I2|fDk)xB4)VZ zZXiS(28D#a+H#V9=fv@JAyCq#tDgGChkS7f|N0#}FBv4oUYG*rSaDL@!_4iW7w6fa z6G4wWbA<{m!eE|FJ}GNkNIriLI;HblisI#@F0%U2Na+_`)40DuPRTliM$d_^3SM+M z$adeC&Hl0W{DMR1&T6f7*$(*k&K2&e%F`f=3?6I!K!P~uV*OdxW0rkk03`DDqeShx zc_3lGJ#<6y^$#2y{nITnyp6zv4sc3ZK^g{F-HM*GoA)Tp!W zhLFgHfl!8FO>c`fB_<$jKk=To9qIqv%bGOU|ZIpIBj8k)0~JBbhlsC`;MgD^uNIHk`0UTfbjh>Wn} zoe#{*0J*uMHn(J?Me*KTjtt(e) z;hDtQJnSU703)d9v3(X(-qrFG&xToh-Aa{VUFTgE?azK#jev+*h%QJcai=uf>3L$S sQ0K8cR%m54H0U;z9Qc1fFE*&KzQz{1XAQIN|BCdk8(k~Yc6$8(0LjU71^@s6 literal 0 HcmV?d00001 diff --git a/point_visual/sources/images/button/play.png b/point_visual/sources/images/button/play.png new file mode 100644 index 0000000000000000000000000000000000000000..1e3cd8de8af5cba2b3c889e6622a83fe8545307a GIT binary patch literal 14702 zcmcJ0i9gie_y3(SmWYY$5$~kzTguL$2-){7O0s2-lx0SR%9dT(lRaA|OJb&k>|?JX zWTysYUuNd_^7;J@-|_I6dA;tv=iYnnx#ygFp65O{G16sbpD7CgvjPS`@7+y}1f~T-_ulOrtCff0@$Cjto9DaU}->C|0IODy# zwja_@gWM0LstxSt22NGKy43KsUlq5s8ybPzB})@t2Lw1=JqCpT^{4g)9hgiShA?e@{uoK0GPW|GrDFMaT3@( zLo}Db-}60ELs#_uVXcS}#YQ*3M3sU@5{l zySW>e2^!5{N? zq{5(C-5>nd9OUS`e{yA9?;*maAp8QB#94Ckc#yRXM<2@CoO0j*yM$sy`TR}-HEI|Tqm#GXo1P^l2TJ9s8Vl%Y`)`!kybdcR*8-H{mv2hJ=ylKmv{+C+(nR5`H?KVFXSpH~13$b|Eb;RJ(@gtA%K@2S$l z6?l41K--EhBl$q;1W7_DMIgxZ&CD3V$4K*-QEwZacvK$^9{0C4%ik#$jm06sQ8 zDmbd$@Hxa|i(9U%d)9$sm=v>Dzk1A2;1tNe-qSp_$FoX*_pUB4tMfF(qkQh}RccN9 zy9nh7XP@hw2pE{O=O!+2#_qiwMfM6^XP$X?}jdQio){t z%B|YBe~?3H0e$qvVJ8Frwn(MmQ{}yiBOKf(i_rF&#EK!Dw9@SKpg^dW$|W)_10a|` zY80=dItYEP=JE|XT}EOe*_Y8w505rwIE&EI_6$wu>BHudtD^S{fvrg49x9)FrAX=S1^Hkvg`;s^0LZa0`9o(JX^Od3e~461xx z$=t~z_gD2t;M(^e_V)WRA|hJ7-eRU_w<2Q4c`KkDjwSryPx>BKd<2^ppua^LKtKHu5Mg`kaCfS1dA|{EO(SKv@&reHgI2mR>;3v zUEEB@j~DmgxF>>KWmD_mERkY0eXxCMM3q9rd1=R1lDj^%6Z)RQ~?2N|p9#I%025K^A7$MQT=kjRNxANwp&7^39!GxKs*3Z*o!tW$wcuoiblq1$nNpQ!2}T)9HumNS z#+L&^*DnB+n*8)bCvBu0GilmeHYJaqXHnlAvEHC~jqs%uQw*0Eu8B1;2AKCbSyM+| z##<_>Q#-!(HVItI?WOqr0!L@>8wDQcXRM@DV4%B@K9!AZ5%H}Sn2sD)@bSzlyzTjOL$iDSQ z>N)_sV@3VAv8L`?LX{D^%PN|S_r#vxN2+JGk4oxejJJa)9PAawB-F39ln>Frj6Uak zonK=n?TXOQ2tHbWy{(V4f#>c>JOD-~iy9~&I3g5U{M}2@3K=}Kwy)ul@mACB**l#f ziM1J!!g+7cMoh7qX2aEP)qg&tp)bJNIJBC@Yx0le%RNeS0=$f0G!*Zx0*O2UUdWZfb7aOwaIRd{H>7F7R$9luw@@m(EGHb=R05 zdc&&mzOk6nzcpPx{&(mLE~Q%-cLCcuv`ZrdGIhJpQ?R2+D`@m1d%;MCXU{(o3+ke; zYLeRiO1Zoegn`BQ?gRBZ3)W}MW6-xAT?Zku!rKwu-O|#kcl}=pE<8B_WOQE(g<&^h z9#7F>->yG$NjRnWD&8t00#mGrz44N)#3w|LSEwYRFJSXT_f(d>oeZKVUo+1jiYFd= zs(AI4Wqhszdn@eT`&Ue#G`l+L|xNWaoCxfT0oePM&^8#9sRbRKcQ6paa zI=@&7eUTP6y?9*tk&YzGp|sf6o~77U4(Iq5O=ina4|$Xj%z#NXf>sL0Pvp`%vlPGv zX1a0q_ zNJs4`IOxE^)RD`Qh6O>^OOh%O9b9kvhvwVDVZqPSM5>WAD@_z%LxTD9NT<8!F(BXd z^(H#MMgh&SV-iix(J>XkKXEKj^O4o8NlnwHS=rxZ7P<&k)9l`{{^Fz;ND*5tT)i3E z6owJEbE{r>Tz=;Lr~J8y=2~T_l58Po#zETFVl_$q@;o(MN?;~AcqHuWY`|xy2DvMo ztM6F$vQ3Yv!Fnp+1yNEs8~%`=)_y{YM5Z&9ALRanb8@mF-dz%-V*?gd<`cNohsw;` zRDJinr6Jh0S2dX>g_iJY@tF`64jO?=FP^ai3-{VI%L!^kY*)l&9O{+4a6X4l_a!^a z>!?jXQa<@Lc2yZ0u<&4jw>+{c}s?gYg?)X(in zt~P}AATr_rzCyS_jfW`CCr~)GWBFq}G4A1IP&+furF|&5>6m+j-3qweB`gFsbSCyA z#=_S7DjUe=S`+;XaKb{?3bTFAyK&dYPax%b)`gf1@T|4;U!vY9g1bd0>O+VcFEAbT+5Dki$}Zu zWuOU!G?_)~C4w_Q7Xy`1P1fDb zW=%m?Sk8fEao0Dkqkpq-%2cw$1rT@=e)hg9+lGFuYyA4087tU|6(f^#hh4Jf`vmZ+ z^swC1G)`_7jOW4f>Cxh`mnoh4=+&d?+vks9nNB%h@!OnUxgmu!SNFm6;&JP4=NC`8 zR8+5UbP$Tw(Vt!6ghH;9v@qhY5tmv)!>%b;|2m;lptsrB=G`ljVab`#3ahQExwa_S(u z-Lo`Tte{*bYlo?E{QFuqa88h4r~1TnLYAo~j#(DW7jo!Qxrv@`okehO{jrEtgR4Ns z{=k%aanpJUZSU^J`=bi{oA9Vr$LqUwZtKJjv$J1?On_`<(5UK=cn<$~X8T(WeJA+F z?1|VWv+4)I4uzZ*_5VJd2boD(ebdPr*WTN$DReEsW_ss1y*805%EcKa8I8J)s=8-p z*!fbTDFXq)H&(YSmt%UZz}YhHAI-hfFAsG#GYU~)CN-MktKei{&r)(G_J9IZhhw{a zGjXQ+mJANx79PqLgM?shcih^aOm%jmK%fU;M~%YsKG?{8?=dJYhGWGrWdM$u<7Fmk zWK}1zO72d4ErOZ6^ev{ijtt6r2q{?6-oM2-Lwh!cvaQ|n(tsu(C6?1V{6qGqtNMdW zcMyVgROePk%8jU<3QhwbILgD~?G1yn5B>ykzLw>c#@t{3WZ(o@HllEUf?ia_^#d9c zIboXC@mWTgwEAs-Weh7Y`kWX}3>!Vb31JfhZGpwiL;)Fpblw{I7R>l~eM(h^p&Zt! zn!25JowtiLx{u(c?Mvn*Dzoz;SZh4>r1yg!uA*&GP|4KNJ^0r>^N$+m5*3zz;q{p6 z%yLN8NpDsmK>8?!ZBTC3>u4@y(aNf^-c=O5?``iNr97A`~F8gqI*3(P%GzTisV&>0n-Oj+m%Z;`sv3eLW zcx``ItvmN0KCduIG0X*WT08S2D@Dch3|qce?&Vs}UP9o-#wUxTVKW@la3Tw^!*(5| zs^Gry+H-!l1Aa~WJc~_VM7Ejoy}QGeCm>R`GieAAH1rR`8-K+`N2nwSbG3Ma*N@g z-Z2uA@f?!bTUKyPMXzoFJ;zZ~ZYV!Uc;i{B0`+hi>dfSG-eseNsCQ_fsBU?6yM~9j zO0Z%D8#@|KK9{c?z%ZwI8f^IbdPGp8!jHX>z^eGVltQ1c_v{oMCS^#zEJn4=@RFL9Q_E8Ge2g5U7^rhNuqUm?#!HukB*!lxMzQRSfIYl@I zF4SPCGb;dk7k{k$_uodjov~jf3~#oXw2y1Qenf-$!Hp!*(RUUk&ATFiYYTa)f6AgA zSJ4#n)2wS?JDU#wA*)w63NIE2^E?T3vQ@uZ{?2=Wj_^1$bf^_NISbiRbgF90kI(+u zR&ye#co<%ZWr}jyH|Wx?539W8)&fE5L-+U*JcIt23@azRh72k1 z39b0ch{?S(v4uefa%Z*Oo^I&rfUwr~oP`-hY~d*Tu2=2-6!dwl^s79z^WbYZJV3f^ ziko-}hSA`%$%cQ^YbLAphTG8!(}`(rwD$V)gEwjg-UDGLS6!sefsG{^mV{r7h0Dg? z*f;Y%qW|b$j^Cvpu-U$BdH&=$8;G-Wpkxel`%*{kxQURRM*9td4Qtj2|zYaeVNcm_}lzR zJf0!WPZ^WjorC_+@sBItOAZhwsm>h(8r;OnNwgwM&RGIQnO6VEeM{DKju1tP?dD*H!}=0gJOC z_8kM(mkHYl(DJ}@aRB!!lzH=QT{Y$(O**7)m71FWww4_Ll(s}6j|Te7KVPlL1NSJD zTi1+W2~8%)!rgecqa#!CdrmKhpw+CK=c8D^IT3{>FukTDBx@Qny_>xcE)~0NNt17G`4#X@?Wp=!QTU|Oa!Q?q6pzC| z87MZ)q3*=x;kp^iw@uB!%g|TtG?Vtrmq=HF^J?#$%6vW{7f~PMx@t)W)_eATi0$`O zp}!T?-Ptg6=@RslBMSTZ7KU4MS6*)``}_~;8&|~ zOV$dQ{t2)}hbK7sn!4g^bq?RH!ZF!dCHF3kQQAs=5pqwE&K?ipZMIN@*E>vD4jUYkw3Xkp zz`7=+*zjLG-McvpFfft(cfn9RWO8XW^%R)THc&c6lv)#FM+pI>t-}>)l?ADr6Xg17 zVFXZGGUKM`dW=mlHJ^l6_9!vWs3Y;AkXRfD{Gf`M?n*r|P~vyD>CaHywS=|MgTi#* zvXo$o%btL+eVPu9x~Bxs)hv1DmlaQ&lYyf#O$ZVMuoTp63V0G)dS*mQXqX=<&bd(; zVfa4_ak<|px`xq`QaqoM27&zI^SHd0*T6YAb@Y?Ou$$L+_qank4zqxn@k-1|SLa-J zU(E9xd6lkze5@BjSb)x@i28bp>z=@eA(|EcSOYq)KxG0Zdl*zOfV)yllU=7LrQMIE zsHsEhN$G?OkH!Qk0Hp&GG@vLbsyRKpdp7yAYc`98{xC?11F5VKXQ<=qL-dVKgv@@a zCjz(R0mtk2VLcyzF2X{Xfz_R(Kcq=<_f#u-httFiRon+qx78GvM+$HP)i6}QQi@xX zF+nlD)k7a;PQ#?cU;4FP=MXy!$2j;3n8yJyU>=a4jIfn^##?$$@FD1@!O*7rQqack zF^$HUC68Bv26U#e4$>j3!;>5{|G2^YBr1|f#n(atc==lz{mW37=G)NXpTHmk(4ceF zR&MWYZI56#_R^eNvjJj@8+`h2c*%1ajohX8GkcV>{~l_%V0spBQ9KXQR(mQn*5gF;hVjm4~*RoCW{s&SPAw(CiyQS zJn|5pd0Cj=(+(s2WJ7Aa=Vm|GCRK1|3aE%)PPVButN1^ONC7_1E9FntWcs9`eGz1(|&Z4Brg)K|{>W z(&D{W?B(fR!axahzVx~|@7sg~TvpLL?F6aIZkLBmi~ioot4ISu6MF9Nda?XEtixYk#(tz}nO&)y&HaMUb zNfxTtn@>(mSJkLf}ITA`&UU1==aDp z?Zcbe=I{r$t%Fxn&w%-F!nU_r0yb13B~o!Ne)EvNyjB)V^^vtbwK0XfgS3JSY15)s zz#2VszCtR}&zP6hrh5f1e+Y+TI*j0LR8)(OWcq1J_~wV-q6{Dacl41Fq{{~n!nR_R zDHRfHa*dCNVQ%4?JDwOCBcCBY8yLW9=HP;PERqEe8E39fd%jx_=kiaT;~Do1V}LPs~ao*(P19n(>v{<#OW@#Ee1b?BAT$Cu{E1sExqf z{SY&!aG}7_*QJ~%e-xsB2a&(nI$#N2dfE1~LnXR4xsNfSaVuR`H?EBBvI3jduSh`g z(11zu@GV%Jf+FaUfyQQFcG#V}1k0~}VFKCTJ)l0`6Y6;l=)CSXwo_^rv{3KhK*o_5 zW+HlvBremtQfiby!JJ3+C}2Cp0Jv1d0qllTDkfY*?p4Rm*+>qW7oY{r{UL*vfl<4g&+BX$9s@Q$OG1j);^%F_J@+U;gKY!Sr*08gDw zmoKv9jh^-^b%ju~vKTO9;=;v`K|b2KS8p>CQxs0>-j>TGD=hs7_Kb=LiuO(sq+EBr zs`4JIf4j`z%Yf=&#i0Aw&$=g^{a8M2oGhRm5Kl+N06{1At89XV^h3$2-+40@`8^ff zux}T~RT&`Bq!ZrF;UY>I(eiE6&VxH~9bEsK>C21h-+Ate5Bk3N|014Lnw|lZgM$&T zlJ=&E^6)QSPIz|ALLbb=S%M1lluMi1`|$xeMFYfO#VLM@!?h>R^k6V{r4dxuj;ip? zhZ-pGaxlc*;L^1S2xt3o!K@RlQ39`9rs2GE(3gzJR01|%)&)e;&*nGSNi1t83*3b^ z@apFKO*TkJGw$0SYlPIv2gS8mWMG}d@Y2W3Vox=|y|{~Hzvg>DtW7*1i!>nx!ScVH z4k>lat21?t*u+o#y%~9s)|eIsA(~YmKK4L5V$I{J$;9^5fW3y;3XKDz7E){uZU2>n zqDn!ezW|W(*2|%uFM+GQ6lcvA#YoNZXoNN;T}Olzx_ya)X0l9T6087xnb%n(5?=TA zKq9Lk|8-jLpNogUBl4etL3$%Lz(+zAgPD?JHO&6ZYrIu0n{n&2U-1CR0w)ZX-z#8~ zPUbadAA)a`vFe8u`}p%3TLW2g;Z<3d5|Ax%5Qjw{Ykv7o^s8Dgfdgr4_zX12LbZ&Q zz{{(6eUOinKUsd6j0_ZesaG{P?REl+UI@-9naF+fS)c0m)E7RHGPQW#g4cG^2Wt+Mqq8=0Fey6L<_`+nr29dOo7+ai#4VVheo&_un^`UcCY$> zt(UX&Ihe^Jm}E#UiXPe*5B&7)XY7~s3-RA^WdzD?S{4X|Q9@C;NYcTlQJyER)^vO~ z^Bwfqlum17lHJE!^O#6>%4l|-qUjj9@SVCTqv%dnOg6{9ks(q6=t7IC-yjBgGZTwM z7m(ldwd{r|??V82Kv?AEy&}l0iq=7DA?5dZ_~LThWm?o(umKO+bK^BQG>Q4}O8r4~ zXM`($(3<~ zjYIY|E_sx}Ve0$?@f;1Y-7M&pNA<<_Lk5&o#cfJTW~dy*6)R zg-kgkG6Pr7+3%!Dy_3~m6h1z*Popt4+tS`z0r~Qw9Rp(r~4{P@JY z=?b}s)gW>fdl=<}8Ouki?VktWUkP{XENc2kmdu=O#wKhwqfE7H(`jHQTvns_6xT8r(; ziXz4VnRe_r{V|2zSSpfqd#P^wMUPix`SDcN-?|SOTXbupciuFdVBU2yUP5N4%26|1 zkB&kOluU(%8`SRYm&1e4b*nTs5OnqbmqL5l7_=9ZbxC;hAF~1HIZ=Xe@|X%P_-xBGCLKbd$dAhIZUpw`>m1VcMS~@pu475 ziMi&>KdVeFen<&4??Cd9sbWd#X$q~FI$FO7mer6w5>me++9X74jXxYt-}*zO@8#+v zFZB+rN|;OQm!0r#hzjl9&yic(WSBI>uCj(W!mwu zm8rPA>f2Yj{HAZc-YTYg73Anb^HmYPi-g8I8Z8>&n-KX}`XJ|(hLg6D_GG9c%+R_l z7`J$k2Oav=l0x-&`pt|Tx?p +jmxqL{#@T|d4@LVL8aMPar?`mN}4isS&_yzhTt zS)&iP?kB^Dx+Qo&OFxGq&27H}|9~T}*E|mVOvi}ZYl^uUExA5)dhg8*XXf2HS7|~h zaoZRBSKZx6A00Ys=P6lXm;IO9B5XEdt-V;Zc3m}s9_6$k)g3Ijt=Z07I-?84q?W30 zvFCdnkjYHS==^H=^}NuPytVx|x(A*Nz^DF5&x)PSb08wt;ILn2RTC)XHYw@WjQ*WA zJJ5UgA0Y7lo@|p5kN=0SB*ar?WBq)=uubZ{Wt0#-Z;WAA(i1 zRVHr#LV|}5Yx(ORE2B?CkuaI`v)w{GiMQl0KgJxCPCeD%ix~R0MjtRVNsaF9l<`T6 zqcB@+wQbb!&9zPDjSeb1rSHJh9ad)AxBKmKX)n`)M=CuzaUVLq(UxA@tqPyZX2G{l zP>H>reljg5EeeXEU>DY&kFPUcCkQ|E^Yzq0tkWWm#k@I7Zl&Fhy`jf?WOsz1f(_;8 znCd&@_CM}1dTAT3>mi_Ac8_5q13@?7D2Q!#LQxME3q3h-`JrGqNI}B@lLmJok zHKVvz!-`Y8^F7_0Vn|opX&IfL|ER^&WP5vgj)je?` zB&SU@CBmFLs$>O*ORYy9J@#S&!C8p`@pc~rq;}Ke8Kr_2xMR z=_CmdQj#=mtf-XUcR6pFNCU3z+o+Dbrvuenaa%Hu zMZkAW5A6lT!HYSKLd0PhF-7m70Eu$X%e^fuU|`su8gGr4qJpi(FGl3o zLO%YYTi$Mk)|p>Z^n{wI71DIuv*lFF)-~KVK;=-GjS^hH4I%|VfJu02kx|0Jg;=3> z1mI{^$I?JWLGHMLO91ucN@kwi%Ico44+)Ot1|qB*$7sYuckvKT;Qs7izR&&ps$2~_ zJJX&(FgN|7J0nW(;vWu>Py1-GY-Zrx#m++4*$KN(r)Zd|3-ill(x?=Whg74%A0YK9q2eQ z)-e3w-{ly^-b-*)lzd;=Id^TN1l<9Op`_ExF&a!4J0he&EE1{oQ4=_~ii~^I7S0X6 zrapm6Gd8lYIINC-EkhxvtbrW|8T+J_M4qd0JzLqxitkt8q0 zgkco+TfI;<5^Z#nJ+guHr*8QB?a2TcrO(06AGf<0>k}ySxUK zkjttq1j>!J@9K7L9S~a25S{#>8d?u;2m}V-u-9r%FxZ4cs9b$lm?7=6MS_(-gvmJ< zwcpmry^&e1=F9-HhdeQLIs7S$Ls*HIZ&4tZxx{TWziw{mQh0LD=ItBM&Qy@B>fFT| zOZ`HR!C@{<9ZS~lU9G^Sn)1WuG7=Du^ zQ%-)x&QPOz3gsi9*O#RtalzyXa30I8Z?wiVJd~6~9+eO%aRvy%)0Lan9k_<4{~T9H zd8UxhpfreFt}7r{?f2XZ_i~?p-&g>_!owd4YbQuW{Rsx*O%cOk-rGjNq-H&8Cui%) z?qY-1p}xD*f*fr5bmjJLLr$y|a(P5;ZYwG->&pYEm2+C9{(1m4R_( zwN?`6X(|NCE4+vvx0$?z&&q(1cvdLk3-Q5H!;=ajW(_vWLt7UTnW?FT>n|Y9R!Lr6VM<)d4>+tM9Ox zZ$Du%F8oX+{*ICJFw`&h7S9mJo_jY*M>d!FN*`-JS<(DlbH;?8Wcv@ZKd_adR<=o1 zξ4bVRJyFw8EZp$0QI`YftgGFK2z|DqUytI$5w-pu=Rajt+|UK_~+L%9~4#gsSg zWN`R>5<5{Y8Fkr7v1@_mMd2Nsv3)jCJTmu^-S2@lDEMGGv=(U$MOE1zW326Z*(qMJ zS!ZD)ZQWa6YTwuNqmda2Apb7C*5Pg56C$^+`^fN%KZK!plsC!M67wOKuazrN2Le|Z zfSt@)Vx7l%XU+dQ-+clcEN0;}tF%D|)Psz~!X)v>#gsHZl z)Ctkl>?99u$(SYQ>&USLh(_j`{wFJZ;OOhmTXkN4Z#Y|{je-D*bPI^odgc7N51d|f z?((RK|LPo0WjfgO;?z{UJgSe}6pOj`WUca=g|_f?8SA(W^> z@X3MV)~4cx>C_~PdEMVfwyU}T(A%#QddvPVTaeh1 z>3j?nQ1lc0xyO6L#%AWP7o)az8t$I|)hB6i{FWpmsh&W=-;T^PbGjq44ke=64xRd_ z|0u!E)+?-;IcCRK`|x6{I4uM(8d1im`RzsZMUdukB#Uq_em9147xP&$F=m1$atjI$ z+(Z^BQ9pL(O>}oR|Jph?6G=^n<gIBk9k52x-t z3V==>vr{#nX4e}p#xpEOel3U1%X#`Ips~;!g&@kv)@e>pQ?C?DiS_ry&c+_D-)ChLcfQIUwS2{!s_kk0x%#3rQvkwOx^hyUxY#-8$~1N z6j}O6dAUZD(_P}m3E@qlkJAEL1C4Dy% z%4N@wS#^w~%YIo*XCdHd&!O>>BIigrWO339^nMwW(M)k3a{rAzX-=v$ihrPcBvLjr$il_OZ@jvvpa<{h4YByUv zB;9iwzy6uBHC_4VZlersn%#@t=#drarM`&%t-~MpYL6}#8VLfF@!6^Ix6YV575iw6 zz;z+SYyCZ=gLSnPUs3HQZPDv|dcGSPquXd{J?a@;&OWn9!_(lXTH!0u%?0{L`O|4r zQv?BXN0?R#S&*!jO)&}zg6E*KVy~5H3*6w}H92UADkI;q?wlg8FC%GV+=_9>DcAbj z!_}7-Jq=`(;!U3#=_02szUuZItSLkvLi3Fq@wjirB$LRrm7o)%&O>C@5!N_$oMB0Z z4i&T=e9|&`Tj|yF>cac70}xjmi;gfo+7tLPuy`ZO!b44?=K$ivZFi3htCR13SwP7O zY9gsWaIwdbNvgg$t%Zj3OML#yoZ^#PP17%}E=UEF)X`-%*4q{!+m3R4CX zWZ8b890BP8LmMbXmGq~h9ER2D3b#IUEez@)+G7;2RNyUN{;j}=DD}v|y}?Fe_ap=? zaT;{pyzQSCc`cj8;_9`~`r-r#41n(6;A_j?&s-}A$Ir^cOv?Wf*044$zW)P)oDZx@ zP|l8wC&7B^XPjElkxE=#1VkzDO<%P7o!XCrS)+KNwB^x7)rP40#AE2lnNn$32>0UV zz&vRn^E>(;#{BPs(Ia0|URD#Cnm7nh9Xdn#$*Hh>%UT_x#N#mylN5IFl@Pw_EAo8( z79^P?OAwwk6UWF{Rzp}RsFX`TecPv4!fH7bfBRRisL=^1_)n#Swo@xXCq4{zW8l}w z%5)ZVzh)L{W)dQkU-Wdnp;44|7@_#j5N*A>^>FyO;GxXMmDnJ+E z6ervyiI>%l6Vx|%p<RKWl32SrQKxK@QLc_W1~ QKkiBIn$guNE$7Go56D+AsetFixedSize(380, 300); userLabel = new QLabel(this); - userLabel->move(80, 80); + userLabel->move(70, 80); userLabel->setText(tr("用户名")); userEditLine = new QLineEdit(this); @@ -18,7 +20,7 @@ LoginDialog::LoginDialog(QWidget *parent):QDialog(parent) userEditLine->setPlaceholderText(tr("请输入用户名")); pwdLabel = new QLabel(this); - pwdLabel->move(80, 130); + pwdLabel->move(70, 130); pwdLabel->setText(tr("密码")); pwdEditLine = new QLineEdit(this); @@ -33,23 +35,24 @@ LoginDialog::LoginDialog(QWidget *parent):QDialog(parent) exitBtn = new QPushButton(this); exitBtn->move(220, 200); exitBtn->setText(tr("退出")); - + connect(loginBtn, &QPushButton::clicked, this, &LoginDialog::login); connect(exitBtn, &QPushButton::clicked, this, &LoginDialog::close); // 待解锁的组件名称 - setting_name = {"laser_topic", "open_file", "bag_progress"}; + setting_name = {"cloud_topic", "laser_topic", "open_file", "bag_progress", "fixd_frame_topic"}; + // 账号路径 + user_file_name = "../PointVisual/src/point_visual/sources/user/user.txt"; - // to do read from file + // 创建用户表存下 createUser(); // userPasswords["root"] = hashPassword("root123"); // userPasswords["wbw"] = hashPassword("123"); } - void LoginDialog::createUser(){ - std::ifstream inputFile("../Rviz/librviz_ws/src/librviz_tutorial/sources/user/user.txt"); + std::ifstream inputFile(user_file_name); // 第一次读写user.txt将user加密后,建立userPasswords if(inputFile.is_open()){ std::string line; @@ -57,7 +60,7 @@ void LoginDialog::createUser(){ std::istringstream iss(line); std::string username, pwd; if(iss>> username && iss>> pwd){ - if(pwd.size() < 8){ + if(pwd.size() <= 8){ // 根据长度判断是否已经被加密 pwd = hashPassword(pwd); } @@ -74,7 +77,7 @@ void LoginDialog::createUser(){ // 加密后的账户密码写入user.txt void LoginDialog::writeHash(const std::map user){ - std::ofstream file("../Rviz/librviz_ws/src/librviz_tutorial/sources/user/user.txt"); + std::ofstream file(user_file_name); if(file.is_open()){ for(auto iter = user.begin(); iter != user.end() ; iter++){ @@ -124,15 +127,16 @@ std::string LoginDialog::hashPassword(const std::string& password) { // 判断用户账号返回不同code int LoginDialog::checkUser(const QString username, const QString pwd){ auto iter = userPasswords.find(username); - if (iter != userPasswords.end()) { - if(iter->second == hashPassword(pwd)){ - // 用户密码都对 - return 2; - }else{ - // 用户对,密码不对 - return 1; - } - } else { + if(iter != userPasswords.end()){ + if(pwd.size() > 8) { return 1; } + if(iter->second == hashPassword(pwd)){ + // 用户密码都对 + return 2; + }else{ + // 用户对,密码不对 + return 1; + } + }else { // 用户不对 return 0; } @@ -168,35 +172,6 @@ void LoginDialog::login() userEditLine->clear(); pwdEditLine->clear(); userEditLine->setFocus(); - // //判断用户名和密码是否正确 todo 函数判断 - // if (userEditLine->text().trimmed() == tr("tom") && - // pwdEditLine->text() == tr("123456")) - // { - // for(int i=0 ; ifindChild(setting_name[i]); - // if (foundChild) { - // qDebug()<<"111" ; - // // 找到了指定的子组件 在这里可以对子组件进行操作 - // foundChild->setEnabled(true); - // }else{ - // qDebug()<<"第" << i+1 <<"组件未找到" ; - // } - // } - // QMessageBox::information(this, tr("登录成功"), tr("已解锁"), QMessageBox::Yes); - // // landed = 1; - // accept(); - // } - // else - // { - // QMessageBox::warning(this, tr("登录失败"), tr("用户名或者密码错误"), QMessageBox::Yes); - // } - // userEditLine->clear(); - // pwdEditLine->clear(); - // userEditLine->setFocus(); - - - // QMessageBox::information(this, tr("提示"), tr("已经登陆,请勿重新登录"), QMessageBox::Yes); - } diff --git a/librviz_tutorial/src/loginDialog.h b/point_visual/src/loginDialog.h similarity index 97% rename from librviz_tutorial/src/loginDialog.h rename to point_visual/src/loginDialog.h index aed63ae..fa60a2a 100644 --- a/librviz_tutorial/src/loginDialog.h +++ b/point_visual/src/loginDialog.h @@ -46,6 +46,7 @@ private: // std::vector setting_name; // 用户表 std::map userPasswords; + std::string user_file_name; }; diff --git a/librviz_tutorial/src/main.cpp b/point_visual/src/main.cpp similarity index 100% rename from librviz_tutorial/src/main.cpp rename to point_visual/src/main.cpp diff --git a/point_visual/src/myviz.cpp b/point_visual/src/myviz.cpp new file mode 100644 index 0000000..2e8ef19 --- /dev/null +++ b/point_visual/src/myviz.cpp @@ -0,0 +1,2038 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rviz/properties/property_tree_widget.h" +#include "rviz/selection/selection_manager.h" +// #include "rviz/views_panel.h" +#include "rviz/panel.h" +#include "rviz/view_controller.h" +#include "rviz/view_manager.h" +#include "rviz/view_controller.h" + +#include "rviz/visualization_manager.h" +#include"rviz/tool_manager.h" +#include "rviz/render_panel.h" +#include "rviz/display.h" + +#include "myviz.h" +#include "../tools/color.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); + + // 全局变量存放处 + // 颜色名称,可添加,待设置rgb类型设置颜色 + circle_index = 0; + color_name = { + "white","red","green","black","blue","yellow", + "pink","purple","skyblue","lightgreen","orange", + }; + // 颜色路径名 + color_icon_filename = QString("../PointVisual/src/point_visual/sources/images/color/%1.png"); + button_icon_filename = QString("../PointVisual/src/point_visual/sources/images/button/%1.png"); + speed_name = {"2.0","1.5","1.25","1.0","0.75","0.5",}; + // circle_radius = {}; + ispauseclick = true; + sliderreleased = false; + playagain = false; + isthreadplaying = false; + stored = false; + + dlg = new LoginDialog(this); + landed = 0; + cur_fixed_frame = "world"; + + // 网格结构 + QGridLayout* controls_layout = new QGridLayout(); + int ctl_index = 0; + + // 登录按钮 + QPushButton* login_button = new QPushButton("登录"); + QPushButton* quit_login_button = new QPushButton("退出"); + + //select按钮 + QPushButton* selection_button = new QPushButton("选择坐标点"); + QPushButton* move_camera_button = new QPushButton("移动视角"); + + controls_layout->addWidget( login_button, ctl_index, 0); + controls_layout->addWidget( quit_login_button, ctl_index++, 1); + + // offline 布局 + // QPushButton setting!//////////////////////////////////////////////////////////////////////////////////////////////// + QIcon pause_png(button_icon_filename.arg("pause")); + QIcon gogo_png(button_icon_filename.arg("approach")); + QIcon back_png(button_icon_filename.arg("back")); + QIcon play_png(button_icon_filename.arg("play")); + + play = new QPushButton(play_png, "", this); + + QPushButton* but_save = new QPushButton("保存一帧", this);// buttonn: save one frame + QPushButton* but_saveMul = new QPushButton("保存多帧", this);// button: save multiple frames + QPushButton* but_file = new QPushButton("打开文件", this);// button: select file + + QPushButton* but_nextFrame = new QPushButton(gogo_png,"", this);// button: next frame********************************************************************************************************************* + QPushButton* but_previousFrame = new QPushButton(back_png,"", this);// button: previous frame********************************************************************************************************************* + + // QLabel setting!///////////////////////////////////////////////////////////////////////////////////////////////////// + QLabel* label_speed = new QLabel("播放速度:");//speed combobox + QLabel* label_split = new QLabel("");//speed combobox + + // QSlider setting!//////////////////////////////////////////////////////////////////////////////////////////////////// + progress = new QSlider(Qt::Horizontal); + // QComboBox setting!////////////////////////////////////////////////////////////////////////////////////////////////// + QComboBox * speed_combobox = new QComboBox(); + + QPushButton* but_trans = new QPushButton("格式转换", this);// buttonn: save one frame + + //enable button//////////////////////////////////////////////////////////////////////////////////////////////////////// + play->setEnabled(true); + but_save->setEnabled(true); + but_saveMul->setEnabled(true); + but_file->setEnabled(false); + + // setting button size!//////////////////////////////////////////////////////////////////////////////////////////////// + play->setFixedSize(130, 30); + but_save->setFixedSize(130, 30); + but_saveMul->setFixedSize(130, 30); + but_file->setFixedSize(130, 30); + + but_nextFrame->setFixedSize(130, 30); + but_previousFrame->setFixedSize(130, 30); + + for(int i = 0; i < speed_name.size(); i++) + speed_combobox->addItem(speed_name[i] + "x"); + speed_combobox->setCurrentIndex(3); + + controls_layout->addWidget( label_split, ctl_index++, 0 ); + controls_layout->addWidget( but_file, ctl_index, 0 ); + controls_layout->addWidget(but_trans, ctl_index++,1); + + controls_layout->addWidget( but_save, ctl_index, 0 ); + controls_layout->addWidget( but_saveMul, ctl_index++, 1 ); + controls_layout->addWidget( move_camera_button, ctl_index, 0); + controls_layout->addWidget( selection_button, ctl_index++ ,1); + + controls_layout->addWidget(label_speed,ctl_index,0); + controls_layout->addWidget(speed_combobox,ctl_index++,1); + + controls_layout->addWidget( play, ctl_index++, 0 ); + + controls_layout->addWidget( but_previousFrame, ctl_index, 0 ); + controls_layout->addWidget( but_nextFrame, ctl_index++, 1 ); + controls_layout->addWidget(progress,ctl_index++,0, 1, 2); // addwidget(widget,row,col,m,n);//m是占几行,n是占几列, + + controls_layout->setSpacing(5); + + // 是否选择显示坐标轴 + QLabel* label_axes = new QLabel("参考坐标系:"); + QCheckBox *axes_checkbox = new QCheckBox("", this); + axes_checkbox->setStyleSheet("QCheckBox::indicator { width: 20px; height: 20px; }"); + // QComboBox * axesBox = new QComboBox(); + // axesBox->addItem("不显示"); + // axesBox->addItem("显示"); + + // 选择圆圈范围 + QLabel* label_circle = new QLabel("距离参考圈(m): "); + QLineEdit* circleLineEdit = new QLineEdit(); + circleLineEdit->setPlaceholderText("请输入数字"); + circleLineEdit->setFixedSize(130,30); + // 设置父组件 + circleLineEdit->setObjectName("circle_edit"); + circleLineEdit->setParent(this); + // 增加圆圈增加,删除按钮 + QPushButton* but_addCircle = new QPushButton("添加参考圈"); + QPushButton* but_deleteCircle = new QPushButton("删除参考圈"); + + // 以红色坐标轴指向自己为正视图 + QPushButton* but_view0 = new QPushButton("正视图"); + QPushButton* but_view_left = new QPushButton("左视图"); + QPushButton* but_view_right = new QPushButton("右视图"); + QPushButton* but_view_top = new QPushButton("俯视图"); + + + controls_layout->addWidget(but_addCircle, ctl_index, 0); + controls_layout->addWidget(but_deleteCircle, ctl_index++, 1); + + controls_layout->addWidget(label_circle, ctl_index,0); + controls_layout->addWidget(circleLineEdit, ctl_index++,1); + + controls_layout->addWidget(label_axes, ctl_index,0); + controls_layout->addWidget(axes_checkbox, ctl_index++,1); + + + controls_layout->addWidget(but_view0, ctl_index, 0); + controls_layout->addWidget(but_view_top, ctl_index++, 1); + controls_layout->addWidget(but_view_left, ctl_index, 0); + controls_layout->addWidget(but_view_right, ctl_index++, 1); + + // Tree 全局变量存放地方 + Tree_Display(controls_layout, ctl_index++); + + // 待解锁子组件设置父组件,方便寻找,登陆后解锁可编辑属性 + login_button->setObjectName("login_button"); + login_button->setParent(this); + quit_login_button->setObjectName("quit_login_button"); + quit_login_button->setParent(this); + quit_login_button->setEnabled(false); + + but_file->setObjectName("open_file"); + but_file->setParent(this); + progress->setObjectName("bag_progress"); + progress->setParent(this); + progress->setEnabled(false); + + // controls_layout->setRowStretch(3, 0); + // Make signal/slot connections. 进行信号/插槽连接 + + // 增加登录按钮 + connect(login_button, SIGNAL(clicked()), this, SLOT(login_button_clicked())); + // 增加退出按钮 + connect(quit_login_button, SIGNAL(clicked()), this, SLOT(quit_login_button_clicked())); + + // 增加切换select + connect(selection_button, SIGNAL(clicked()), this, SLOT( slot_select())); + connect(move_camera_button, SIGNAL(clicked()), this, SLOT( slot_move_camera())); + + // 增加坐标变换 + // connect(axesBox, SIGNAL(currentIndexChanged(int)), this, SLOT(AxesDisplayChanged(int))); + connect(axes_checkbox, SIGNAL(stateChanged(int)), this, SLOT(AxesDisplayChanged(int))); + // 增加圆圈变换-弃用 + // connect(circleLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(CircleDisplayChanged(const QString&))); + // 增加圆圈 + connect(but_addCircle, SIGNAL(clicked()), this, SLOT(CircleDisplayAdded())); + // 删除圆圈 + connect(but_deleteCircle, SIGNAL(clicked()), this, SLOT(CircleDisplayDeleted())); + // 增加视角回正 + connect(but_view0, SIGNAL(clicked()), this, SLOT(ViewZero())); + // 增加视角左转 + connect(but_view_left, SIGNAL(clicked()), this, SLOT(ViewLeft())); + // 增加视角右转 + connect(but_view_right, SIGNAL(clicked()), this, SLOT(ViewRight())); + // 增加视角俯视 + connect(but_view_top, SIGNAL(clicked()), this, SLOT(ViewTop())); + + //off + connect(but_nextFrame, &QPushButton::clicked, this, [&]() { + if(!ispauseclick){ + is_next = true; + }else{ + QMessageBox::information(this, "tips", "No file is played"); + } + }); + connect(but_previousFrame, &QPushButton::clicked, this, [&]() { + if(!ispauseclick){ + is_previous = true; + }else{ + QMessageBox::information(this, "tips", "No file is played"); + } + }); + + connect(progress,&QSlider::valueChanged,this,[=](){// 变动会触发 + if(!ismoved){ + ischanged = true;// “戳”这一个动作会使ischange变成true + } + }); + connect(progress,&QSlider::sliderMoved,this,[=](){// 变动会触发 + sliderreleased = true; + ismoved = true; + }); + connect(progress,&QSlider::sliderPressed,this,[=](){// 拖动时打开暂停 + ispauseclick = false; + // play->setText("Play"); + play->setIcon(QIcon(play_png)); + }); + connect(progress,&QSlider::sliderReleased,this,[=](){// 释放时关闭暂停 + // sliderreleased = true; + ispauseclick = false; + ismoved = false; + // play->setText("Play"); + play->setIcon(QIcon(play_png)); + }); + + connect(but_save, &QPushButton::clicked, this, [&]() { + if(isthreadplaying){ + ispauseclick = false; + SavefileName = QFileDialog::getSaveFileName(nullptr, "Save File", "/home","files(*)"); + is_save = true; + } + // QMessageBox::information(this, "tips", "Save Complete!");} + else + ; + // QMessageBox::information(this, "tips", "No file is played"); + }); + connect(but_saveMul, &QPushButton::clicked, this, [&]() { + if(isthreadplaying){ + if(is_saveMul){ + is_saveMul = false; + is_saveMulFinish = true; + QMessageBox::information(this, "tips", "Save Mul Complete!"); + } + else{ + SaveMulfileName = QFileDialog::getSaveFileName(nullptr, "Save File", "/home","files(*)"); + is_saveMul = true; + std::string nametype = find(SaveMulfileName.toStdString()); + if(!ispauseclick && !is_saveMulFinish && is_saveMul && nametype=="bag") + { + QMessageBox::information(this, "tips", "Move Slider to Choose End!"); + } + } + }else{ + QMessageBox::information(this, "tips", "No file is played"); + } + }); + connect(but_file, &QPushButton::clicked, this, &MyViz::selectFile); + + connect(speed_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(SpeedChanged(int)));//speed change + connect(play, &QPushButton::clicked,this,[=]() {// ********************************************************************************************************************* + // 只有当线程启动时才能按暂停键 + if(filePath.isEmpty()) + { + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Please Choose a File!"); + msgBox.resize(2000, 1000); + msgBox.exec(); + } + else{ + + + if(threadout && !playagain){ + threadout = false; + playagain = true; + ispauseclick = true; + isthreadplaying = true; + stored = true; + // play->setText("Pause"); + play->setIcon(pause_png); + std::string choice = find(filePath.toStdString()); + int filename; + if(choice == "bag") + { + filename = 0; + if(Topic.isEmpty()) + { + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Please set topic!"); + msgBox.resize(2000, 1000); + msgBox.exec(); + filename = -1; + isthreadplaying = false; + playagain = false; + threadout = true; + ispauseclick = false; + stored = false; + } + } + if(choice == "pcd"){filename = 1;} + if(choice == "ply"){filename = 2;} + if(choice == "bin"){filename = 3;} + + switch(filename) + { + case 0: + {std::thread rosBagPlay(&MyViz::openFile, this); + rosBagPlay.detach(); + break;} + case 1: + {std::thread rosBagPlay(&MyViz::pcdFile, this); + rosBagPlay.detach(); + break;} + case 2: + {std::thread rosBagPlay(&MyViz::plyFile, this); + rosBagPlay.detach(); + break;} + case 3: + {std::thread rosBagPlay(&MyViz::bin, this); + rosBagPlay.detach(); + break;} + } + } + else if(isthreadplaying && playagain && ispauseclick){ // pause + ispauseclick = false; + play->setIcon(play_png); + }else if(isthreadplaying && playagain && ! ispauseclick){ // not pause + ispauseclick = true;; + play->setIcon(pause_png); + } + + + } + + }); + // connect(bg_topic_text,SIGNAL(textChanged(const QString&)),this,SLOT(setTopic(const QString&))); + + connect(but_trans, &QPushButton::clicked, this, &MyViz::TransformeType); + + // Construct and lay out render panel. + render_panel_ = new rviz::RenderPanel(); // RVIZ在QT上显示的类 + QHBoxLayout* main_layout = new QHBoxLayout; // 主要layout QV垂直,QH水平 + + // 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_ ); // 是实现rviz功能的类 + // 初始化camera 这行代码实现放大 缩小 平移等操作 + render_panel_->initialize( manager_->getSceneManager(), manager_ ); + manager_->setFixedFrame("laser"); + // 初始化tool_manager_ + tool_manager_ = manager_->getToolManager(); + + // 在初始化之后再进行更改操作 + manager_->initialize(); + manager_->startUpdate(); + + manager_->setFixedFrame(cur_fixed_frame); + + // 创建select panel + QLabel *select_label = new QLabel("坐标点显示:"); + select_tree_widget_ = new rviz::PropertyTreeWidget(); + select_tree_widget_->setModel(manager_->getSelectionManager()->getPropertyModel()); + ctl_index++; + controls_layout->addWidget(select_label, ctl_index++, 0); + controls_layout->addWidget(select_tree_widget_, ctl_index++, 0, 1, 2); + + // view_tree_widget_ = new rviz::PropertyTreeWidget(); + // 视角类ViewManager + view_man_ = manager_->getViewManager(); + viewController = view_man_ ->getCurrent(); + ROS_ASSERT( viewController != NULL ); + // view_tree_widget_->setModel(view_man_ -> getPropertyModel()); + // controls_layout->addWidget(view_tree_widget_, ctl_index++, 0, 1, 2); + + // 设置网格某一行伸缩比例,放最后一行 + // controls_layout->setRowStretch(--ctl_index, 6); + + // Create a Grid display. + grid_ = manager_->createDisplay( "rviz/Grid", "adjustable grid", true ); + ROS_ASSERT( grid_ != NULL ); + grid_->subProp( "Line Style" )->setValue( "Billboards" ); + grid_->subProp( "Color" )->setValue( QColor( Qt::white ) ); + + // 新增点云 + cloud = manager_->createDisplay( "rviz/PointCloud2", "point cloud", true ); + ROS_ASSERT( cloud != NULL ); + cloud->subProp("Size (m)")->setValue("0.005"); + cloud->subProp("Color Transformer")->setValue("FlatColor"); + cloud->subProp("Style")->setValue("Flat Squares"); + // cloud->subProp("Topic")->setValue("/pose_graph/octree"); + // cloud->subProp("Invert Rainbow")->setValue("true"); + // cloud->subProp("Decay Time")->setValue("1"); + // Configure the GridDisplay the way we like it. + + laser_ = manager_->createDisplay( "rviz/LaserScan", "Qlaser", true ); + ROS_ASSERT( laser_ ); + // 新增雷达 + laser_->subProp("Topic")->setValue("/scan"); + laser_->subProp("Size (m)")->setValue("0.015"); + laser_->subProp("Color Transformer")->setValue("FlatColor"); + laser_->subProp("Color")->setValue(color_name[1]); + + // 新增坐标系 + axes_display= manager_->createDisplay("rviz/Axes", "Axes", false); + ROS_ASSERT( axes_display != NULL ); + axes_display->subProp("Length")->setValue(0.2); // 设置透明度 + axes_display->subProp("Radius")->setValue(0.05); // 设置坐标系的缩放大小 + // 新增Marker_ 显示圆圈 + marker_ = manager_->createDisplay("rviz/Marker", "Marker", false); + ROS_ASSERT( marker_ != NULL ); + marker_->subProp("Topic")->setValue("visualization_marker"); + + // Set the top-level layout for this MyViz widget. + main_layout->addLayout( controls_layout ); + // main_layout->addLayout( controls_layout ); + // main_layout->addWidget( view_tree_widget_); + main_layout->addWidget( render_panel_,2); // 存放rviz的位置, stretch 拉伸系数 + setLayout( main_layout ); + +} + +void MyViz::Tree_Display(QGridLayout* controls_layout, int index){ + QTreeWidget *menu = new QTreeWidget(); + // menu->setColumnCount(2); + // 列头自适应大小 + menu->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + menu->setHeaderLabels(QStringList()<<"key"<<"value"); + menu->setHeaderHidden(true); + + // 设置不同层次菜单的缩进 + // menu->setIndentation(10); + // global-一层树 + QTreeWidgetItem *global= new QTreeWidgetItem(menu, QStringList("全局变量")); + menu->addTopLevelItem(global); + global->setExpanded(true); + + // fix frame + QTreeWidgetItem *fixed_frame = new QTreeWidgetItem(global, QStringList("固定参考系")); + QLineEdit* fixed_frame_text = new QLineEdit(); + fixed_frame_text->setText("world"); + // 方便寻找,登陆后解锁 + fixed_frame_text->setObjectName("fixd_frame_topic"); + fixed_frame_text->setParent(this); + fixed_frame_text->setEnabled(false); + // fixed_frame_text->setStyleSheet("background:transparent;border-width:0;border-style:inset"); + // 初始化固定坐标系 + cur_fixed_frame = fixed_frame_text->text(); + // 设置事件 + connect(fixed_frame_text,SIGNAL(textChanged(const QString&)),this,SLOT(setFixedFrame(const QString&))); + // 将其他控件(非TreeWidgetItem)放入treeItem中 + menu->setItemWidget(fixed_frame, 1, fixed_frame_text); // 将container 放到 Item ,数字为列数 + + QTreeWidgetItem *bg_color = new QTreeWidgetItem(global, QStringList("背景颜色 ")); + QLineEdit* bg_color_text = new QLineEdit(); + bg_color_text->setText("50;50;50"); + // bg_color_text->setFixedWidth(130); + // bg_color_text->setStyleSheet("border-width:0;border-style:outset"); + connect(bg_color_text,SIGNAL(textChanged(const QString&)),this,SLOT(setBackgroundColor(const QString&))); + menu->setItemWidget(bg_color, 1, bg_color_text); // 将container 放到 Item ,数字为列数 + + // global->addChild(fixed_frame); + // global->setTextAlignment(1, 2); // (列数,对齐方式) + // grid - 一层树 + QTreeWidgetItem * grid = new QTreeWidgetItem(menu, QStringList("栅格")); + // 二层 + QTreeWidgetItem *grid_color = new QTreeWidgetItem(grid, QStringList("颜色")); + QComboBox * grid_color_cbox = new QComboBox(); + for(int i =0 ; i < color_name.size(); ++i){ + QString color_path = color_icon_filename.arg(color_name[i]); + QIcon color_icon(color_path); + grid_color_cbox->addItem(color_icon, color_name[i]); + } + connect(grid_color_cbox, SIGNAL(currentIndexChanged(int)), this, SLOT(GridColorChanged(int))); + menu->setItemWidget(grid_color, 1, grid_color_cbox); + + // grid cell size + QTreeWidgetItem *grid_cell = new QTreeWidgetItem(grid, QStringList("网格大小")); + QSlider* cell_size_slider = new QSlider( Qt::Horizontal ); + cell_size_slider->setMinimum( 1 ); + cell_size_slider->setMaximum( 100 ); + cell_size_slider->setValue(10); + connect( cell_size_slider, SIGNAL( valueChanged( int )), this, SLOT( setCellSize( int ))); + menu->setItemWidget(grid_cell, 1, cell_size_slider); + + // grid Line Thickness + QTreeWidgetItem *grid_thickness = new QTreeWidgetItem(grid, QStringList("线条粗细")); + QSlider* thickness_slider = new QSlider( Qt::Horizontal ); + thickness_slider->setMinimum( 1 ); + thickness_slider->setMaximum( 100 ); + thickness_slider->setValue(5); + connect( thickness_slider, SIGNAL( valueChanged( int )), this, SLOT( setThickness( int ))); + + menu->setItemWidget(grid_thickness, 1, thickness_slider); + + // laser - 一层树 + QTreeWidgetItem * laser = new QTreeWidgetItem(menu, QStringList("雷达")); + // 二层 + QTreeWidgetItem *laser_topic = new QTreeWidgetItem(laser, QStringList("话题")); + // Topic_Laser 输入雷达 + QLineEdit* laser_topic_text = new QLineEdit(); + + // 方便寻找,登陆后解锁 + laser_topic_text->setObjectName("laser_topic"); + laser_topic_text->setParent(this); + laser_topic_text->setEnabled(false); + // 增加雷达话题 + connect(laser_topic_text, SIGNAL(textChanged(const QString &)), this, SLOT(setLaserTopic(const QString &))); + menu->setItemWidget(laser_topic, 1, laser_topic_text); + + // Laser size + QTreeWidgetItem *laser_size = new QTreeWidgetItem(laser, QStringList("大小(m)")); + QLineEdit* laser_size_text=new QLineEdit(); + laser_size_text->setText("0.015"); + connect(laser_size_text,SIGNAL(textChanged(const QString &)),this,SLOT(setLaserSize(const QString &))); + menu->setItemWidget(laser_size, 1, laser_size_text); + + // Laser Alpha + QTreeWidgetItem *laser_alpha = new QTreeWidgetItem(laser, QStringList("透明度")); + QLineEdit* laser_alpha_text=new QLineEdit(); + laser_alpha_text->setText("1"); + connect(laser_alpha_text,SIGNAL(textChanged(const QString &)),this,SLOT(setLaserAlpha(const QString &))); + menu->setItemWidget(laser_alpha, 1, laser_alpha_text); + + // Laser color + QTreeWidgetItem *laser_color = new QTreeWidgetItem(laser, QStringList("颜色")); + QComboBox * laser_color_comboBox = new QComboBox(); + for(int i =0 ; i < color_name.size(); ++i){ + QString color_path = color_icon_filename.arg(color_name[i]); + QIcon color_icon(color_path); + laser_color_comboBox->addItem(color_icon, color_name[i]); + } + // 设置初始化雷达颜色为红色 + laser_color_comboBox->setCurrentIndex(1); + // 增加颜色变换 + connect(laser_color_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(LaserColorChanged(int))); + menu->setItemWidget(laser_color, 1, laser_color_comboBox); + + // cloud - 一层树 + QTreeWidgetItem * cloud = new QTreeWidgetItem(menu, QStringList("点云")); + // 二层 + QTreeWidgetItem *cloud_topic = new QTreeWidgetItem(cloud, QStringList("话题")); + // Colud_Topic 输入雷达 + QLineEdit* cloud_topic_text = new QLineEdit(); + cloud_topic_text->setObjectName("cloud_topic"); + cloud_topic_text->setParent(this); + cloud_topic_text->setEnabled(false); + connect(cloud_topic_text, SIGNAL(textChanged(const QString &)), this, SLOT(setCloudTopic(const QString &))); + menu->setItemWidget(cloud_topic, 1, cloud_topic_text); + + // Cloud Size + QTreeWidgetItem *cloud_size = new QTreeWidgetItem(cloud, QStringList("大小(m)")); + QLineEdit* cloud_size_text=new QLineEdit(); + cloud_size_text->setText("0.005"); + connect(cloud_size_text,SIGNAL(textChanged(const QString &)),this,SLOT(setCloudSize(const QString &))); + menu->setItemWidget(cloud_size, 1, cloud_size_text); + + // Cloud Alpha + QTreeWidgetItem *cloud_alpha = new QTreeWidgetItem(cloud, QStringList("透明度")); + QLineEdit* cloud_alpha_text=new QLineEdit(); + cloud_alpha_text->setText("1"); + connect(cloud_alpha_text,SIGNAL(textChanged(const QString &)),this,SLOT(setCloudAlpha(const QString &))); + menu->setItemWidget(cloud_alpha, 1, cloud_alpha_text); + + // cloud color + QTreeWidgetItem *cloud_color = new QTreeWidgetItem(cloud, QStringList("颜色")); + QComboBox * cloud_color_comboBox = new QComboBox(); + for(int i =0 ; i < color_name.size(); ++i){ + QString color_path = color_icon_filename.arg(color_name[i]); + QIcon color_icon(color_path); + cloud_color_comboBox->addItem(color_icon, color_name[i]); + } + // 设置初始化颜色 + cloud_color_comboBox->setCurrentIndex(0); + + connect(cloud_color_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(CloudColorChanged(int))); + menu->setItemWidget(cloud_color, 1, cloud_color_comboBox); + + //controls_layout->addWidget(menu, index, 0 ); + controls_layout->addWidget(menu, index, 0, 1, 2); +} + +void MyViz::ViewTop(){ + viewController->reset(); + viewController->subProp("Yaw")->setValue(0); + viewController->subProp("Pitch")->setValue(1.5698); +} + +void MyViz::ViewLeft(){ + viewController->reset(); + viewController->subProp("Yaw")->setValue(1.570796*3); + viewController->subProp("Pitch")->setValue(0); +} + +void MyViz::ViewRight(){ + viewController->reset(); + // 0.785398是以红色为正的右边45度 + viewController->subProp("Yaw")->setValue(1.570796); + viewController->subProp("Pitch")->setValue(0); +} + +void MyViz::ViewZero(){ + if (viewController) + { + viewController->reset(); + viewController->subProp("Yaw")->setValue(0); + viewController->subProp("Pitch")->setValue(0); + } +} + +void MyViz::quit_login_button_clicked(){ + landed = 0; + // 退出设置登录按钮可用 + QWidget * login_button = this->findChild("login_button"); + if (login_button) { + login_button->setEnabled(true); + }else{ + qDebug() << "login_button组件未找到" ; + } + // 退出设置退出按钮不可用 + QWidget * quit_button = this->findChild("quit_login_button"); + if (quit_button) { + quit_button->setEnabled(false); + }else{ + qDebug() << "quit_login_button组件未找到" ; + } + // 退出设置其他组件不可用 + if(dlg != NULL){ + for (int i=0; i< dlg->setting_name.size(); i++){ + QWidget *foundChild = this->findChild(dlg->setting_name[i]); + if (foundChild) { + // 找到了指定的子组件 在这里可以对子组件进行操作 + foundChild->setEnabled(false); + }else{ + qDebug()<<"第" << i+1 <<"组件未找到" ; + } + } + } +} + +void MyViz::login_button_clicked(){ + dlg->setWindowTitle(tr("登录")); + // dlg->open();// 非模态级别对话框 + if(dlg->exec() == QDialog::Accepted){ + // 登录成功后 + landed = 1; + qDebug() << "login accepted"; + // 登录设置其他组件和退出可用 + QWidget * quit_button = this->findChild("quit_login_button"); + if (quit_button) { + quit_button->setEnabled(true); + }else{ + qDebug() << "quit_login_button组件未找到" ; + } + // 登录设置登录不可用 + QWidget * login_button = this->findChild("login_button"); + if (login_button) { + login_button->setEnabled(false); + }else{ + qDebug()<<"login_button组件未找到" ; + } + }else{ + // 登录失败 + qDebug() << "login refused"; + } +} + +void MyViz::PlayCircles(){ + int argc =0; + char** argv; + ros::init(argc, argv, "basic_shapes"); + ros::NodeHandle n; + ros::Rate r(100); + ros::Publisher marker_pub = n.advertise("visualization_marker", 10); + uint32_t shape = visualization_msgs::Marker::LINE_STRIP; + double radius = 0; // 圆的半径 + + if(ros::ok()) + { + for(auto it = circle_radius.begin(); it != circle_radius.end(); ++it){ + // qDebug() << it->first <<"r_test_playcicle"; + visualization_msgs::Marker marker; + // Set the frame ID and timestamp. See the TF tutorials for information on these. + marker.header.frame_id = cur_fixed_frame.toStdString(); + // marker.header.frame_id = "laser"; + marker.header.stamp = ros::Time::now(); + + // Set the namespace and id for this marker. This serves to create a unique ID + // Any marker sent with the same namespace and id will overwrite the old one + marker.ns = "basic_shapes"; + marker.id = it->second; + // Set the marker type. Initially this is CUBE, and cycles between that and SPHERE, ARROW, and CYLINDER + marker.type = shape; + + // Set the marker action. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) + marker.action = visualization_msgs::Marker::ADD; + + // Set the pose of the marker. This is a full 6DOF pose relative to the frame/time specified in the header + marker.pose.position.x = 0; + marker.pose.position.y = 0; + marker.pose.position.z = 0; + marker.pose.orientation.x = 0.0; + marker.pose.orientation.y = 0.0; + marker.pose.orientation.z = 0.0; + marker.pose.orientation.w = 1.0; + + // Set the scale of the marker -- 1x1x1 here means 1m on a side + // 设置线段的宽度 + marker.scale.x = 0.02; + marker.scale.y = 1.0; + marker.scale.z = 1.0; + + // Set the color -- be sure to set alpha to something non-zero! + marker.color.r = 0.0f; + marker.color.g = 1.0f; + marker.color.b = 0.0f; + marker.color.a = 1.0; + // points + // 绘制同心圆 + // double r_c= std::stod(argv[1]); + int num_points = 360; + + // Create the vertices for the points and lines 画圆 + radius = it->first; + for (uint32_t i = 0; i < num_points; ++i) + { + double angle = 2 * M_PI * i / num_points; + geometry_msgs::Point point; + point.x = radius * cos(angle); + point.y = radius * sin(angle); + point.z = 0.0; + marker.points.push_back(point); + } + marker.lifetime = ros::Duration(); + + // Publish the marker 等待订阅者订阅 + while (marker_pub.getNumSubscribers() < 1) // 测试rviz点击就更新了,全部更新 + { + if (!ros::ok()) + { + std::cout<< "publish return 0" << std::endl; + } + ROS_WARN_ONCE("Please create a subscriber to the marker"); + // sleep(1); + } + + marker_pub.publish(marker); + r.sleep(); + } + } +} + +// 删除圆圈槽函数 +void MyViz::CircleDisplayDeleted(){ + if(circle_radius.empty()){ + // 如果为空,置index为0,也可以不置 + circle_index = 0; + } + QLineEdit * circle_edit = this->findChild("circle_edit"); + if (circle_edit && !circle_radius.empty()) { + QString text = circle_edit->text(); + double r = text.toDouble(); + // 从map中找到半径元素 + auto it = circle_radius.find(r); + // 找到半径 + if( it != circle_radius.end()){ + // 更新界面 + marker_->reset(); + // 先删除再发布 + circle_radius.erase(it); + std::thread ThreadCircleDelete(&MyViz::PlayCircles, this); + ThreadCircleDelete.detach(); + // qDebug() << "删除成功" << it->first ; + } + }else{ + // to do + // qDebug() << "circle_edit组件未找到 或 circle_radius为空" ; + } +} + +// 增加圆圈槽函数 +void MyViz::CircleDisplayAdded(){ + marker_->setValue(true); + QLineEdit * circle_edit = this->findChild("circle_edit"); + if (circle_edit) { + // 获取要删除的半径 + QString text = circle_edit->text(); + double r = text.toDouble(); + if( r == 0 ){ + // qDebug() << "输入半径为0"; + // marker_->setValue(false); + // to do + }else{ + // marker_->setValue(true); + // 判断半径重复 + if(circle_radius.find(r) == circle_radius.end()){ + // 未找到相同的半径,则添加 + circle_radius[r] = circle_index; + circle_index++; + }else{ + qDebug() << "相同元素" << r <<"已添加"; + } + marker_->reset(); + std::thread ThreadCircle(&MyViz::PlayCircles, this); + ThreadCircle.detach();// 执行完成后自动回收资源 + } + }else{ + qDebug() << "circle_edit组件未找到" ; + } +} + +// 参考系变化槽函数 +void MyViz::AxesDisplayChanged(int stage) +{ + // 0未被选中,1被部分选中,2 被选中 + switch (stage) + { + case 0: // 未被选中 + if (axes_display != NULL) + axes_display->setValue(false); + break; + + case 2: // 被选择 + if (axes_display != NULL) + axes_display->setValue(true); + break; + } +} + +void MyViz::slot_move_camera(){ + rviz::Tool* current_tool_= tool_manager_->getDefaultTool(); + tool_manager_->setCurrentTool(current_tool_); + manager_->startUpdate(); +} + + +void MyViz::slot_select(){ + // 获得工具类 + // tool_manager_ = manager_->getToolManager(); + rviz::Tool* current_tool_ = tool_manager_->addTool("rviz/Select"); + tool_manager_->setCurrentTool(current_tool_); + manager_->startUpdate(); +} + +void MyViz::setBackgroundColor(const QString& color_string){ + if(manager_!=NULL){ + const QString t = color_string; + QColor new_color = parseColor(t); + if(new_color.isValid()){ + // rviz源码中修改背景颜色的方式 + render_panel_->setBackgroundColor(qtToOgre(new_color)); + manager_->queueRender(); + } + else + { + ROS_INFO_STREAM("Color invalid !!!"); + } + }else{ + ROS_INFO_STREAM("manager_ failed !!!"); + } +} + + +void MyViz::LaserColorChanged(int index){ + if(laser_!=NULL) laser_->subProp("Color")->setValue(color_name[index]); + +} + +void MyViz::CloudColorChanged(int index){ + if(cloud!=NULL) cloud->subProp("Color")->setValue(color_name[index]); + +} +void MyViz::GridColorChanged(int index){ + if(grid_!=NULL) grid_->subProp("Color")->setValue(color_name[index]); +} + +// Destructor. +MyViz::~MyViz() +{ + delete manager_; +} + +void MyViz::setThickness( int thickness_percent ) +{ + if( grid_ != NULL ) + { + grid_->subProp( "Line Style" )->subProp( "Line Width" )->setValue( thickness_percent / 100.0f ); + } +} + +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){ + Topic = newTopic; + cloud->subProp( "Topic" )->setValue(newTopic); + ROS_INFO_STREAM("cloud topic changed to => "<startUpdate(); +} + +void MyViz::setCloudSize(const QString & size){ + if(cloud!=NULL){ + cloud->subProp("Size (m)")->setValue(size); + } +} + +void MyViz::setCloudAlpha(const QString& cloudAlpha){ + if(cloud!=NULL){ + cloud->subProp("Alpha")->setValue(cloudAlpha); + } +} +void MyViz::setLaserAlpha(const QString& laserAlpha){ + if(laser_!=NULL){ + laser_->subProp("Alpha")->setValue(laserAlpha); + } +} +void MyViz::setLaserTopic(const QString &newTopic){ + if(laser_!=NULL){ + Topic = newTopic; + laser_->subProp( "Topic" )->setValue(newTopic); + ROS_INFO_STREAM("laser topic changed to => "<setEnabled(true); + // laser->subProp( "Color" )->setValue( QColor( Qt::yellow ) ); + // manager_->setFixedFrame("laser"); + manager_->startUpdate(); +} + +void MyViz::setFixedFrame(const QString & fix_frame){ + manager_->setFixedFrame(fix_frame); + cur_fixed_frame = fix_frame; + manager_->startUpdate(); +} + +void MyViz::setLaserSize(const QString& laserSize){ + if(laser_!=NULL){ + laser_->subProp("Size (m)")->setValue(laserSize); + } +} + +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::openFile() +{ + ros::Rate r(10.0); + rosbag::Bag bag,newBag; + std::string pcdFilePathStr = filePath.toStdString(); + int frameEnd; + int frameStart; + bool saveFlag = false; + if(stored) + { + char** argv;// 字符串内容(输入) + int argc=0;// 字符串个数(输入) + countbag = 0; + ros::init(argc, argv, "laser_scan_publisher");// "laser"???????????重新启动一下roscore可以解决 + + ros::NodeHandle n; + scan_pub = n.advertise(Topic.toStdString(), 5);// "topic" + scan_pubpcd = n.advertise(Topic.toStdString(), 5); + // sleep time + + bag.open(pcdFilePathStr, rosbag::bagmode::Read); + + rosbag::View view1(bag, rosbag::TopicQuery("/" + Topic.toStdString())); + view = &view1; + std::cout<size()<begin(); it != view->end(); ++it)// 用于预先存储bag包的信息 + { + auto m = *it; + sensor_msgs::LaserScan::ConstPtr input = m.instantiate(); + if(input==nullptr){break;} + indx.push_back(input); + } + rosbag::View view2(bag, rosbag::TopicQuery("/" + Topic.toStdString())); + view_ = &view2; + if(view2.size()==0) + { + isthreadplaying = false; + playagain = false; + threadout = true; + ispauseclick = false; + stored = false; + QMessageBox::information(this, "warning", "Error Topic!"); + return; + } + if(indx.size()==0){ + for (rosbag::View::iterator it = view_->begin(); it != view_->end(); ++it)// 用于预先存储bag包的信息 + { + auto m = *it; + sensor_msgs::PointCloud2::ConstPtr input = m.instantiate(); + if(input != nullptr){ + indxpcd.push_back(input);} + } + pointcloud2 = true; + } + stored = false; + indxsize = indx.size(); + indxpcdsize = indxpcd.size(); + bag.close(); + } + + if(!pointcloud2){ + while (isthreadplaying)// 用于播放存储的bag包的信息 + { + if(threadout) + { + break; + } + double countbagvalue = countbag; + if(countbag>=indxsize) + { + scan_pub.publish(*indx[indxsize-1]); + end = true;// 停在当前位置 + countbag = 0;// 从头开始 + } + // progressmove = false; + + if(is_rateChanged){ + r = ros::Rate(speed_rate); + r.reset(); + is_rateChanged = false; + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if(is_save){ + if(newBag.isOpen()) + newBag.close(); + std::string saveFilePathStr = SavefileName.toStdString(); + + qDebug() << SavefileName; + if(SavefileName.isEmpty()){ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Save failure, No file is selected"); + msgBox.resize(2000, 1000); + msgBox.exec(); + is_save = false; + continue; + } + std::string NameType = find(saveFilePathStr); + if(NameType == "bag"){ + std::ifstream file(saveFilePathStr.c_str()); + if (!file.good()) { + QFile newFile(QString::fromStdString(saveFilePathStr)); + newBag.open(saveFilePathStr, rosbag::bagmode::Write); + newBag.write("/scan", ros::Time::now(), indx[countbag]); + newBag.close(); + }else{ + // file exist + } + } + // else if(NameType == "pcd"){ + // pcl::PointCloud pcl_cloud; + // // pcl::fromROSMsg(*indx[countbag], pcl_cloud); + // populatePointCloudFromLaserScan(*indx[countbag], pcl_cloud); + // // 保存点云数据到PCD文件 + // pcl::io::savePCDFileASCII(saveFilePathStr, pcl_cloud); + // } + // }else if(NameType == "ply"){ + // pcl::PointCloud pcl_cloud; + // pcl::fromROSMsg(*indx[countbag], pcl_cloud); + + // // 保存点云数据到PLY文件 + // pcl::io::savePLYFileASCII(saveFilePathStr, pcl_cloud); + // }else if(NameType == "bin"){ + // pcl::PointCloud pcl_cloud; + // pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + + // pcl::fromROSMsg(*indx[countbag], *cloud); + // std::ofstream bin_file(saveFilePathStr.c_str(),std::ios::out|std::ios::binary|std::ios::app); + // if(!bin_file.good()) std::cout<<"Couldn't open "<points.size (); ++i) + // { + // bin_file.write(reinterpret_cast(&cloud->points[i].x), sizeof(float)); + // bin_file.write(reinterpret_cast(&cloud->points[i].y), sizeof(float)); + // bin_file.write(reinterpret_cast(&cloud->points[i].z), sizeof(float)); + // } + + // bin_file.close(); + // } + else{ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("file type error"); + msgBox.resize(2000, 1000); + msgBox.exec(); + is_save = false; + } + is_save = false; + r.sleep(); + countbag++; + continue; + } + if(is_saveMul || is_saveMulFinish){ + if(ispauseclick && is_saveMul) + { + is_saveMul = false; + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Pause First Please!"); + msgBox.resize(2000, 1000); + msgBox.exec(); + continue; + } + // QMessageBox::information(this, "tips", "Move Slider to Choose End!"); + if(is_saveMulFinish){ + frameEnd = countbag; + + if(frameStart > frameEnd){ + int tem = frameEnd; + frameEnd = frameStart; + frameStart = tem; + } + std::ifstream file(SaveMulfileName.toStdString().c_str()); + if (!file.good()) { + newBag.open(SaveMulfileName.toStdString(), rosbag::bagmode::Write); + for(int i = frameStart; i < frameEnd; i++){ + newBag.write("/scan", ros::Time::now(), indx[i]); + } + is_saveMulFinish = false; + is_saveMul = false; + saveFlag = false; + newBag.close(); + }else{ + + } + + } + else{ + if(!saveFlag){ + std::string NameType = find(SaveMulfileName.toStdString()); + if(SaveMulfileName.isEmpty()) + { + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Save failure, No file is selected, Please seve again!"); + msgBox.resize(2000, 1000); + msgBox.exec(); + is_saveMul = false; + continue; + }else if(NameType != "bag"){ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("file type error"); + msgBox.resize(2000, 1000); + msgBox.exec(); + is_saveMul = false; + continue; + } + frameStart = countbag; + saveFlag = true; + }else{ + ; + } + } + if(ismoved) + { + double valuedouble = progress->value(); + countbag = round((valuedouble/100)*indxsize); + scan_pub.publish(*indx[countbag]); + progress->setSliderPosition((countbag/indxsize*100)); + ismoved = false; + } + else{ + scan_pub.publish(*indx[countbag]); + progress->setSliderPosition((countbag/indxsize*100)); + r.sleep(); + ischanged = false;} + //Reached the end of the file, but did not stop the multiple frame saving operation + if(countbag == indxsize){ + // newBag.close(); + frameEnd = indxsize; + is_saveMulFinish = true; + + // countbag = 0; + + // ispauseclick = false; + } + + continue;} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if(ismoved)// 手拖动时改变进度条 + { + end = false; + int value = (progress->value()/100)*indxsize; + double valuedouble = progress->value(); + countbag = round((valuedouble/100)*indxsize); + if(countbag >= indxsize)// 直接重新开始for循环 + { + countbag = indxsize-1; + progress->setSliderPosition(100); + ispauseclick = false; + } + ismoved = false; + } + else if(ischanged)// 手戳动时改变进度条 + { + end = false; + int value = (progress->value()/100)*indxsize; + double valuedouble = progress->value(); + countbag = round((valuedouble/100)*indxsize); + if(countbag >= indxsize)// 直接重新开始for循环 + { + countbag = indxsize-1; + progress->setSliderPosition(100); + ispauseclick = false; + } + scan_pub.publish(*indx[countbag]); + countbag++; + ischanged = false; + r.sleep(); + continue; + // ismoved = false; + } + if(!ispauseclick || end) + { + if(is_next){ + if(countbag == indxsize) + countbag = 0; + else + countbag++; + scan_pub.publish(*indx[countbag]); + progress->setSliderPosition(ceil(countbag/indxsize*100)); + r.sleep(); + ischanged = false; + is_next = false; + // continue; + } + if(is_previous){ + if(countbag > 0) + countbag--; + scan_pub.publish(*indx[countbag]); + progress->setSliderPosition(ceil(countbag/indxsize*100)); + r.sleep(); + ischanged = false; + is_previous = false; + // continue; + } + if(countbagsliderPosition()<=100 && countbagvalue/indxsize*100<=100) + { + progress->setSliderPosition(ceil(countbagvalue/indxsize*100)); + } + countbag++; + ischanged = false; + r.sleep(); + } + } + } + else if(pointcloud2){ + while (isthreadplaying){// 用于播放存储的bag包的信息 + if(threadout) + break; + double countbagvalue = countbag; + if(countbag>=indxpcdsize) + { + scan_pubpcd.publish(*indxpcd[indxpcdsize-1]); + end = true;// 停在当前位置 + countbag = 0;// 从头开始 + } + // progressmove = false; + + if(is_rateChanged){ + r = ros::Rate(speed_rate); + r.reset(); + is_rateChanged = false; + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if(is_save){ + if(newBag.isOpen()) + newBag.close(); + std::string saveFilePathStr = SavefileName.toStdString(); + + qDebug() << SavefileName; + if(SavefileName.isEmpty()){ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Save failure, No file is selected"); + msgBox.resize(2000, 1000); + msgBox.exec(); + is_save = false; + continue; + } + std::string NameType = find(saveFilePathStr); + if(NameType == "bag"){ + std::ifstream file(saveFilePathStr.c_str()); + if (!file.good()) { + // std::ofstream outputFile(saveFilePathStr); //create a file + // outputFile.close(); + + QFile newFile(QString::fromStdString(saveFilePathStr)); + + newBag.open(saveFilePathStr, rosbag::bagmode::Write); + newBag.write("/scan", ros::Time::now(), indxpcd[countbag]); + + newBag.close(); + }else{ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Save failure"); + msgBox.resize(2000, 1000); + msgBox.exec(); + continue; + } + }else if(NameType == "pcd"){ + pcl::PointCloud pcl_cloud; + pcl::fromROSMsg(*indxpcd[countbag], pcl_cloud); + + // 保存点云数据到PCD文件 + pcl::io::savePCDFileASCII(saveFilePathStr, pcl_cloud); + }else if (NameType == "ply"){ + pcl::PointCloud pcl_cloud; + pcl::fromROSMsg(*indxpcd[countbag], pcl_cloud); + + // 保存点云数据到PCD文件 + pcl::io::savePLYFileASCII(saveFilePathStr, pcl_cloud); + }else if(NameType == "bin"){ + pcl::PointCloud pcl_cloud; + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + + pcl::fromROSMsg(*indxpcd[countbag], *cloud); + std::ofstream bin_file(saveFilePathStr.c_str(),std::ios::out|std::ios::binary|std::ios::app); + if(!bin_file.good()) std::cout<<"Couldn't open "<points.size (); ++i) + { + bin_file.write(reinterpret_cast(&cloud->points[i].x), sizeof(float)); + bin_file.write(reinterpret_cast(&cloud->points[i].y), sizeof(float)); + bin_file.write(reinterpret_cast(&cloud->points[i].z), sizeof(float)); + } + + bin_file.close(); + }else{ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("file type error"); + msgBox.resize(2000, 1000); + msgBox.exec(); + continue; + } + // std::ifstream file(saveFilePathStr.c_str()); + + + is_save = false; + r.sleep(); + // countbag++; + continue; + } + if(is_saveMul || is_saveMulFinish){ + if(ispauseclick && is_saveMul) + { + is_saveMul = false; + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Pause First Please!"); + msgBox.resize(2000, 1000); + msgBox.exec(); + continue; + } + // QMessageBox::information(this, "tips", "Move Slider to Choose End!"); + if(is_saveMulFinish){ + frameEnd = countbag; + // std::string temFileStr = pcdFilePathStr; + // temFileStr.resize(pcdFilePathStr.length() - 4); + if(frameStart > frameEnd){ + int tem = frameEnd; + frameEnd = frameStart; + frameStart = tem; + } + std::ifstream file(SaveMulfileName.toStdString().c_str()); + if (!file.good()) { + newBag.open(SaveMulfileName.toStdString(), rosbag::bagmode::Write); + + for(int i = frameStart; i < frameEnd; i++) + newBag.write("/scan", ros::Time::now(), indxpcd[i]); + + is_saveMulFinish = false; + is_saveMul = false; + saveFlag = false; + newBag.close(); + }else{ + + } + } + else{ + if(!saveFlag){ + std::string filetype = find(SaveMulfileName.toStdString()); + if(SaveMulfileName.isEmpty()){ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Save failure, No file is selected, Please seve again!"); + msgBox.resize(2000, 1000); + msgBox.exec(); + is_saveMul = false; + continue; + }else if(filetype != "bag"){ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("file type error"); + msgBox.resize(2000, 1000); + msgBox.exec(); + is_saveMul = false; + continue; + } + frameStart = countbag; + saveFlag = true; + }else{ + ; + } + } + if(ismoved){ + double valuedouble = progress->value(); + countbag = round((valuedouble/100)*indxpcdsize); + scan_pubpcd.publish(*indxpcd[countbag]); + progress->setSliderPosition((countbag/indxpcdsize*100)); + ismoved = false; + } + else{ + scan_pubpcd.publish(*indxpcd[countbag]); + progress->setSliderPosition((countbag/indxpcdsize*100)); + r.sleep(); + ischanged = false; + } + //Reached the end of the file, but did not stop the multiple frame saving operation + if(countbag == indxpcdsize){ + // newBag.close(); + frameEnd = indxpcdsize; + is_saveMulFinish = true; + + // countbag = 0; + + // ispauseclick = false; + } + + continue; + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if(ismoved)// 手拖动时改变进度条 + { + end = false; + int value = (progress->value()/100)*indxpcdsize; + double valuedouble = progress->value(); + countbag = round((valuedouble/100)*indxpcdsize); + if(countbag >= indxpcdsize)// 直接重新开始for循环 + { + countbag = indxpcdsize-1; + progress->setSliderPosition(100); + ispauseclick = false; + } + ismoved = false; + } + else if(ischanged)// 手戳动时改变进度条 + { + end = false; + int value = (progress->value()/100)*indxpcdsize; + double valuedouble = progress->value(); + countbag = round((valuedouble/100)*indxpcdsize); + if(countbag >= indxpcdsize)// 直接重新开始for循环 + { + countbag = indxpcdsize-1; + progress->setSliderPosition(100); + ispauseclick = false; + } + scan_pubpcd.publish(*indxpcd[countbag]); + countbag++; + ischanged = false; + r.sleep(); + continue; + // ismoved = false; + } + if(!ispauseclick || end) + { + if(is_next){ + if(countbag == indxpcdsize) + countbag = 0; + else + countbag++; + scan_pubpcd.publish(*indxpcd[countbag]); + progress->setSliderPosition(ceil(countbag/indxpcdsize*100)); + r.sleep(); + ischanged = false; + is_next = false; + // continue; + } + if(is_previous){ + if(countbag > 0) + countbag--; + scan_pubpcd.publish(*indxpcd[countbag]); + progress->setSliderPosition(ceil(countbag/indxpcdsize*100)); + r.sleep(); + ischanged = false; + is_previous = false; + // continue; + } + if(countbagsliderPosition()<=100 && countbagvalue/indxpcdsize*100<=100) + { + progress->setSliderPosition(ceil(countbagvalue/indxpcdsize*100)); + } + countbag++; + ischanged = false; + r.sleep(); + } + } + } +} + +double MyViz::extractDuration(const QByteArray& output) { + 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(); + + return number; +} + + +void MyViz::selectFile(){// ********************************************************************************************************************* + QIcon play_png(button_icon_filename.arg("play")); + //QIcon play_png("/home/laijunwen/show/src/point-cloud-visualization/librviz_tutorial/sources/images/play.png"); + play->setIcon(play_png); + threadout = true; + if(threadout)// 清空播放容器 + { + stored = true; + indx.clear(); + } + progress->setSliderPosition(0); + isthreadplaying = false; + playagain = false; + QString tempath; + tempath = QFileDialog::getOpenFileName(nullptr, "select file", "/home", "bag(*.bag);;所有文件 (*)"); + if(tempath.isEmpty()){ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("No file is selected"); + msgBox.resize(2000, 1000); + // 设置样式表,包括透明度设置 + msgBox.setStyleSheet("QMessageBox {" + "background-color: rgba(255, 255, 255, 0.9);" // 设置背景颜色和透明度 + "border: 2px solid rgba(0, 0, 0, 0.9);" // 设置边框颜色和透明度 + "}" + "QLabel {" + "color: rgba(0, 0, 0, 0.9);" // 设置文本颜色和透明度 + "font-size: 16px;" + "}"); + QTimer selectTimer; + selectTimer.setSingleShot(true); // 设置定时器只触发一次 + selectTimer.start(2000); // 3秒后触发定时器,单位是毫秒 + + //连接定时器的超时信号到关闭消息框的槽函数 + QObject::connect(&selectTimer, &QTimer::timeout, [&]() { + msgBox.done(QMessageBox::Ok); // 关闭消息框 + }); + msgBox.exec(); + }else{ + filePath = tempath; + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("File selected"); + msgBox.resize(2000, 1000); + // 设置样式表,包括透明度设置 + msgBox.setStyleSheet("QMessageBox {" + "background-color: rgba(255, 255, 255, 0.9);" // 设置背景颜色和透明度 + "border: 2px solid rgba(0, 0, 0, 0.9);" // 设置边框颜色和透明度 + "}" + "QLabel {" + "color: rgba(0, 0, 0, 0.9);" // 设置文本颜色和透明度 + "font-size: 16px;" + "}"); + QTimer selectTimer; + selectTimer.setSingleShot(true); // 设置定时器只触发一次 + selectTimer.start(2000); // 3秒后触发定时器,单位是毫秒 + + //连接定时器的超时信号到关闭消息框的槽函数 + QObject::connect(&selectTimer, &QTimer::timeout, [&]() { + msgBox.done(QMessageBox::Ok); // 关闭消息框 + }); + msgBox.exec(); + isthreadplaying = true; + } +} + + +void MyViz::SpeedChanged(int index){ + // if(isthreadplaying) + bool ok; + double number = speed_name[index].toDouble(&ok); + if(ok){ + speed_rate = 10.0 * number; + is_rateChanged = true; + } + else{ + qDebug() << "speed change failed"; + } +} + + +void MyViz::pcdFile()// ********************************************************************************************************************* +{ + char** argv;// 字符串内容(输入) + int argc=0;// 字符串个数(输入) + ros::init (argc, argv, "UandBdetect"); + ros::NodeHandle nh; + ros::Publisher pcl_pub = nh.advertise ("pcd_output", 1); + pcl::PointCloud cloud; + sensor_msgs::PointCloud2 output; + std::string pcdFilePathStr = filePath.toStdString(); + pcl::io::loadPCDFile (pcdFilePathStr, cloud); //修改自己pcd文件所在路径 + //Convert the cloud to ROS message + pcl::toROSMsg(cloud, output); + output.header.frame_id = "world";//this has been done in order to be able to visualize our PointCloud2 message on the RViz visualizer + + ros::Rate loop_rate(1); + + pcl_pub.publish(output); + ros::spinOnce(); + loop_rate.sleep(); +} + + +std::string MyViz::find(const std::string Path) +{ + size_t lastDotPos = Path.find_last_of('.'); + std::string fileExtension; + if (lastDotPos != std::string::npos) + // 提取后缀 + fileExtension = Path.substr(lastDotPos + 1); + return fileExtension; +} + + +//********************************************************************************************************************* +void MyViz::plyFile() +{ + char** argv;// 字符串内容(输入) + int argc=0;// 字符串个数(输入) + ros::init (argc, argv, "UandBdetect"); + ros::NodeHandle nh; + ros::Publisher pcl_pub = nh.advertise ("ply_output", 1); + pcl::PointCloud cloud; + sensor_msgs::PointCloud2 output; + std::string pcdFilePathStr = filePath.toStdString(); + pcl::io::loadPLYFile (pcdFilePathStr, cloud); //修改自己pcd文件所在路径 + //Convert the cloud to ROS message + pcl::toROSMsg(cloud, output); + output.header.frame_id = "world";//this has been done in order to be able to visualize our PointCloud2 message on the RViz visualizer + //!!!这一步需要注意,是后面rviz的 fixed_frame !!!敲黑板,画重点。 + ros::Rate loop_rate(1); + // while (ros::ok()) + // { + pcl_pub.publish(output); + ros::spinOnce(); + loop_rate.sleep(); + // } +} + +void MyViz::bin() +{ + std::string binFilePathStr = filePath.toStdString(); + std::ifstream inFile(binFilePathStr, std::ios::binary); + if (!inFile.is_open()) + { + std::cerr << "Failed to open input binary file." << std::endl; + + } + pcl::PointCloud::Ptr cloud1(new pcl::PointCloud); + while (!inFile.eof()) + { + pcl::PointXYZ point; + inFile.read(reinterpret_cast(&point.x), sizeof(float)); + inFile.read(reinterpret_cast(&point.y), sizeof(float)); + inFile.read(reinterpret_cast(&point.z), sizeof(float)); + + if (inFile.eof()) + break; + + cloud1->push_back(point); + } + + inFile.close(); + binFilePathStr = binFilePathStr.substr(0,binFilePathStr.size()-4); + char** argv;// 字符串内容(输入) + int argc=0;// 字符串个数(输入) + ros::init (argc, argv, "UandBdetect"); + ros::NodeHandle nh; + ros::Publisher pcl_pub = nh.advertise ("bin_output", 1); + pcl::PointCloud cloud; + sensor_msgs::PointCloud2 output; + binFilePathStr = binFilePathStr+".pcd"; + pcl::toROSMsg(*cloud1, output); + output.header.frame_id = "world";//this has been done in order to be able to visualize our PointCloud2 message on the RViz visualizer + ros::Rate loop_rate(1); + pcl_pub.publish(output); + ros::spinOnce(); + loop_rate.sleep(); + +} + +void MyViz::pcd2bin (std::string &in_file, std::string& out_file){ + //Create a PointCloud value + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + + //Open the PCD file + if (pcl::io::loadPCDFile (in_file, *cloud) == -1) + { + PCL_ERROR ("Couldn't read in_file\n"); + } + //Create & write .bin file + std::ofstream bin_file(out_file.c_str(),std::ios::out|std::ios::binary|std::ios::app); + if(!bin_file.good()) std::cout<<"Couldn't open "<points.size (); ++i) + { + bin_file.write(reinterpret_cast(&cloud->points[i].x), sizeof(float)); + bin_file.write(reinterpret_cast(&cloud->points[i].y), sizeof(float)); + bin_file.write(reinterpret_cast(&cloud->points[i].z), sizeof(float)); + } + + bin_file.close(); +} + +void MyViz::pcd2ply(std::string &in_file, std::string& out_file){ + pcl::PointCloud cloud; + + pcl::io::loadPCDFile (in_file, cloud); //修改自己pcd文件所在路径 + // pcl::PLYWriter writer; + // writer.write(out_file, cloud); + + pcl::io::savePLYFileASCII(out_file, cloud); +} + +void MyViz::ply2pcd(std::string &in_file, std::string& out_file){ + pcl::PointCloud cloud; + + pcl::io::loadPLYFile (in_file, cloud); //修改自己ply文件所在路径 + + pcl::io::savePCDFileASCII(out_file, cloud); +} + +void MyViz::ply2bin(std::string &in_file, std::string& out_file){ + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + if (pcl::io::loadPLYFile (in_file, *cloud) == -1) + { + PCL_ERROR ("Couldn't read in_file\n"); + } + //Create & write .bin file + std::ofstream bin_file(out_file.c_str(),std::ios::out|std::ios::binary|std::ios::app); + if(!bin_file.good()) std::cout<<"Couldn't open "<points.size (); ++i) + { + bin_file.write(reinterpret_cast(&cloud->points[i].x), sizeof(float)); + bin_file.write(reinterpret_cast(&cloud->points[i].y), sizeof(float)); + bin_file.write(reinterpret_cast(&cloud->points[i].z), sizeof(float)); + } + + bin_file.close(); +} + +void MyViz::bin2pcd(std::string &in_file, std::string& out_file){ + std::fstream input(in_file.c_str(), std::ios::in | std::ios::binary); + if(!input.good()){ + std::cerr << "Couldn't read in_file: " << in_file << endl; + } + + pcl::PointCloud::Ptr points (new pcl::PointCloud); + + int i; + for (i=0; input.good() && !input.eof(); i++) { + pcl::PointXYZI point; + input.read((char *) &point.x, 3*sizeof(float)); + input.read((char *) &point.intensity, sizeof(float)); + points->push_back(point); + } + input.close(); + + pcl::io::savePCDFileASCII(out_file, *points); +} + +void MyViz::bin2ply(std::string &in_file, std::string& out_file){ + std::fstream input(in_file.c_str(), std::ios::in | std::ios::binary); + if(!input.good()){ + std::cerr << "Couldn't read in_file: " << in_file << endl; + } + + pcl::PointCloud::Ptr points (new pcl::PointCloud); + + int i; + for (i=0; input.good() && !input.eof(); i++) { + pcl::PointXYZI point; + input.read((char *) &point.x, 3*sizeof(float)); + input.read((char *) &point.intensity, sizeof(float)); + points->push_back(point); + } + input.close(); + + pcl::io::savePLYFileASCII(out_file, *points); + // pcl::PLYWriter writer; + // writer.write(out_file, *points); +} + +void MyViz::TransformeType(){ + if(filePath.isEmpty()){ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Not select File"); + msgBox.resize(2000, 1000); + msgBox.exec(); + return; + } + std::string na = find(filePath.toStdString()); + if(na == "bag") + { + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("bag file can't transform from here!!"); + msgBox.resize(2000, 1000); + msgBox.exec(); + return; + } + QString SavefileName = QFileDialog::getSaveFileName(nullptr, "Save File", "/home","files(*)"); + qDebug() << SavefileName; + if(SavefileName.isEmpty()){ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("Not select File"); + msgBox.resize(2000, 1000); + msgBox.exec(); + return; + } + + std::string SaveFileNameStr = SavefileName.toStdString(); + std::string aftertypeName = find(SaveFileNameStr); + std::string beforetypeName = find(filePath.toStdString()); + + if(beforetypeName == "pcd"){ + if(aftertypeName == "pcd"){ + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + //Open the PCD file + if (pcl::io::loadPCDFile (filePath.toStdString(), *cloud) == -1) + PCL_ERROR ("Couldn't read in_file\n"); + if(pcl::io::savePCDFileASCII(SaveFileNameStr, *cloud) != -1) + std::cout<<" --- save " << SaveFileNameStr << std::endl; + }else if(aftertypeName == "ply"){ + std::string temstr = filePath.toStdString(); + pcd2ply(temstr, SaveFileNameStr); + }else if(aftertypeName == "bin"){ + std::string temstr = filePath.toStdString(); + pcd2bin(temstr, SaveFileNameStr); + } + + }else if(beforetypeName == "ply"){ + if(aftertypeName == "pcd"){ + std::string temstr = filePath.toStdString(); + ply2pcd(temstr, SaveFileNameStr); + } + else if(aftertypeName == "ply"){ + pcl::PointCloud cloud; + + std::string plyFilePathStr = filePath.toStdString(); + pcl::io::loadPLYFile (plyFilePathStr, cloud); //修改自己ply文件所在路径 + // pcl::PLYWriter writer; + // writer.write(SaveFileNameStr, cloud); + if(pcl::io::savePLYFileASCII(SaveFileNameStr, cloud) != -1) + std::cout<<" --- save " << SaveFileNameStr << std::endl; + }else if(aftertypeName == "bin"){ + std::string temstr = filePath.toStdString(); + ply2bin(temstr, SaveFileNameStr); + } + }else if(beforetypeName == "bin"){ + if(aftertypeName == "pcd"){ + std::string temstr = filePath.toStdString(); + bin2pcd(temstr, SaveFileNameStr); + } + else if(aftertypeName == "ply"){ + std::string temstr = filePath.toStdString(); + bin2ply(temstr, SaveFileNameStr); + } + // else if(aftertypeName == "bin"){ + // std::string binFilePathStr = filePath.toStdString(); + // std::ifstream inFile(binFilePathStr, std::ios::binary); + // if (!inFile.is_open()) + // std::cerr << "Failed to open input binary file." << std::endl; + // pcl::PointCloud::Ptr cloud(new pcl::PointCloud); + // while (!inFile.eof()){ + // pcl::PointXYZ point; + // inFile.read(reinterpret_cast(&point.x), sizeof(float)); + // inFile.read(reinterpret_cast(&point.y), sizeof(float)); + // inFile.read(reinterpret_cast(&point.z), sizeof(float)); + + // if (inFile.eof()) + // break; + // cloud->push_back(point); + // } + // inFile.close(); + // std:: ofstream bin_file(filePath.toStdString().c_str(),std::ios::out|std::ios::binary|std::ios::app); + // if(!bin_file.good()) std::cout<<"Couldn't open "<points.size (); ++i){ + // bin_file.write((char*)&cloud->points[i].x,3*sizeof(float)); + // bin_file.write((char*)&cloud->points[i].intensity,sizeof(float)); + // } + // bin_file.close(); + // } + }else{ + QMessageBox msgBox; + msgBox.setWindowTitle("Tips"); + msgBox.setText("File type error"); + msgBox.resize(2000, 1000); + msgBox.exec(); + } +} + diff --git a/librviz_tutorial/src/myviz.h b/point_visual/src/myviz.h similarity index 66% rename from librviz_tutorial/src/myviz.h rename to point_visual/src/myviz.h index 9de4261..9160e0b 100644 --- a/librviz_tutorial/src/myviz.h +++ b/point_visual/src/myviz.h @@ -32,20 +32,22 @@ #include -#include +#include #include -#include -#include"std_msgs/String.h" -#include +#include +#include "std_msgs/String.h" +#include #include -#include +#include #include #include #include -#include +#include #include #include +#include + #include #include"loginDialog.h" @@ -56,8 +58,8 @@ class RenderPanel; class VisualizationManager; class ToolManager; class PropertyTreeWidget; -class ToolManager; -class PropertyTreeWidget; +class ViewManager; +class ViewController; } // BEGIN_TUTORIAL @@ -70,64 +72,93 @@ public: virtual ~MyViz(); void subCallback(const std_msgs::String& msg); void pubThread(); - void PlayCircle(double index); // 圆圈线程执行函数 + void PlayCircles(); // 圆圈线程执行函数 rosbag::View *view; + rosbag::View *view_; + private Q_SLOTS: // QT信号和槽 void setThickness( int thickness_percent ); void setCellSize( int cell_size_percent ); void setCloudTopic(const QString &newTopic); - void setLaserTopic(const QString &newTopic); - void setCloudSize(int cloudsize); - void setLaserSize(int lasersize); + void setCloudSize(const QString& size); + void setCloudAlpha(const QString& text); + void setLaserAlpha(const QString& laserAlpha); + void setLaserTopic(const QString &newTopic); + void setLaserSize(const QString & laserSize); void LaserColorChanged(int index); // 雷达颜色 void GridColorChanged(int index); // 格栅颜色 + void CloudColorChanged(int index); void setFixedFrame(const QString &FixedFrame); void setBackgroundColor(const QString& bg_color); // 背景颜色 void AxesDisplayChanged(int index); // axe - void CircleDisplayChanged(const QString& text); // 圆圈变化 void Tree_Display(QGridLayout* layout, int index); void login_button_clicked(); void quit_login_button_clicked(); void slot_select(); // 切换至select模式 void slot_move_camera(); + void CircleDisplayAdded(); // 增加圆圈按钮 + void CircleDisplayDeleted(); // 删除圆圈按钮 + void ViewZero(); // 视角回正 + void ViewLeft(); // 视角左转 + void ViewRight(); // 视角右转 + void ViewTop(); // 视角俯视 // off double extractDuration(const QByteArray& output); void selectFile(); void SpeedChanged(int index); void openFile(); + void pcdFile(); + void plyFile(); + void bin(); + std::string find(const std::string Path); + + void pcd2ply (std::string &in_file, std::string& out_file); + void pcd2bin (std::string &in_file, std::string& out_file); + void ply2pcd (std::string &in_file, std::string& out_file); + void ply2bin (std::string &in_file, std::string& out_file); + void bin2pcd (std::string &in_file, std::string& out_file); + void bin2ply (std::string &in_file, std::string& out_file); + void TransformeType(); + private: rviz::VisualizationManager* manager_; // rviz::RenderPanel* render_panel_; rviz::ToolManager* tool_manager_; + rviz::ViewManager* view_man_; // 视角总类 + rviz::ViewController* viewController; // 视角控制类 rviz::Display* grid_; rviz::Display* cloud; rviz::Display* laser_; rviz::Display* axes_display; rviz::Display* marker_; - rviz::PropertyTreeWidget* tree_widget_ ; + rviz::PropertyTreeWidget* select_tree_widget_ ; // select框 + + // rviz::PropertyTreeWidget* view_tree_widget_ ; ros::NodeHandle nh; ros::Subscriber sub; ros::Publisher pub; std::thread pub_thread; std::vector color_name; // 颜色的名称集合 + // std::vector button_name; // 带图标按钮的名称集合 QString color_icon_filename; + QString button_icon_filename; LoginDialog* dlg; bool landed; QString cur_fixed_frame; + int circle_index; + std::map circle_radius; // off - bool isclicked;// 用于判断play第几次按下play键 bool ispauseclick;// 用于判断暂停键第几次按下 int count;// 用于记录timeout的次数 bool sliderreleased;// 用于判断是否拖动了进度条 bool playagain;// 用于检测是否多次按下play按钮 bool isthreadplaying;// 用于标识线程是否在运行 - bool abs;// 用于单独开线程? - bool stored;// 用于存储一次容器,只在第一次play的时候存储容器 + bool stored = false;// 用于存储一次容器,只在第一次play的时候存储容器 bool is_pause = false; bool is_reset = false; bool is_end = false; @@ -142,17 +173,30 @@ private: bool threadout = false; bool ischanged = false; bool ismoved = false; - double speed_rate; + + bool sliderpause = false; + bool is_next = false; + bool is_previous = false; + bool flag = false; + bool pointcloud2 = false; + double speed_rate = 10; + QPushButton* play; QSlider* progress; QString filePath; - double duration; - int remain; + QString Topic; + QString SavefileName; + QString SaveMulfileName; + + double sliderPosition = 0; int prog_num = 0; std::vector speed_name; ros::Publisher scan_pub; - std::vector indx;// 存储节点信息 - int countbag;// 存储播放的具体位置 - double indxsize; + ros::Publisher scan_pubpcd; + std::vector indx;// 存储laser节点信息 + std::vector indxpcd;// 存储cloud节点信息 + int countbag = 0;// 存储播放的具体位置 + double indxsize = 0; + double indxpcdsize = 0; int test = 0; diff --git a/librviz_tutorial/src/node.cpp b/point_visual/src/node.cpp similarity index 100% rename from librviz_tutorial/src/node.cpp rename to point_visual/src/node.cpp diff --git a/librviz_tutorial/tools/color.cpp b/point_visual/tools/color.cpp similarity index 100% rename from librviz_tutorial/tools/color.cpp rename to point_visual/tools/color.cpp diff --git a/librviz_tutorial/tools/color.h b/point_visual/tools/color.h similarity index 100% rename from librviz_tutorial/tools/color.h rename to point_visual/tools/color.h