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 = false
	ui.saveBinaryAction->setData(QVariant(true));  // isSaveBinary = true
	connect(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 one
	for (int i = 0; i != filePathList.size(); i++) {
		timeStart(); // time start
		mycloud.cloud.reset(new PointCloudT); // Reset cloud
		QFileInfo fileInfo(filePathList[i]);
		std::string filePath = fromQString(fileInfo.filePath());
		std::string fileName = fromQString(fileInfo.fileName());

		// begin loading
		ui.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 off

		consoleLog(
			"Open",
			toQString(mycloud.fileName),
			toQString(mycloud.filePath),
			"Time cost: " + timeCostSecond + " s, Points: " + QString::number(mycloud.cloud->points.size())
		);

		// update tree widget
		QTreeWidgetItem* 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 function
	mycloud_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();  // 清空属性窗口propertyTable
	QStringList 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,                                    // parent
		toQString("Save point cloud" + string(isSaveBinary ? " (binary)" : "")), // caption
		toQString(mycloud.filePath),             // dir
		toQString(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_cloud
	bool 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();  // 清空资源管理器的item
	viewer->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); // 禁止点击属性管理器的 item
	ui.consoleTable->setSelectionMode(QAbstractItemView::NoSelection);  // 禁止点击输出窗口的 item
	ui.dataTree->setSelectionMode(QAbstractItemView::ExtendedSelection); // 允许 dataTree 进行多选

	// 设置默认主题
	QString qss = darcula_qss;
	qApp->setStyleSheet(qss);

	setPropertyTable();
	setConsoleTable();

	// 输出窗口
	consoleLog("Software start", "CloudViewer", "Welcome to use CloudViewer", "Nightn");

	// 设置背景颜色为 dark
	viewer->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 color
	for (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();  // 清空资源管理器的item
	viewer->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();  // 清空资源管理器的item
	viewer->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) // 改变颜色的信号来自于 dataTree
		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(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++,网页领域博主

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CentOS系统启动过程

CentOS系统启动流程图 &#xff08;1&#xff09; 加载 BIOS 计算机电源加电质检&#xff0c;首先加载基本输入输出系统&#xff08;Basic Input Output System&#xff0c;BIOS&#xff09;&#xff0c;BIOS 中包含硬件 CPU、内存、硬盘等相关信息&#xff0c;包含设备启动顺序…

【文献分享】比目前最先进的模型轻30%!高效多机器人SLAM蒸馏描述符!

论文题目&#xff1a;Descriptor Distillation for Efficient Multi-Robot SLAM 中文题目&#xff1a;高效多机器人SLAM蒸馏描述符 作者&#xff1a;Xiyue Guo, Junjie Hu, Hujun Bao and Guofeng Zhang 作者机构&#xff1a;浙江大学CAD&CG国家重点实验室 香港中文大学…

JS正则表达式:常用正则手册/RegExp/正则积累

一、正则基础语法 JavaScript 正则表达式 | 菜鸟教程 JS正则表达式语法大全&#xff08;非常详细&#xff09; 二、使用场景 2.1、校验中国大陆手机号的正则表达式 正则 /^1[3456789]\d{9}$/解释 序号正则解释1^1以数字 1 开头2[3456789]第二位可以是 3、4、5、6、7、8、…

【性能优化】MySQL百万数据深度分页优化思路分析

业务场景 一般在项目开发中会有很多的统计数据需要进行上报分析&#xff0c;一般在分析过后会在后台展示出来给运营和产品进行分页查看&#xff0c;最常见的一种就是根据日期进行筛选。这种统计数据随着时间的推移数据量会慢慢的变大&#xff0c;达到百万、千万条数据只是时间问…

3.Java面试题—JVM基础、内存管理、垃圾回收、JVM 调优

本文目录如下&#xff1a; 七、JVM一、JVM 基础什么是Java虚拟机?Java对象 创建过程?对象有哪几种引用&#xff1f; 二、内存管理说⼀下 JVM 的内存区域 (堆 和 栈)&#xff1f;内存溢出 和 内存泄漏 是什么意思&#xff1f;内存泄漏可能由哪些原因导致呢&#xff1f;Java 堆…

rocketmq rsqldb 简单记录

GitHub 地址 https://github.com/alibaba/rsqldb/tree/main&#xff0c;是和目前stream sql化看齐的Rocketmq的sql&#xff0c;类似还有kafka的sqlDB 和flink sql。 目前版本0.2 &#xff0c;主要提供rest模式调用&#xff0c;controller类为public class RsqlController支持的…

F5 LTM 知识点和实验 5-健康检测

第五章:健康检测 监控的分类: 地址监控(3层)服务监控(4层)内容监控(7层)应用监控(7层)性能监控(7层)路径监控(3、4、7层)三层监控: 三层监控可以帮助bipip系统通过检查网络是否可达监视资源。比如使用icmp echo,向监控节点发送icmp_echo报文,如果接收到响应…

Redis(三)—— Redis基本的事务操作、Redis实现乐观锁

一、Redis基本的事务操作 首先声明&#xff1a; redis的单条命令是保证原子性的&#xff08;回想一下setnx k1 v1 k5 v5命令如果k1已经存在&#xff0c;那么k5也会设置失败&#xff09;但是redis的事务不保证原子性&#xff01;见下面“1.2 某条命令有错怎么办&#xff1f;”…

C++之科学技术法e使用(一百七十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【读点论文】PP-YOLOE: An evolved version of YOLO,面向友好部署的模型设计,为项目后续产业落地提供了更加有效的参考

PP-YOLOE: An evolved version of YOLO Abstract 在本报告中&#xff0c;我们介绍了PP-YOLOE&#xff0c;一种具有高性能和友好部署的工业最先进的目标探测器。我们在之前的PP-YOLOv2的基础上进行优化&#xff0c;采用无锚模式&#xff0c;更强大的骨干和颈部配备CSPRepResSt…

安全学习DAY08_算法加密

算法加密 漏洞分析、漏洞勘测、漏洞探针、挖漏洞时要用到的技术知识 存储密码加密-应用对象传输加密编码-发送回显数据传输格式-统一格式代码特性混淆-开发语言 传输数据 – 加密型&编码型 安全测试时&#xff0c;通常会进行数据的修改增加提交测试 数据在传输的时候进行…

0725 区块链1.0 2.0 3.0 智能合约 比特币 以太坊 DAG 有向无环图

文献阅读&#xff1a;[1]华亚洲. 基于改进Block-DAG区块链的时空数据存储及查询方法研究[D].辽宁大学,2022.DOI:10.27209/d.cnki.glniu.2022.001364.[1]华亚洲,丁琳琳,陈泽等.面向时空数据的区块链构建及查询方法[J].计算机应用,2022,42(11):3429-3437. 文献总结&#xff1a; …

【 Spring AOP学习二】统一功能处理:拦截器异常返回数据格式

目录 一、用户登录权限效验 &#x1f351;1、Spring拦截器实现用户统一登录验证&#xff08;重要&#xff09; &#xff08;1&#xff09;定义一个拦截器 &#xff08;2&#xff09;将自定义拦截器加入到系统配置中 &#x1f351;2、拦截器实现原理 &#x1f351;3、统一…

【计算机视觉中的 GAN 】 - 条件图像合成和 3D 对象生成(2)

一、说明 上文 【计算机视觉中的 GAN 】或多或少是GANs&#xff0c;生成学习和计算机视觉的介绍。我们达到了在 128x128 图像中生成可区分图像特征的程度。但是&#xff0c;如果你真的想了解GAN在计算机视觉方面的进展&#xff0c;你肯定必须深入研究图像到图像的翻译。…

mybatis-config.xml-配置文件详解

文章目录 mybatis-config.xml-配置文件详解说明文档地址:配置文件属性解析properties 属性应用实例 settings 全局参数定义应用实例 typeAliases 别名处理器举例说明 typeHandlers 类型处理器environments 环境environment 属性应用实例 mappers配置 mybatis-config.xml-配置文…

什么是 HTTP 长轮询?

什么是 HTTP 长轮询&#xff1f; Web 应用程序最初是围绕客户端/服务器模型开发的&#xff0c;其中 Web 客户端始终是事务的发起者&#xff0c;向服务器请求数据。因此&#xff0c;没有任何机制可以让服务器在没有客户端先发出请求的情况下独立地向客户端发送或推送数据。 为…

在linux中进行arm交叉编译体验tiny6410裸机程序开发流程

在某鱼上找了一个友善之臂的Tiny6410开发板用来体验一下嵌入式开发。这次先体验一下裸机程序的开发流程&#xff0c;由于这个开发板比较老旧了&#xff0c;官方文档有很多过期的内容&#xff0c;所以记录一下整个过程。 1. 交叉编译器安装 按照光盘A中的文档《04- Tiny6410 L…

fasta序列转为数字0和1-python

原始文件&#xff1a; 目标文件&#xff1a; linux版本 #name:lin_convert_fasta_to_01.py #! /usr/bin/env python #usage: python hash-always.py -l 1.list -f 2.txt > out.txt import argparse parser argparse.ArgumentParser(description"Advanced screeni…

perf 分析MySQL底层函数调用

文章目录 一、安装软件包二、数据采集2.1 perf top2.2 perf record 三、数据加工和解读 一、安装软件包 sudo yum install -y perf git clone https://github.com/brendangregg/FlameGraph二、数据采集 2.1 perf top perf top -g -p pidof mysqld 第一列&#xff1a;符号引…

常见面试题之设计模式--工厂方法模式

1. 概述 需求&#xff1a;设计一个咖啡店点餐系统。 设计一个咖啡类&#xff08;Coffee&#xff09;&#xff0c;并定义其两个子类&#xff08;美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】&#xff09;&#xff1b;再设计一个咖啡店类&#xff08;CoffeeStore&…