2026-02-19 00:45:00 +08:00
|
|
|
|
#ifndef CLOUD_VIEW_MAIN_WINDOW_H
|
|
|
|
|
|
#define CLOUD_VIEW_MAIN_WINDOW_H
|
|
|
|
|
|
|
|
|
|
|
|
#include <QMainWindow>
|
|
|
|
|
|
#include <QWidget>
|
|
|
|
|
|
#include <QPushButton>
|
2026-03-11 23:39:28 +08:00
|
|
|
|
#include <QToolButton>
|
2026-02-19 00:45:00 +08:00
|
|
|
|
#include <QLabel>
|
|
|
|
|
|
#include <QListWidget>
|
|
|
|
|
|
#include <QGroupBox>
|
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
|
|
#include <QSplitter>
|
|
|
|
|
|
#include <QFileDialog>
|
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
|
#include <QStatusBar>
|
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
|
#include <QRadioButton>
|
|
|
|
|
|
#include <QCheckBox>
|
2026-08-08 17:41:02 +08:00
|
|
|
|
#include <QComboBox>
|
|
|
|
|
|
#include <QDialog>
|
|
|
|
|
|
#include <QStringList>
|
|
|
|
|
|
#include <memory>
|
2026-02-19 00:45:00 +08:00
|
|
|
|
|
2026-08-08 17:41:02 +08:00
|
|
|
|
#include "CloudView3D.h"
|
|
|
|
|
|
#include "PointCloudConverter.h"
|
2026-02-19 00:45:00 +08:00
|
|
|
|
|
2026-08-08 17:41:02 +08:00
|
|
|
|
class QTextEdit;
|
|
|
|
|
|
class QTableWidget;
|
|
|
|
|
|
class QCloseEvent;
|
2026-02-19 00:45:00 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 点云查看器主窗口
|
|
|
|
|
|
* 左侧显示点云,右侧为操作区域
|
|
|
|
|
|
*/
|
|
|
|
|
|
class CloudViewMainWindow : public QMainWindow
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
2026-08-08 17:41:02 +08:00
|
|
|
|
public:
|
|
|
|
|
|
struct StartupFiles
|
|
|
|
|
|
{
|
|
|
|
|
|
QStringList displayFiles;
|
|
|
|
|
|
QStringList bboxFiles;
|
|
|
|
|
|
QStringList circleFiles;
|
|
|
|
|
|
QStringList rectangleFiles;
|
|
|
|
|
|
QStringList surfaceFiles;
|
|
|
|
|
|
QStringList triangleFiles;
|
|
|
|
|
|
bool sequential = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
explicit CloudViewMainWindow(QWidget* parent = nullptr);
|
|
|
|
|
|
~CloudViewMainWindow() override;
|
|
|
|
|
|
|
|
|
|
|
|
bool loadStartupFiles(const StartupFiles& files);
|
|
|
|
|
|
bool loadDisplayFile(const QString& fileName);
|
|
|
|
|
|
bool loadCircleShapeFile(const QString& fileName);
|
|
|
|
|
|
bool loadRectangleShapeFile(const QString& fileName);
|
|
|
|
|
|
bool loadSurfaceShapeFile(const QString& fileName);
|
|
|
|
|
|
bool loadTriangleShapeFile(const QString& fileName);
|
|
|
|
|
|
void fitViewToContent();
|
|
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
bool eventFilter(QObject* obj, QEvent* event) override;
|
|
|
|
|
|
void closeEvent(QCloseEvent* event) override;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
enum class StartupFileType
|
|
|
|
|
|
{
|
|
|
|
|
|
Display,
|
|
|
|
|
|
BBox,
|
|
|
|
|
|
Circle,
|
|
|
|
|
|
Rectangle,
|
|
|
|
|
|
Surface,
|
|
|
|
|
|
Triangle
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct StartupItem
|
|
|
|
|
|
{
|
|
|
|
|
|
StartupFileType type;
|
|
|
|
|
|
QString fileName;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void buildStartupSequence(const StartupFiles& files);
|
|
|
|
|
|
bool loadStartupItem(const StartupItem& item);
|
|
|
|
|
|
bool loadCurrentStartupSequenceItem();
|
|
|
|
|
|
|
|
|
|
|
|
private slots:
|
2026-02-19 00:45:00 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 打开文件
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onOpenFile();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 打开姿态点文件
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onOpenPoseFile();
|
|
|
|
|
|
|
2026-03-11 23:39:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 打开包围盒JSON文件
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onOpenBBoxFile();
|
|
|
|
|
|
|
2026-02-19 00:45:00 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 清除所有点云
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onClearAll();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 重置视图
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onResetView();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 清除选中的点
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onClearSelectedPoints();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 清除选线
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onClearLinePoints();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理点选中事件
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onPointSelected(const SelectedPointInfo& point);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理两点选中事件(测距)
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onTwoPointsSelected(const SelectedPointInfo& p1, const SelectedPointInfo& p2, float distance);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理线选中事件
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onLineSelected(const SelectedLineInfo& line);
|
|
|
|
|
|
|
2026-03-01 18:14:19 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 点1坐标编辑完成(回车)
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onPoint1CoordChanged();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 点2坐标编辑完成(回车)
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onPoint2CoordChanged();
|
|
|
|
|
|
|
2026-02-19 00:45:00 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 通过输入线号选择线
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onSelectLineByNumber();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 选线模式切换(纵向/横向)
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onLineSelectModeChanged(bool checked);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 显示选中线上的所有点坐标
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onShowLinePoints();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 线上点列表项被点击
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onLinePointTableClicked(int row, int column);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 显示点1的姿态
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onShowPose1();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 显示点2的姿态
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onShowPose2();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 显示输入的线段
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onShowInputLine();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 清除输入的线段
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onClearInputLine();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 欧拉角旋转顺序改变
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onEulerOrderChanged(int index);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-14 17:04:39 +08:00
|
|
|
|
* @brief 眼在手外矩阵变换(直接应用矩阵到点云)
|
2026-02-19 00:45:00 +08:00
|
|
|
|
*/
|
2026-06-14 17:04:39 +08:00
|
|
|
|
void onEyeToHandTransform();
|
2026-02-19 00:45:00 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-14 17:04:39 +08:00
|
|
|
|
* @brief 眼在手上矩阵变换(手眼矩阵 + 机械臂位姿)
|
2026-02-19 00:45:00 +08:00
|
|
|
|
*/
|
2026-06-14 17:04:39 +08:00
|
|
|
|
void onEyeInHandTransform();
|
2026-02-19 00:45:00 +08:00
|
|
|
|
|
2026-04-25 19:00:49 +08:00
|
|
|
|
void onGeneratePointCloud();
|
|
|
|
|
|
void onRotateCloudByZ();
|
2026-06-14 17:04:39 +08:00
|
|
|
|
void onSavePointCloud(); // 保存 txt 点云
|
|
|
|
|
|
void onSavePlyPcdCloud(); // 保存 ply/pcd 点云
|
2026-07-03 12:46:19 +08:00
|
|
|
|
void onBatchConvertTxtToPly(); // 批量转换 txt -> ply
|
2026-08-08 17:41:02 +08:00
|
|
|
|
void onConvertEulerMatrix();
|
|
|
|
|
|
void onShowCircleShape();
|
|
|
|
|
|
void onShowRectangleShape();
|
|
|
|
|
|
void onShowSurfaceShape();
|
|
|
|
|
|
void onShowTriangleShape();
|
2026-02-19 00:45:00 +08:00
|
|
|
|
|
2026-06-14 17:04:39 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 姿态补偿计算
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onPoseCompensation();
|
|
|
|
|
|
|
2026-02-19 00:45:00 +08:00
|
|
|
|
private:
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 初始化界面
|
|
|
|
|
|
*/
|
|
|
|
|
|
void setupUI();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 创建左侧点云显示区域
|
|
|
|
|
|
*/
|
|
|
|
|
|
QWidget* createViewerArea();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 创建右侧操作区域
|
|
|
|
|
|
*/
|
|
|
|
|
|
QWidget* createControlPanel();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 创建文件操作组
|
|
|
|
|
|
*/
|
|
|
|
|
|
QGroupBox* createFileGroup();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 创建选点测距组
|
|
|
|
|
|
*/
|
|
|
|
|
|
QGroupBox* createMeasureGroup();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 创建选点测距页面
|
|
|
|
|
|
*/
|
|
|
|
|
|
QWidget* createMeasurePage();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 创建选线页面
|
|
|
|
|
|
*/
|
|
|
|
|
|
QWidget* createLinePage();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 创建选线拟合组
|
|
|
|
|
|
*/
|
|
|
|
|
|
QGroupBox* createLineGroup();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 创建点云列表组
|
|
|
|
|
|
*/
|
|
|
|
|
|
QGroupBox* createCloudListGroup();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 更新选点信息显示
|
|
|
|
|
|
*/
|
2026-08-08 17:41:02 +08:00
|
|
|
|
void updateSelectedPointsDisplay();
|
|
|
|
|
|
void clearMeasureResultDisplay();
|
|
|
|
|
|
void updateMeasureResultDisplay(const SelectedPointInfo& p1,
|
|
|
|
|
|
const SelectedPointInfo& p2,
|
|
|
|
|
|
float distance,
|
|
|
|
|
|
const QString& statusPrefix = QString());
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-02-19 00:45:00 +08:00
|
|
|
|
* @brief 从原始数据获取线上点(包含0,0,0)
|
|
|
|
|
|
*/
|
|
|
|
|
|
QVector<QVector3D> getOriginalLinePoints(const SelectedLineInfo& lineInfo);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 更新线上点对话框内容
|
|
|
|
|
|
*/
|
|
|
|
|
|
void updateLinePointsDialog();
|
|
|
|
|
|
|
2026-02-22 16:54:45 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 加载点云文件
|
|
|
|
|
|
* @return 是否成功加载到点云数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool loadPointCloudFile(const QString& fileName);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 加载线段文件(Poly格式)
|
|
|
|
|
|
* @return 是否成功加载到线段数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool loadSegmentFile(const QString& fileName);
|
|
|
|
|
|
|
2026-03-11 23:39:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 加载包围盒JSON文件
|
|
|
|
|
|
* @return 是否成功加载到包围盒数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool loadBoundingBoxFile(const QString& fileName);
|
2026-08-08 17:41:02 +08:00
|
|
|
|
void applyTransformToAllClouds(const QMatrix4x4& matrix);
|
|
|
|
|
|
void addGeneratedCloud(const PointCloudXYZ& cloud, const QString& name);
|
|
|
|
|
|
void showBasicShape(const QString& shapeName,
|
|
|
|
|
|
const QVector<LineSegment>& segments,
|
|
|
|
|
|
const QVector<BasicShapePoint>& points = QVector<BasicShapePoint>());
|
|
|
|
|
|
void showBasicSurface(const QString& shapeName, const QVector<SurfaceQuad>& surfaces, const QVector<LineSegment>& outlines);
|
2026-03-11 23:39:28 +08:00
|
|
|
|
|
2026-02-19 00:45:00 +08:00
|
|
|
|
// 点云显示控件
|
|
|
|
|
|
PointCloudGLWidget* m_glWidget;
|
|
|
|
|
|
|
|
|
|
|
|
// 点云转换器
|
|
|
|
|
|
std::unique_ptr<PointCloudConverter> m_converter;
|
|
|
|
|
|
|
|
|
|
|
|
// 文件操作控件
|
|
|
|
|
|
QPushButton* m_btnOpenFile;
|
2026-03-11 23:39:28 +08:00
|
|
|
|
QPushButton* m_btnOpenBBox;
|
2026-02-19 00:45:00 +08:00
|
|
|
|
QPushButton* m_btnClearAll;
|
|
|
|
|
|
|
|
|
|
|
|
// 选点测距控件
|
|
|
|
|
|
QCheckBox* m_cbMeasureDistance;
|
|
|
|
|
|
QPushButton* m_btnClearPoints;
|
2026-08-08 17:41:02 +08:00
|
|
|
|
QLabel* m_lblPoint1;
|
|
|
|
|
|
QLabel* m_lblPoint2;
|
|
|
|
|
|
QLabel* m_lblDistance;
|
|
|
|
|
|
QLabel* m_lblDeltaXYZ;
|
2026-02-19 00:45:00 +08:00
|
|
|
|
|
2026-03-01 18:14:19 +08:00
|
|
|
|
// 点1坐标编辑控件
|
|
|
|
|
|
QLineEdit* m_editPoint1X;
|
|
|
|
|
|
QLineEdit* m_editPoint1Y;
|
|
|
|
|
|
QLineEdit* m_editPoint1Z;
|
|
|
|
|
|
|
|
|
|
|
|
// 点2坐标编辑控件
|
|
|
|
|
|
QLineEdit* m_editPoint2X;
|
|
|
|
|
|
QLineEdit* m_editPoint2Y;
|
|
|
|
|
|
QLineEdit* m_editPoint2Z;
|
|
|
|
|
|
|
2026-02-19 00:45:00 +08:00
|
|
|
|
// 点1姿态输入控件
|
|
|
|
|
|
QLineEdit* m_editRx1;
|
|
|
|
|
|
QLineEdit* m_editRy1;
|
|
|
|
|
|
QLineEdit* m_editRz1;
|
|
|
|
|
|
QPushButton* m_btnShowPose1;
|
|
|
|
|
|
|
|
|
|
|
|
// 点2姿态输入控件
|
|
|
|
|
|
QLineEdit* m_editRx2;
|
|
|
|
|
|
QLineEdit* m_editRy2;
|
|
|
|
|
|
QLineEdit* m_editRz2;
|
|
|
|
|
|
QPushButton* m_btnShowPose2;
|
|
|
|
|
|
|
2026-03-29 22:21:10 +08:00
|
|
|
|
// 姿态坐标轴缩放
|
|
|
|
|
|
QLineEdit* m_editPoseScale;
|
|
|
|
|
|
|
2026-02-19 00:45:00 +08:00
|
|
|
|
// 欧拉角旋转顺序选择
|
|
|
|
|
|
QComboBox* m_comboEulerOrder;
|
|
|
|
|
|
|
|
|
|
|
|
// 选线拟合控件
|
|
|
|
|
|
QPushButton* m_btnClearLine;
|
|
|
|
|
|
QPushButton* m_btnShowLinePoints;
|
|
|
|
|
|
QLineEdit* m_lineNumberInput;
|
|
|
|
|
|
QPushButton* m_btnSelectByNumber;
|
|
|
|
|
|
QRadioButton* m_rbVertical;
|
|
|
|
|
|
QRadioButton* m_rbHorizontal;
|
|
|
|
|
|
QLabel* m_lblLineIndex;
|
|
|
|
|
|
QLabel* m_lblLinePointCount;
|
|
|
|
|
|
|
|
|
|
|
|
// 输入线段控件
|
|
|
|
|
|
QLineEdit* m_editLineX1;
|
|
|
|
|
|
QLineEdit* m_editLineY1;
|
|
|
|
|
|
QLineEdit* m_editLineZ1;
|
|
|
|
|
|
QLineEdit* m_editLineX2;
|
|
|
|
|
|
QLineEdit* m_editLineY2;
|
|
|
|
|
|
QLineEdit* m_editLineZ2;
|
|
|
|
|
|
QPushButton* m_btnShowLine;
|
|
|
|
|
|
QPushButton* m_btnClearLine2;
|
|
|
|
|
|
|
|
|
|
|
|
// 点云列表
|
|
|
|
|
|
QListWidget* m_cloudList;
|
|
|
|
|
|
|
|
|
|
|
|
// 已加载的点云数量
|
|
|
|
|
|
int m_cloudCount;
|
|
|
|
|
|
|
|
|
|
|
|
// 当前点云的线信息(用于旋转)
|
|
|
|
|
|
int m_currentLineNum;
|
|
|
|
|
|
int m_currentLinePtNum;
|
|
|
|
|
|
|
|
|
|
|
|
// 原始完整点云数据(包含0,0,0点,用于旋转)
|
|
|
|
|
|
PointCloudXYZ m_originalCloud;
|
|
|
|
|
|
|
|
|
|
|
|
// 线上点对话框
|
|
|
|
|
|
QDialog* m_linePointsDialog;
|
|
|
|
|
|
QTableWidget* m_linePointsTable;
|
|
|
|
|
|
QVector<QVector3D> m_currentLinePoints; // 当前线的原始点坐标
|
|
|
|
|
|
|
2026-08-08 17:41:02 +08:00
|
|
|
|
// 启动参数序列显示
|
|
|
|
|
|
QVector<StartupItem> m_startupSequence;
|
|
|
|
|
|
int m_startupSequenceIndex;
|
|
|
|
|
|
bool m_startupSequentialMode;
|
|
|
|
|
|
};
|
2026-02-19 00:45:00 +08:00
|
|
|
|
|
|
|
|
|
|
#endif // CLOUD_VIEW_MAIN_WINDOW_H
|