CloudCompare源代码编译成功后,即可进行二次开发,可通过修改源码实现二次开发基础功能(见:CloudCompare如何进行二次开发?),也可修改源码实现二次开发界面ui设计与实现(见:CloudCompare二次开发之如何设计界面ui与功能实现?),二次开发也可进行插件式开发(见:CloudCompare如何进行二次开发之插件开发?),若想要实现更多自定义功能,可以自定义界面ui,并操作CloudCompare程序处理数据。本文讲解CloudCompare的插件能够被扩展的ui界面进行克隆点云操作。
本文使用的插件文件来自:CloudCompare插件开发之点云如何创建、保存并显示?
本文使用的界面ui文件来自:CloudCompare二次开发之如何设计界面ui与功能实现?
CMake编译,将插件目录文件包含进工程。
编译步骤见:CloudCompare如何进行二次开发之插件开发?。
#pragma once
#include "ccOverlayDialog.h"
#include "ui_MyForm2.h"
class QMdiSubWindow;
class ccGenericPointCloud;
class ccPointCloud;
class MyForm2 :public QWidget
{
Q_OBJECT
public:
//MyForm2(QWidget *parent = Q_NULLPTR);
MyForm2(ccPointCloud* pc, QWidget *parent = Q_NULLPTR);
//explicit MyForm2(QWidget* parent, ccPointCloud* pc);
virtual ~MyForm2() override;
signals:
void sig(ccPointCloud* pcOut);
public slots:
void onClone(); //点云克隆
void closeForm();//关闭窗体
private:
Ui::MyFormClass* m_ui = nullptr; //功能界面
ccPointCloud* m_cloud = nullptr; //选中的点云
};
②MyFom.cpp修改
#pragma once
#include "MyForm2.h"
#include "ccPointCloud.h"
#include "OperateData_1.h"
MyForm2::MyForm2(ccPointCloud* pc, QWidget* parent)
:QWidget(parent)
, m_cloud(pc)
, m_ui(new Ui::MyFormClass)
{
m_ui->setupUi(this);
//信号槽连接
connect(m_ui->pushButton, &QAbstractButton::clicked, this, &MyForm2::onClone);
connect(m_ui->pushButton_2, &QAbstractButton::clicked, this, &MyForm2::closeForm);
}
MyForm2::~MyForm2()
{
if (m_ui) {
delete m_ui;
m_ui = nullptr;
}
if (m_cloud) {
delete m_cloud;
m_cloud = nullptr;
}
}
void MyForm2::onClone()
{
if (!m_cloud)
{
return;
}
//点到点的克隆
ccPointCloud* pc = new ccPointCloud(m_cloud->getName() + QString("-Clone"));
//为克隆对象分配内存
pc->reserve(m_cloud->size());
size_t pointSize = m_cloud->size();
for (size_t i = 0; i < pointSize; ++i)
{
pc->addPoint(*m_cloud->getPoint(i));
}
//发送信号给主窗体
emit sig(pc);
}
void MyForm2::closeForm()
{
this->close();
}
(2)OperateData_1文件修改
①OperateData_1.h修改
#ifndef EXAMPLE_PLUGIN_HEADER
#define EXAMPLE_PLUGIN_HEADER
//##########################################################################
//# #
//# CLOUDCOMPARE PLUGIN: OperateData_1 #
//# #
//# This program is free software; you can redistribute it and/or modify #
//# it under the terms of the GNU General Public License as published by #
//# the Free Software Foundation; version 2 of the License. #
//# #
//# This program is distributed in the hope that it will be useful, #
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
//# GNU General Public License for more details. #
//# #
//# COPYRIGHT: XXX #
//# #
//##########################################################################
#include "ccStdPluginInterface.h"
#include "MyForm2.h"
//! Example qCC plugin
/** Replace 'MySecondPlugin' by your own plugin class name throughout and then
check 'MySecondPlugin.cpp' for more directions.
Each plugin requires an info.json file to provide information about itself -
the name, authors, maintainers, icon, etc..
The one method you are required to implement is 'getActions'. This should
return all actions (QAction objects) for the plugin. CloudCompare will
automatically add these with their icons in the plugin toolbar and to the
plugin menu. If your plugin returns several actions, CC will create a
dedicated toolbar and a sub-menu for your plugin. You are responsible for
connecting these actions to methods in your plugin.
Use the ccStdPluginInterface::m_app variable for access to most of the CC
components (database, 3D views, console, etc.) - see the ccMainAppInterface
class in ccMainAppInterface.h.
**/
class OperateData_1 : public QObject, public ccStdPluginInterface
{
Q_OBJECT
Q_INTERFACES(ccStdPluginInterface)
// Replace "Example" by your plugin name (IID should be unique - let's hope your plugin name is unique ;)
// The info.json file provides information about the plugin to the loading system and
// it is displayed in the plugin information dialog.
Q_PLUGIN_METADATA(IID "cccorp.cloudcompare.plugin.Example" FILE "info.json")
public:
explicit OperateData_1(QObject *parent = nullptr);
~OperateData_1() override = default;
//OperateData_1(QObject * parent);
// inherited from ccStdPluginInterface
void onNewSelection(const ccHObject::Container &selectedEntities) override;
QList<QAction *> getActions() override;
private:
/*** ADD YOUR CUSTOM ACTIONS HERE ***/
void doAction();
//! Default action
/** You can add as many actions as you want in a plugin.
Each action will correspond to an icon in the dedicated
toolbar and an entry in the plugin menu.
**/
QAction* m_action;
private slots:
void onClone(ccPointCloud*); //点云克隆
private:
MyForm2* myForm2 = nullptr;
};
#endif
//##########################################################################
//# #
//# CLOUDCOMPARE PLUGIN: OperateData_1 #
//# #
//# This program is free software; you can redistribute it and/or modify #
//# it under the terms of the GNU General Public License as published by #
//# the Free Software Foundation; version 2 of the License. #
//# #
//# This program is distributed in the hope that it will be useful, #
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
//# GNU General Public License for more details. #
//# #
//# COPYRIGHT: XXX #
//# #
//##########################################################################
// First:
// Replace all occurrences of 'OperateData_1' by your own plugin class name in this file.
// This includes the resource path to info.json in the constructor.
// Second:
// Open OperateData_1.qrc, change the "prefix" and the icon filename for your plugin.
// Change the name of the file to .qrc
// Third:
// Open the info.json file and fill in the information about the plugin.
// "type" should be one of: "Standard", "GL", or "I/O" (required)
// "name" is the name of the plugin (required)
// "icon" is the Qt resource path to the plugin's icon (from the .qrc file)
// "description" is used as a tootip if the plugin has actions and is displayed in the plugin dialog
// "authors", "maintainers", and "references" show up in the plugin dialog as well
#include
#include "OperateData_1.h"
#include "qinputdialog.h"
#include "ccProgressDialog.h"
#include "ccPointCloud.h"
#include
#include
#include "qtextstream.h"
#include "math.h"
#include
using namespace std;
// Default constructor:
// - pass the Qt resource path to the info.json file (from .qrc file)
// - constructor should mainly be used to initialize actions and other members
OperateData_1::OperateData_1(QObject *parent)
: QObject(parent)
, ccStdPluginInterface(":/CC/plugin/OperateData_1/info.json")
, m_action(nullptr)
{
}
// This method should enable or disable your plugin actions
// depending on the currently selected entities ('selectedEntities').
void OperateData_1::onNewSelection(const ccHObject::Container &selectedEntities)
{
if (m_action == nullptr)
{
return;
}
// If you need to check for a specific type of object, you can use the methods
// in ccHObjectCaster.h or loop and check the objects' classIDs like this:
//
// for ( ccHObject *object : selectedEntities )
// {
// if ( object->getClassID() == CC_TYPES::VIEWPORT_2D_OBJECT )
// {
// // ... do something with the viewports
// }
// }
// For example - only enable our action if something is selected.
//m_action->setEnabled(!selectedEntities.empty());
m_action->setEnabled(true);
}
// This method returns all the 'actions' your plugin can perform.
// getActions() will be called only once, when plugin is loaded.
QList<QAction *> OperateData_1::getActions()
{
// default action (if it has not been already created, this is the moment to do it)
if (!m_action)
{
// Here we use the default plugin name, description, and icon,
// but each action should have its own.
m_action = new QAction(getName(), this);
m_action->setToolTip(getDescription());
m_action->setIcon(getIcon());
// Connect appropriate signal
connect(m_action, &QAction::triggered, this, &OperateData_1::doAction);
}
return{ m_action };
}
// This is an example of an action's method called when the corresponding action
// is triggered (i.e. the corresponding icon or menu entry is clicked in CC's
// main interface). You can access most of CC's components (database,
// 3D views, console, etc.) via the 'm_app' variable (see the ccMainAppInterface
// class in ccMainAppInterface.h).
void OperateData_1::doAction()
{
if (m_app == nullptr)
{
// m_app should have already been initialized by CC when plugin is loaded
Q_ASSERT(false);
return;
}
const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();//此处为选择已有点云的操作
ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(selectedEntities[0]);//此处为选择已有点云的操作
myForm2 = new MyForm2(cloud);
connect(myForm2, SIGNAL(sig(ccPointCloud*)), this, SLOT(onClone(ccPointCloud*)));
myForm2->show();
创建点云
//ccPointCloud* myPc = new ccPointCloud(QString("myPc"));
//int pointCount = 10000;//设置创建10000个点
//myPc->reserve(pointCount);
//for (size_t i = 0; i < pointCount; i++)
//{
// float angle = (i % 360)*3.1415926 / 180;
// float x = 100 * cos(angle);
// float y = 100 * sin(angle);
// float z = int(i / 360) * 1;
// const CCVector3* vcc = new CCVector3(x, y, z);
// myPc->addPoint(*vcc);
//}
保存点云
//QString dirPath = QFileDialog::getExistingDirectory(nullptr, "please select a saving path");
//if (dirPath.isEmpty()) {
// m_app->dispToConsole("The user did not select a folder.");
// return;
//}
//QString filename = QString(myPc->getName());
//QFile file(dirPath + "\\" + filename + ".txt");
//if (!file.exists()) {
// file.open(QIODevice::ReadWrite | QIODevice::Text);
// file.close();
//}
//file.open(QIODevice::Text | QIODevice::Truncate | QIODevice::WriteOnly);
//QTextStream out(&file);
//int precision = 3;
//for (int i = 0; isize(); i++) {
// float x = myPc->getPoint(i)->x;
// float y = myPc->getPoint(i)->y;
// float z = myPc->getPoint(i)->z;
// out << QString("%1,%2,%3").arg(x, 0, 'r', precision).arg(y, 0, 'r', precision).arg(z, 0, 'r', precision) << endl;
//}
//file.close();
显示点云
//std::vector allCloud;
//allCloud.push_back(myPc);
//ccHObject* CloudGroup = new ccHObject(QString("CloudGroup"));
//for (size_t i = 0; i < allCloud.size(); i++)
//{
// CloudGroup->addChild(allCloud[i]);
//}
//m_app->addToDB(CloudGroup);
//m_app->refreshAll();
//m_app->updateUI();
}
void OperateData_1::onClone(ccPointCloud* myPc)
{
//显示点云
std::vector<ccHObject*> allCloud;
allCloud.push_back(myPc);
ccHObject* CloudGroup = new ccHObject(QString("CloudGroup"));
for (size_t i = 0; i < allCloud.size(); i++)
{
CloudGroup->addChild(allCloud[i]);
}
m_app->addToDB(CloudGroup);
m_app->refreshAll();
m_app->updateUI();
}
参考资料:
[1] cacrle. Visual Studio如何使用Qt开发桌面软件?; 2023-04-18 [accessed 2023-04-20].
[2] cacrle. CloudCompare如何进行二次开发?; 2023-04-19 [accessed 2023-04-20].
[3] cacrle. CloudCompare如何进行二次开发之插件开发?; 2023-04-19 [accessed 2023-04-20].
[4] cacrle. CloudCompare二次开发之如何设计界面ui与功能实现?; 2023-04-19 [accessed 2023-04-20].
[5] 问也去. CloudCompare实现点选点云功能; 2021-09-23 [accessed 2023-04-20].
[6] 进击の小黑. CloudCompare简单二次开发教程 上(界面设计与ui文件编译); 2020-12-17 [accessed 2023-04-20].
[7] 进击の小黑. CloudCompare简单二次开发 下(功能实现); 2020-12-18 [accessed 2023-04-20].
[8] shaomq2187. VS2019已有项目中添加Qt; 2021-11-08 [accessed 2023-04-20].
[9] wb175208. VS2013 在配置中手动添加宏定义; 2018-04-08 [accessed 2023-04-20].