程序示例精选
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++,网页领域博主