VS+QT+PCL点云显示转网格表面体窗体实现

 程序示例精选

VS+QT+PCL点云显示转网格表面体窗体实现

如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助!

前言

这篇博客针对<<VS+QT+PCL点云显示转网格表面体窗体实现>>编写代码,代码整洁,规则,易读。 学习与应用推荐首选。


文章目录

一、所需工具软件

二、使用步骤

        1. 引入库

        2. 代码实现

        3. 运行结果

三、在线协助

一、所需工具软件

1. VS, Qt

2. PCL

二、使用步骤

1.引入库

#ifndef CLOUDVIEWER_H
#define CLOUDVIEWER_H
#include "MyCloud.h"
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/io/vtk_io.h>
#include <pcl/io/obj_io.h>
#include <pcl/io/vtk_lib_io.h>  // loadPolygonFileOBJ#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/common/common.h>
#include <pcl/ModelCoefficients.h>
#include <pcl/PolygonMesh.h>#include <QtWidgets/QMainWindow>
#include "GBK.h"
#include "ui_CloudViewer.h"
#include "AboutWin.h"
#include "Tools.h"
#include "MeshProcessing.h"
#include "FileIO.h"#include <vector>
#include <map>
#include <algorithm>
#include <QtWidgets/QMainWindow>
#include <QString>
#include <QDebug>
#include <QLabel>
#include <QMessageBox>
#include <QAction>
#include <QMenu>
#include <QMenuBar>
#include <QToolBar>
#include <QStatusBar>
#include <QFileDialog>
#include <QColorDialog>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include "QVTKWidget.h"
#include <vtkRenderWindow.h>

2. 代码实现

代码如下:

#include "CloudViewer.h"CloudViewer::CloudViewer(QWidget* parent): QMainWindow(parent) {ui.setupUi(this);/***** Slots connection of QMenuBar and QToolBar *****/// File (connect)QObject::connect(ui.openAction, &QAction::triggered, this, &CloudViewer::open);QObject::connect(ui.addAction, &QAction::triggered, this, &CloudViewer::add);QObject::connect(ui.clearAction, &QAction::triggered, this, &CloudViewer::clear);ui.saveAction->setData(QVariant(false));       // isSaveBinary = falseui.saveBinaryAction->setData(QVariant(true));  // isSaveBinary = trueconnect(ui.saveAction, SIGNAL(triggered()), this, SLOT(save()));connect(ui.saveBinaryAction, SIGNAL(triggered()), this, SLOT(save()));QObject::connect(ui.exitAction, &QAction::triggered, this, &CloudViewer::exit);// Display (connect)QObject::connect(ui.pointcolorAction, &QAction::triggered, this, &CloudViewer::pointcolorChanged);QObject::connect(ui.bgcolorAction, &QAction::triggered, this, &CloudViewer::bgcolorChanged);QObject::connect(ui.mainviewAction, &QAction::triggered, this, &CloudViewer::mainview);QObject::connect(ui.leftviewAction, &QAction::triggered, this, &CloudViewer::leftview);QObject::connect(ui.topviewAction, &QAction::triggered, this, &CloudViewer::topview);// Generate (connect)QObject::connect(ui.cubeAction, &QAction::triggered, this, &CloudViewer::cube);QObject::connect(ui.sphereAction, &QAction::triggered, this, &CloudViewer::createSphere);QObject::connect(ui.cylinderAction, &QAction::triggered, this, &CloudViewer::createCylinder);// Process (connect)QObject::connect(ui.meshsurfaceAction, &QAction::triggered, this, &CloudViewer::convertSurface);QObject::connect(ui.wireframeAction, &QAction::triggered, this, &CloudViewer::convertWireframe);// Option (connect)ui.windowsThemeAction->setData(QVariant(CLOUDVIEWER_THEME_WINDOWS));ui.darculaThemeAction->setData(QVariant(CLOUDVIEWER_THEME_DARCULA));ui.englishAction->setData(QVariant(CLOUDVIEWER_LANG_ENGLISH));ui.chineseAction->setData(QVariant(CLOUDVIEWER_LANG_CHINESE));connect(ui.windowsThemeAction, SIGNAL(triggered()), this, SLOT(changeTheme()));connect(ui.darculaThemeAction, SIGNAL(triggered()), this, SLOT(changeTheme()));connect(ui.englishAction, SIGNAL(triggered()), this, SLOT(changeLanguage()));connect(ui.chineseAction, SIGNAL(triggered()), this, SLOT(changeLanguage()));// About (connect)QObject::connect(ui.aboutAction, &QAction::triggered, this, &CloudViewer::about);QObject::connect(ui.helpAction, &QAction::triggered, this, &CloudViewer::help);/***** Slots connection of RGB widget *****/// Random color (connect)connect(ui.colorBtn, SIGNAL(clicked()), this, SLOT(colorBtnPressed()));// Connection between RGB slider and RGB value (connect)connect(ui.rSlider, SIGNAL(valueChanged(int)), this, SLOT(rSliderChanged(int)));connect(ui.gSlider, SIGNAL(valueChanged(int)), this, SLOT(gSliderChanged(int)));connect(ui.bSlider, SIGNAL(valueChanged(int)), this, SLOT(bSliderChanged(int)));// RGB slider released (connect)connect(ui.rSlider, SIGNAL(sliderReleased()), this, SLOT(RGBsliderReleased()));connect(ui.gSlider, SIGNAL(sliderReleased()), this, SLOT(RGBsliderReleased()));connect(ui.bSlider, SIGNAL(sliderReleased()), this, SLOT(RGBsliderReleased()));// Change size of cloud (connect)//connect(ui.pSlider, SIGNAL(valueChanged(int)), ui.sizeLCD, SLOT(display(int)));connect(ui.pSlider, SIGNAL(valueChanged(int)), this, SLOT(pSliderChanged(int)));}void CloudViewer::test22() 
{std::cout << "test_ " << std::endl;}CloudViewer::~CloudViewer() {}void CloudViewer::doOpen(const QStringList& filePathList) {// Open point cloud file one by onefor (int i = 0; i != filePathList.size(); i++) {timeStart(); // time startmycloud.cloud.reset(new PointCloudT); // Reset cloudQFileInfo fileInfo(filePathList[i]);std::string filePath = fromQString(fileInfo.filePath());std::string fileName = fromQString(fileInfo.fileName());// begin loadingui.statusBar->showMessage(fileInfo.fileName() + ": " + QString::number(i) + "/" + QString::number(filePathList.size())+ " point cloud loading...");mycloud = fileIO.load(fileInfo);if (!mycloud.isValid) {// TODO: deal with the error, print error info in console?debug("invalid cloud.");continue;}mycloud.viewer = viewer;mycloud_vec.push_back(mycloud);timeCostSecond = timeOff(); // time offconsoleLog("Open",toQString(mycloud.fileName),toQString(mycloud.filePath),"Time cost: " + timeCostSecond + " s, Points: " + QString::number(mycloud.cloud->points.size()));// update tree widgetQTreeWidgetItem* cloudName = new QTreeWidgetItem(QStringList()<< toQString(mycloud.fileName));cloudName->setIcon(0, QIcon(":/Resources/images/icon.png"));ui.dataTree->addTopLevelItem(cloudName);total_points += mycloud.cloud->points.size();}ui.statusBar->showMessage("");showPointcloudAdd();setPropertyTable();
}// Open point cloud
void CloudViewer::open() {std::cout << "test_ " << std::endl;QStringList filePathList = QFileDialog::getOpenFileNames(this,tr("Open point cloud file"),toQString(mycloud.fileDir),toQString(fileIO.getInputFormatsStr()));if (filePathList.isEmpty()) return;// Clear cache// TODO: abstract a functionmycloud_vec.clear();total_points = 0;ui.dataTree->clear();viewer->removeAllPointClouds();doOpen(filePathList);
}// Add Point Cloud
void CloudViewer::add() {QStringList filePathList = QFileDialog::getOpenFileNames(this,tr("Add point cloud file"),toQString(mycloud.fileDir),toQString(fileIO.getInputFormatsStr()));if (filePathList.isEmpty()) return;doOpen(filePathList);
}// Clear all point clouds
void CloudViewer::clear() {mycloud_vec.clear();  // 从点云容器中移除所有点云viewer->removeAllPointClouds();  // 从viewer中移除所有点云viewer->removeAllShapes(); // 这个remove更彻底ui.dataTree->clear();  // 将dataTree清空ui.propertyTable->clear();  // 清空属性窗口propertyTableQStringList header;header << "Property" << "Value";ui.propertyTable->setHorizontalHeaderLabels(header);// 输出窗口consoleLog("Clear", "All point clouds", "", "");setWindowTitle("CloudViewer");  // 更新窗口标题showPointcloud();  // 更新显示
}// Save point cloud
void CloudViewer::save() {if (!mycloud.isValid) {QMessageBox::critical(this, tr("Saving file error"),tr("There is no point cloud to save"));return;}// get binary flag from sender()QAction* action = qobject_cast<QAction*>(sender());QVariant v = action->data();bool isSaveBinary = (bool)v.value<bool>();QString selectedFilter = toQString(fileIO.outputFiltersMap.at(mycloud.fileSuffix));QString saveFilePath = QFileDialog::getSaveFileName(this,                                    // parenttoQString("Save point cloud" + string(isSaveBinary ? " (binary)" : "")), // captiontoQString(mycloud.filePath),             // dirtoQString(fileIO.getOutputFormatsStr()), // filter&selectedFilter                          // selected filter);if (saveFilePath.isEmpty()) return;QFileInfo fileInfo(saveFilePath);QString saveFileName = fileInfo.fileName();string saveFilePathStd = fromQString(saveFilePath);string saveFileNameStd = fromQString(saveFileName);if (mycloud_vec.size() > 1) {savemulti(fileInfo, isSaveBinary);return;}bool saveStatus = fileIO.save(mycloud, fileInfo, isSaveBinary);if (!saveStatus) {QMessageBox::critical(this, tr("Saving file error"),tr("We can not save the file"));return;}consoleLog("Save", saveFileName, saveFilePath, "Single save");setWindowTitle(saveFilePath + " - CloudViewer");QMessageBox::information(this, tr("save point cloud file"),toQString("Save " + saveFileNameStd + " successfully!"));
}// Save multi point cloud
void CloudViewer::savemulti(const QFileInfo& fileInfo, bool isSaveBinary) {string subname = fromQString(fileInfo.fileName());QString saveFilePath = fileInfo.filePath();PointCloudT::Ptr multi_cloud;multi_cloud.reset(new PointCloudT);multi_cloud->height = 1;int sum = 0;for (auto c : mycloud_vec) {sum += c.cloud->points.size();}multi_cloud->width = sum;multi_cloud->resize(multi_cloud->height * multi_cloud->width);int k = 0;for (int i = 0; i != mycloud_vec.size(); ++i) {// 注意cloudvec[i]->points.size()和cloudvec[i]->size()的区别for (int j = 0; j != mycloud_vec[i].cloud->points.size(); ++j) {multi_cloud->points[k].x = mycloud_vec[i].cloud->points[j].x;multi_cloud->points[k].y = mycloud_vec[i].cloud->points[j].y;multi_cloud->points[k].z = mycloud_vec[i].cloud->points[j].z;multi_cloud->points[k].r = mycloud_vec[i].cloud->points[j].r;multi_cloud->points[k].g = mycloud_vec[i].cloud->points[j].g;multi_cloud->points[k].b = mycloud_vec[i].cloud->points[j].b;k++;}}MyCloud multiMyCloud;multiMyCloud.cloud = multi_cloud;multiMyCloud.isValid = true;// save multi_cloudbool saveStatus = fileIO.save(multiMyCloud, fileInfo, isSaveBinary);if (!saveStatus) {QMessageBox::critical(this, tr("Saving file error"),tr("We can not save the file"));return;}if (isSaveBinary) {consoleLog("Save as binary", QString::fromLocal8Bit(subname.c_str()), saveFilePath, "Multi save (binary)");}else {consoleLog("Save", QString::fromLocal8Bit(subname.c_str()), saveFilePath, "Multi save");}// 将保存后的 multi_cloud 设置为当前 mycloud,以便保存之后直接进行操作mycloud.cloud = multi_cloud;mycloud.filePath = fromQString(saveFilePath);mycloud.fileName = subname;setWindowTitle(saveFilePath + " - CloudViewer");QMessageBox::information(this, tr("save point cloud file"), toQString("Save " + subname + " successfully!"));
}// 退出程序
void CloudViewer::exit() {this->close();
}// Generate cube
void CloudViewer::cube() {mycloud.cloud.reset(new PointCloudT);total_points = 0;ui.dataTree->clear();  // 清空资源管理器的itemviewer->removeAllPointClouds();  // 从viewer中移除所有点云mycloud_vec.clear();  // 清空点云容器mycloud.cloud->width = 50000;         // 设置点云宽mycloud.cloud->height = 1;            // 设置点云高,高为1,说明为无组织点云mycloud.cloud->is_dense = false;mycloud.cloud->resize(mycloud.cloud->width * mycloud.cloud->height);     // 重置点云大小for (size_t i = 0; i != mycloud.cloud->size(); ++i){mycloud.cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);mycloud.cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);mycloud.cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);mycloud.cloud->points[i].r = red;mycloud.cloud->points[i].g = green;mycloud.cloud->points[i].b = blue;}// 设置资源管理器QTreeWidgetItem* cloudName = new QTreeWidgetItem(QStringList() << QString::fromLocal8Bit("cube"));cloudName->setIcon(0, QIcon(":/Resources/images/icon.png"));ui.dataTree->addTopLevelItem(cloudName);// 输出窗口consoleLog("Generate cube", "cube", "cube", "");mycloud_vec.push_back(mycloud);showPointcloudAdd();
}// 初始化
void CloudViewer::initial() {// 界面初始化setWindowIcon(QIcon(tr(":/Resources/images/icon.png")));setWindowTitle(tr("CloudViewer"));// 点云初始化mycloud.cloud.reset(new PointCloudT);mycloud.cloud->resize(1);viewer.reset(new pcl::visualization::PCLVisualizer("viewer", false));// viewer->addPointCloud(cloud, "cloud");ui.screen->SetRenderWindow(viewer->getRenderWindow());viewer->setupInteractor(ui.screen->GetInteractor(), ui.screen->GetRenderWindow());ui.screen->update();ui.propertyTable->setSelectionMode(QAbstractItemView::NoSelection); // 禁止点击属性管理器的 itemui.consoleTable->setSelectionMode(QAbstractItemView::NoSelection);  // 禁止点击输出窗口的 itemui.dataTree->setSelectionMode(QAbstractItemView::ExtendedSelection); // 允许 dataTree 进行多选// 设置默认主题QString qss = darcula_qss;qApp->setStyleSheet(qss);setPropertyTable();setConsoleTable();// 输出窗口consoleLog("Software start", "CloudViewer", "Welcome to use CloudViewer", "Nightn");// 设置背景颜色为 darkviewer->setBackgroundColor(30 / 255.0, 30 / 255.0, 30 / 255.0);}// 显示点云,不重置相机角度
void CloudViewer::showPointcloud() {for (int i = 0; i != mycloud_vec.size(); i++){viewer->updatePointCloud(mycloud_vec[i].cloud, mycloud_vec[i].cloudId);}// viewer->resetCamera();ui.screen->update();
}// 添加点云到viewer,并显示点云
void CloudViewer::showPointcloudAdd() {for (int i = 0; i != mycloud_vec.size(); i++) {viewer->addPointCloud(mycloud_vec[i].cloud, mycloud_vec[i].cloudId);viewer->updatePointCloud(mycloud_vec[i].cloud, mycloud_vec[i].cloudId);}viewer->resetCamera();ui.screen->update();
}void CloudViewer::setCloudColor(unsigned int r, unsigned int g, unsigned int b) {// Set the new colorfor (size_t i = 0; i < mycloud.cloud->size(); i++) {mycloud.cloud->points[i].r = r;mycloud.cloud->points[i].g = g;mycloud.cloud->points[i].b = b;mycloud.cloud->points[i].a = 255;}
}// 关于
void CloudViewer::about() {AboutWin* aboutwin = new AboutWin(this);aboutwin->setModal(true);aboutwin->show();consoleLog("About", "Nightn", "http://nightn.github.io", "Welcome to my blog!");
}// 帮助
void CloudViewer::help() {QDesktopServices::openUrl(QUrl(QLatin1String("http://nightn.github.io/cloudviewer")));consoleLog("Help", "Cloudviewer help", "http://nightn.github.io/cloudviewer", "");
}// 绘制基本图形
void CloudViewer::createSphere() {mycloud.cloud.reset(new PointCloudT);ui.dataTree->clear();  // 清空资源管理器的itemviewer->removeAllShapes();mycloud_vec.clear();  // 清空点云容器pcl::PointXYZ p;p.x = 0; p.y = 0; p.z = 0;viewer->addSphere(p, 100, "sphere1");viewer->resetCamera();ui.screen->update();// 输出窗口consoleLog("Create sphere", "Sphere", "", "Succeeded");
}void CloudViewer::createCylinder() {mycloud.cloud.reset(new PointCloudT);ui.dataTree->clear();  // 清空资源管理器的itemviewer->removeAllShapes();mycloud_vec.clear();  // 清空点云容器viewer->addCylinder(*(new pcl::ModelCoefficients()), "cylinder");viewer->resetCamera();ui.screen->update();// 输出窗口consoleLog("Create cylinder", "Cylinder", "", "Failed");}// Change theme: Windows/Darcula
void CloudViewer::changeTheme() {QAction* action = qobject_cast<QAction*>(sender());QVariant v = action->data();int theme = (int)v.value<int>();QColor colorLight(241, 241, 241, 255);QColor colorDark(0, 0, 0, 255);QString qss;switch (theme) {case CLOUDVIEWER_THEME_WINDOWS: {qss = windows_qss;for (int i = 0; i != mycloud_vec.size(); i++) {if (ui.dataTree->topLevelItem(i)->textColor(0) == colorLight) {ui.dataTree->topLevelItem(i)->setTextColor(0, colorDark);}}theme_id = 0;consoleLog("Change theme", "Windows theme", "", "");break;}case CLOUDVIEWER_THEME_DARCULA: {qss = darcula_qss;for (int i = 0; i != mycloud_vec.size(); i++) {if (ui.dataTree->topLevelItem(i)->textColor(0) == colorDark) {ui.dataTree->topLevelItem(i)->setTextColor(0, colorLight);}}consoleLog("Change theme", "Darcula theme", "", "");theme_id = 1;break;}}qApp->setStyleSheet(qss);
}// Change language: English/Chinese
void CloudViewer::changeLanguage() {QAction* action = qobject_cast<QAction*>(sender());QVariant v = action->data();int language = (int)v.value<int>();switch (language) {case CLOUDVIEWER_LANG_ENGLISH: {consoleLog("Change language", "English", "", "");break;}case CLOUDVIEWER_LANG_CHINESE: {consoleLog("Change language", "Chinese", "Doesn't support Chinese temporarily", "");break;}}
}/*********************************************/
/*****************界面槽函数*****************/
/********************************************/
void CloudViewer::colorBtnPressed() {QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();int selected_item_count = ui.dataTree->selectedItems().size();// 如果未选中任何点云,则对视图窗口中的所有点云进行着色if (selected_item_count == 0) {for (int i = 0; i != mycloud_vec.size(); i++) {for (int j = 0; j != mycloud_vec[i].cloud->points.size(); j++) {mycloud_vec[i].cloud->points[j].r = 255 * (1024 * rand() / (RAND_MAX + 1.0f));mycloud_vec[i].cloud->points[j].g = 255 * (1024 * rand() / (RAND_MAX + 1.0f));mycloud_vec[i].cloud->points[j].b = 255 * (1024 * rand() / (RAND_MAX + 1.0f));}}// 输出窗口consoleLog("Random color", "All point clous", "", "");}else {for (int i = 0; i != selected_item_count; i++) {int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);for (int j = 0; j != mycloud_vec[cloud_id].cloud->size(); j++) {mycloud_vec[cloud_id].cloud->points[j].r = red;mycloud_vec[cloud_id].cloud->points[j].g = 255 * (1024 * rand() / (RAND_MAX + 1.0f));mycloud_vec[cloud_id].cloud->points[j].b = 255 * (1024 * rand() / (RAND_MAX + 1.0f));}}// 输出窗口consoleLog("Random color", "Point clouds selected", "", "");}showPointcloud();
}void CloudViewer::RGBsliderReleased() {QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();int selected_item_count = ui.dataTree->selectedItems().size();// 如果未选中任何点云,则对视图窗口中的所有点云进行着色if (selected_item_count == 0) {for (int i = 0; i != mycloud_vec.size(); i++) {mycloud_vec[i].setPointColor(red, green, blue);}// 输出窗口consoleLog("Change cloud color", "All point clouds", QString::number(red) + " " + QString::number(green) + " " + QString::number(blue), "");}else {for (int i = 0; i != selected_item_count; i++) {int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);mycloud_vec[cloud_id].setPointColor(red, green, blue);}// 输出窗口consoleLog("Change cloud color", "Point clouds selected", QString::number(red) + " " + QString::number(green) + " " + QString::number(blue), "");}showPointcloud();
}// 设置所有点云的尺寸void CloudViewer::psliderReleased() {std::cout << "test_ " << std::endl;int intValue = ui.sizeLCD->intValue();QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();int selected_item_count = ui.dataTree->selectedItems().size();if (selected_item_count == 0) {for (int i = 0; i != mycloud_vec.size(); i++) {viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,p, mycloud_vec[i].cloudId);}// 输出窗口consoleLog("Change cloud size", "All point clouds", "Size: " + QString::number(p), "");}else {for (int i = 0; i != selected_item_count; i++) {int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,p, mycloud_vec[i].cloudId);}// 输出窗口consoleLog("Change cloud size", "Point clouds selected", "Size: " + QString::number(p), "");}ui.screen->update();
}void CloudViewer::pSliderChanged(int value) {std::cout << "test_ " << std::endl;p = value;ui.sizeLCD->display(value);}void CloudViewer::rSliderChanged(int value) {red = value;ui.rLCD->display(value);
}void CloudViewer::gSliderChanged(int value) {green = value;ui.gLCD->display(value);
}void CloudViewer::bSliderChanged(int value) {blue = value;ui.bLCD->display(value);
}void CloudViewer::cooCbxChecked(int value) {switch (value) {case 0: {viewer->removeCoordinateSystem();consoleLog("Remove coordinate system", "Remove", "", "");break;}case 2: {viewer->addCoordinateSystem();consoleLog("Add coordinate system", "Add", "", "");break;}}ui.screen->update();
}void CloudViewer::bgcCbxChecked(int value) {switch (value) {case 0: {viewer->setBackgroundColor(30 / 255.0, 30 / 255.0, 30 / 255.0);consoleLog("Change bg color", "Background", "30 30 30", "");break;}case 2: {// !注意:setBackgroundColor()接收的是0-1的double型参数viewer->setBackgroundColor(240 / 255.0, 240 / 255.0, 240 / 255.0);consoleLog("Change bg color", "Background", "240 240 240", "");break;}}ui.screen->update();
}// 通过颜色对话框改变点云颜色
void CloudViewer::pointcolorChanged() {QColor color = QColorDialog::getColor(Qt::white, this, "Select color for point cloud");if (color.isValid()) {// QAction* action = dynamic_cast<QAction*>(sender());// if (action != ui.pointcolorAction) // 改变颜色的信号来自于 dataTreeQList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();int selected_item_count = ui.dataTree->selectedItems().size();if (selected_item_count == 0) {for (int i = 0; i != mycloud_vec.size(); ++i) {mycloud_vec[i].setPointColor(color.red(), color.green(), color.blue());}// 输出窗口consoleLog("Change cloud color", "All point clouds", QString::number(color.red()) + " " + QString::number(color.green()) + " " + QString::number(color.blue()), "");}else {for (int i = 0; i != selected_item_count; i++) {int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);mycloud_vec[cloud_id].setPointColor(color.red(), color.green(), color.blue());}// 输出窗口consoleLog("Change cloud color", "Point clouds selected", QString::number(color.red()) + " " + QString::number(color.green()) + " " + QString::number(color.blue()), "");}// 颜色的改变同步至RGB停靠窗口ui.rSlider->setValue(color.red());ui.gSlider->setValue(color.green());ui.bSlider->setValue(color.blue());showPointcloud();}
}// 通过颜色对话框改变背景颜色
void CloudViewer::bgcolorChanged() {QColor color = QColorDialog::getColor(Qt::white, this,"Select color for point cloud");if (color.isValid()) {viewer->setBackgroundColor(color.red() / 255.0,color.green() / 255.0, color.blue() / 255.0);// 输出窗口consoleLog("Change bg color", "Background", QString::number(color.red()) + " " + QString::number(color.green()) + " " + QString::number(color.blue()), "");showPointcloud();}
}// 三视图
void CloudViewer::mainview() {viewer->setCameraPosition(0, -1, 0, 0.5, 0.5, 0.5, 0, 0, 1);ui.screen->update();
}void CloudViewer::leftview() {viewer->setCameraPosition(-1, 0, 0, 0, 0, 0, 0, 0, 1);ui.screen->update();
}void CloudViewer::topview() {viewer->setCameraPosition(0, 0, 1, 0, 0, 0, 0, 1, 0);ui.screen->update();
}// 设置属性管理窗口
void CloudViewer::setPropertyTable() {QStringList header;header << "Property" << "Value";ui.propertyTable->setHorizontalHeaderLabels(header);ui.propertyTable->setItem(0, 0, new QTableWidgetItem("Clouds"));ui.propertyTable->setItem(0, 1, new QTableWidgetItem(QString::number(mycloud_vec.size())));ui.propertyTable->setItem(1, 0, new QTableWidgetItem("Points"));ui.propertyTable->setItem(1, 1, new QTableWidgetItem(""));ui.propertyTable->setItem(2, 0, new QTableWidgetItem("Faces"));ui.propertyTable->setItem(2, 1, new QTableWidgetItem(""));ui.propertyTable->setItem(3, 0, new QTableWidgetItem("Total points"));ui.propertyTable->setItem(3, 1, new QTableWidgetItem(QString::number(total_points)));ui.propertyTable->setItem(4, 0, new QTableWidgetItem("RGB"));ui.propertyTable->setItem(4, 1, new QTableWidgetItem(""));}void CloudViewer::setConsoleTable() {// 设置输出窗口QStringList header2;header2 << "Time" << "Operation" << "Operation object" << "Details" << "Note";ui.consoleTable->setHorizontalHeaderLabels(header2);ui.consoleTable->setColumnWidth(0, 150);ui.consoleTable->setColumnWidth(1, 200);ui.consoleTable->setColumnWidth(2, 200);ui.consoleTable->setColumnWidth(3, 300);// ui.consoleTable->setEditTriggers(QAbstractItemView::NoEditTriggers); // 设置不可编辑ui.consoleTable->verticalHeader()->setDefaultSectionSize(22); // 设置行距ui.consoleTable->setContextMenuPolicy(Qt::CustomContextMenu);}void CloudViewer::consoleLog(QString operation, QString subname, QString filename, QString note) {if (enable_console == false) {return;}int rows = ui.consoleTable->rowCount();ui.consoleTable->setRowCount(++rows);QDateTime time = QDateTime::currentDateTime();  // 获取系统现在的时间QString time_str = time.toString("MM-dd hh:mm:ss"); // 设置显示格式ui.consoleTable->setItem(rows - 1, 0, new QTableWidgetItem(time_str));ui.consoleTable->setItem(rows - 1, 1, new QTableWidgetItem(operation));ui.consoleTable->setItem(rows - 1, 2, new QTableWidgetItem(subname));ui.consoleTable->setItem(rows - 1, 3, new QTableWidgetItem(filename));ui.consoleTable->setItem(rows - 1, 4, new QTableWidgetItem(note));ui.consoleTable->scrollToBottom(); // 滑动自动滚到最底部
}// QTreeWidget的item的点击相应函数
void CloudViewer::itemSelected(QTreeWidgetItem* item, int count) {count = ui.dataTree->indexOfTopLevelItem(item);  // 获取item的行号for (int i = 0; i != mycloud_vec.size(); i++){viewer->updatePointCloud(mycloud_vec[i].cloud, mycloud_vec[i].cloudId);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, mycloud_vec[i].cloudId);}// 提取当前点云的RGB,点云数量等信息int cloud_size = mycloud_vec[count].cloud->points.size();unsigned int cloud_r = mycloud_vec[count].cloud->points[0].r;unsigned int cloud_g = mycloud_vec[count].cloud->points[0].g;unsigned int cloud_b = mycloud_vec[count].cloud->points[0].b;bool multi_color = true;if (mycloud_vec[count].cloud->points.begin()->r == (mycloud_vec[count].cloud->points.end() - 1)->r) // 判断点云单色多色的条件(不是很严谨)multi_color = false;ui.propertyTable->setItem(0, 1, new QTableWidgetItem(QString::number(mycloud_vec.size())));ui.propertyTable->setItem(1, 1, new QTableWidgetItem(QString::number(cloud_size)));int faces = mycloud_vec[count].meshId.size() != 0 ? mycloud_vec[count].mesh->polygons.size() : 0;ui.propertyTable->setItem(2, 1, new QTableWidgetItem(QString::number(faces)));ui.propertyTable->setItem(3, 1, new QTableWidgetItem(QString::number(total_points)));ui.propertyTable->setItem(4, 1, new QTableWidgetItem(multi_color ? "Multi Color" : (QString::number(cloud_r) + " " + QString::number(cloud_g) + " " + QString::number(cloud_b))));// 选中item所对应的点云尺寸变大QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();int selected_item_count = ui.dataTree->selectedItems().size();for (int i = 0; i != selected_item_count; i++) {int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,2, mycloud_vec[i].cloudId);}// mycloud = mycloud_vec[count];ui.screen->update();
}// consoleTable 右击响应事件
void CloudViewer::popMenuInConsole(const QPoint&) {QAction clearConsoleAction("Clear console", this);QAction enableConsoleAction("Enable console", this);QAction disableConsoleAction("Disable console", this);connect(&clearConsoleAction, &QAction::triggered, this, &CloudViewer::clearConsole);connect(&enableConsoleAction, &QAction::triggered, this, &CloudViewer::enableConsole);connect(&disableConsoleAction, &QAction::triggered, this, &CloudViewer::disableConsole);QPoint pos;QMenu menu(ui.dataTree);menu.addAction(&clearConsoleAction);menu.addAction(&enableConsoleAction);menu.addAction(&disableConsoleAction);if (enable_console == true) {menu.actions()[1]->setVisible(false);menu.actions()[2]->setVisible(true);}else {menu.actions()[1]->setVisible(true);menu.actions()[2]->setVisible(false);}menu.exec(QCursor::pos()); // 在当前鼠标位置显示
}// 清空 consoleTable
void CloudViewer::clearConsole() {ui.consoleTable->clearContents();ui.consoleTable->setRowCount(0);
}// 允许使用 consoleTable
void CloudViewer::enableConsole() {enable_console = true;
}// 禁用 consoleTable
void CloudViewer::disableConsole() {clearConsole();enable_console = false;}

3. 运行结果

 

 

三、在线协助:

如需安装运行环境或远程调试,见文章底部个人 QQ 名片,由专业技术人员远程协助!
1)远程安装运行环境,代码调试
2)Qt, C++, Python入门指导
3)界面美化
4)软件制作

当前文章连接:Python+Qt桌面端与网页端人工客服沟通工具_alicema1111的博客-CSDN博客

博主推荐文章:python人脸识别统计人数qt窗体-CSDN博客

博主推荐文章:Python Yolov5火焰烟雾识别源码分享-CSDN博客

                         Python OpenCV识别行人入口进出人数统计_python识别人数-CSDN博客

个人博客主页:alicema1111的博客_CSDN博客-Python,C++,网页领域博主

博主所有文章点这里alicema1111的博客_CSDN博客-Python,C++,网页领域博主

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/12755.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

watch避坑,使用computed进行处理数据

业务场景&#xff1a;在vue中监听el-input 中的字数有没有超过60&#xff0c;如果超过60字时将60后面的字变为 “>>” 符号&#xff0c;以此实现预览苹果手机推送摘要场景。 错误&#xff1a;开始的逻辑是使用watch监听&#xff0c;检查length超过60直接 加上符号&#x…

python 网络编程

TCP编程 客户端 创建TCP连接时&#xff0c;主动发起连接的叫做客户端&#xff0c;被动响应的叫做服务端。当定义一个Socket表示打开一个网络连接&#xff0c;创建一个Socket需要知道目标计算机的IP地址和端口号和对应的协议类型。 # 导入socket库: import socket# 创建一个s…

基于MSP432P401R爬坡小车【2020年电赛C题】

文章目录 一、任务清单1. 硬件部分2. 软件部分 二、OpenMV巡线三、舵机转向四、停止线识别五、技术交流 一、任务清单 1. 硬件部分 主控板&#xff1a; MSP432P401R数据显示&#xff1a; OLED电机&#xff1a; 霍尔编码器电机电池&#xff1a; 7.3V航模电池巡线&#xff1a; …

idea 里 controller service impl mapper xml 切换跳转快捷键

首先在controller层&#xff0c;对着接口点方法的方法上按着ctrl和鼠标左键&#xff0c;你会进入service层。 对着方法ctrlaltb不按鼠标&#xff0c;你会进入impl层。service层的方法上按ctrl和鼠标左键会回到controller&#xff0c;ctrlaltb不按鼠标也会进入到impl层,impl上的…

[Linux]线程基本知识

概念 进程 一个正在执行的程序&#xff0c;它是资源分配的最小单位 进程中的事情需要按照一定的顺序逐个进行 进程出现了很多弊端: 一是由于进程是资源拥有者&#xff0c;创建、撤消与切换存在较大的时空开销&#xff0c;因此需要引入轻型进程&#xff1b; 二是由于对称多…

C++:类和对象(下)---对类和对象深入一些的理解

文章目录 构造函数&#xff1f;初始化列表explicit关键字 匿名对象 构造函数&#xff1f; 初始化列表 前面已然介绍过构造函数&#xff0c;但并未完全结束&#xff0c;构造函数有很多种写法&#xff0c;有带缺省参数的&#xff0c;有全缺省的&#xff0c;不带缺省参数的…但用…

Linux下查找python路径

本地目前装了几个版本的python&#xff0c;这里记录下查找python路径的方法。 1&#xff1a;whereis命令 whereis python2&#xff1a;which命令 which python与whereis相似&#xff0c;但which会返回第一个找到的执行文件的位置。 3&#xff1a;find命令 find命令可以搜索系…

web流程自动化详解

今天给大家带来Selenium的相关解释操作 一、Selenium Selenium是一个用于自动化Web浏览器操作的开源工具和框架。它提供了一组API&#xff08;应用程序接口&#xff09;&#xff0c;可以让开发人员使用多种编程语言&#xff08;如Java、Python、C#等&#xff09;编写测试脚本&…

需求分析案例:消息配置中心

本文介绍了一个很常见的消息推送需求&#xff0c;在系统需要短信、微信、邮件之类的消息推送时&#xff0c;边界如何划分和如何设计技术方案。 1、需求 一个系统&#xff0c;一般会区分多个业务模块&#xff0c;并拆分成不同的业务系统&#xff0c;例如一个商城的架构如下&am…

day45-Netflix Mobile Navigation(左边侧边栏动态导航)

50 天学习 50 个项目 - HTMLCSS and JavaScript day45-Netflix Mobile Navigation&#xff08;左边侧边栏动态导航&#xff09; 效果 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name&…

33. 本地记事本

本地记事本 html部分 <button class"add"><i class"iconfont icon-jiahao"></i> </button>css部分 *{margin: 0;padding: 0; } body{background-color: #7bdaf3;display: flex;padding-top: 3rem;flex-wrap: wrap; } .add{pos…

每日一题——删除有序数组中的重复项

删除有序数组中的重复项 题目链接 注&#xff1a;本题所采用的方法是建立在移除元素的基础之上的&#xff0c;如果大家对双指针的方法不大了解&#xff0c;或者不会做《移除元素》这一题&#xff0c;建议先去看看&#x1f449;传送门 具体步骤 定义两个指针slow和fast&#…

【计算机网络】传输层协议 -- UDP协议

文章目录 1. 传输层相关知识1.1 端口号1.2 端口号范围划分1.3 知名端口号1.4 一些相关命令 2. UDP协议2.1 UDP协议格式2.2 UDP协议的特点2.3 什么是面向数据报2.4 UDP的缓冲区2.5 UDP使用注意事项2.6 基于UDP的应用层协议 1. 传输层相关知识 传输层是计算机网络中的一个重要层…

【如何训练一个中英翻译模型】LSTM机器翻译模型部署之onnx(python)(四)

系列文章 【如何训练一个中英翻译模型】LSTM机器翻译seq2seq字符编码&#xff08;一&#xff09; 【如何训练一个中英翻译模型】LSTM机器翻译模型训练与保存&#xff08;二&#xff09; 【如何训练一个中英翻译模型】LSTM机器翻译模型部署&#xff08;三&#xff09; 【如何…

云原生架构

1. 何为云原生&#xff1f; 很多IT业内小伙伴会经常听到这个名词&#xff0c;那么什么是云原生呢&#xff1f;云原生是在云计算环境中构建、部署和管理现代应用程序的软件方法。 当今时代&#xff0c;众多企业希望构建高度可扩展、灵活且有弹性的应用程序&#xff0c;以便能够快…

13 亿美金买个寂寞?No!AI 时代的数据行业蓄势待发

6月底&#xff0c;全球数据分析领域彻底炸锅了。 两大数据分析企业Databricks和Snowflake纷纷将目光瞄准了AI大模型。要知道&#xff0c;这两位对手平时没少对台戏&#xff0c;为性能、产品和技术经常开撕。但在今年的自家大会上&#xff0c;两家企业却出奇的一致&#xff0c;…

云安全攻防(二)之 云原生安全

云原生安全 什么是云原生安全&#xff1f;云原生安全包含两层含义&#xff1a;面向云原生环境的安全和具有云原生特征的安全 面向云原生环境的安全 面向云原生环境的安全的目标是防护云原生环境中的基础设施、编排系统和微服务系统的安全。这类安全机制不一定会具有云原生的…

Java 设计模式 - 简单工厂模式 - 创建对象的简便之道

简单工厂模式是一种创建型设计模式&#xff0c;它提供了一种简单的方式来创建对象&#xff0c;而无需暴露对象创建的逻辑。在本篇博客中&#xff0c;我们将深入了解简单工厂模式的概念、实现方式以及如何在Java中使用它来创建对象。 为什么使用简单工厂模式&#xff1f; 在软…

【无标题】深圳卫视专访行云创新马洪喜:拥抱AI与云原生,深耕云智一体化创新

人工智能&#xff08;AI&#xff09;是引领新一轮科技革命和产业变革的重要驱动力。因此&#xff0c;深圳出台相关行动方案&#xff0c;统筹设立规模1,000亿元的人工智能基金群&#xff0c;引导产业集聚培育企业梯队&#xff0c;积极打造国家新一代人工智能创新发展试验区和国家…

【高压架构】AP5199S LED平均电流型恒流驱动IC 0.01调光 景观舞台汽车灯驱动照明

说明 AP5199S 是一款外围电路简单的多功能平均电流型 LED 恒流驱动器&#xff0c;适用于宽电压范围的非隔离式大功率恒流 LED 驱动领域。芯片 PWM 端口支持超小占空比的 PWM 调光&#xff0c;可响应 60ns 脉宽。为客户提供解决方案&#xff0c;限度地发挥灯具优势&#xff0c;…