QT6 QML vtk学习之(1)点云在QML窗口中显示
QT6 QML vtk学习之(1)点云在QML窗口中显示
系统环境是ubantu22.04 QT6.6.3 opengl(这个使用apt安装dev版本就行)
需要编译一个VTK9.4(最新的就行)
QTcreator构建工程,新建一个QML工程
然后对cmakelists.txt做如下修改
cmake_minimum_required(VERSION 3.16)
project(vtk_qml_test VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Quick)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Quick)
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
find_package(Qt6 6.2 COMPONENTS Core Quick QuickControls2 Gui
Widgets Charts Concurrent Sql SerialBus Network
Core5Compat
REQUIRED
)
find_package(VTK
COMPONENTS
CommonCore
FiltersSources
GUISupportQtQuick)
qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(appvtk_qml_test
main.cpp
)
qt_add_qml_module(appvtk_qml_test
URI vtk_qml_test
VERSION 1.0
QML_FILES
Main.qml
SOURCES PFG3DVTKItem.h PFG3DVTKItem.cpp
)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(appvtk_qml_test PROPERTIES
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appvtk_qml_test
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
target_link_libraries(appvtk_qml_test
PUBLIC
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Quick
Qt6::QuickControls2
Qt6::Quick
Qt6::Gui
Qt6::Widgets
Qt6::Charts
Qt6::Concurrent
Qt6::Core5Compat
${VTK_LIBRARIES}
)
vtk_module_autoinit(
TARGETS ${PROJECT_NAME}
MODULES ${VTK_LIBRARIES}
)
include(GNUInstallDirs)
install(TARGETS appvtk_qml_test
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
然后是新建一个VTKItem类
需要注意的是,新建的类文件头字母必须为大写,不然会报错,然而QT新建文件默认是全小写
代码如下
#ifndef VTKITEM_H
#define VTKITEM_H
#include <QMetaObject>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QQuickVTKItem.h>
#include <QRandomGenerator>
#include <QVTKRenderWindowAdapter.h>
#include <vtkRenderWindow.h>
#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkOpenGLRenderWindow.h>
#include <vtkOpenGLState.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkPointData.h>
#include <QVTKInteractor.h>
class VTKItem : public QQuickVTKItem
{
Q_OBJECT
public:
VTKItem(QQuickItem* parent = nullptr);
~VTKItem();
vtkUserData initializeVTK(vtkRenderWindow* renderWindow) override;
void destroyingVTK(vtkRenderWindow *renderWindow, vtkUserData userData) override;
// Q_INVOKABLE void close(){
// m_cylinder->close();
// m_cylinder=nullptr;
// }
// Q_INVOKABLE void start(){
// if (m_renderWin && !m_cylinder) {
// m_cylinder = new RandomCylinderGenerator(m_renderWin);
// m_cylinder->generateRandomCylinderPoints();
// }
// }
// vtkRenderWindow* render(){
// return m_renderWin;
// }
void updatePoints(vtkSmartPointer<vtkPoints> points,
vtkSmartPointer<vtkUnsignedCharArray> colors);
void clearPointCloud();
Q_INVOKABLE void resetCamera(int view);
void SetView(vtkSmartPointer<vtkRenderer> renderer, const int& view);
// 缩放操作
Q_INVOKABLE void zoom(qreal scaleFactor) {
auto camera = m_renderer->GetActiveCamera();
camera->Zoom(scaleFactor); // 调整摄像机缩放
m_renderWin->Render(); // 刷新渲染窗口
}
// 新增的缩放方法
Q_INVOKABLE void zoom_in() {
zoom(1.1); // 放大
}
Q_INVOKABLE void zoom_out() {
zoom(0.9); // 缩小
}
private:
void Init();
public slots:
private:
vtkRenderWindow* m_renderWin;
vtkSmartPointer<vtkRenderer> m_renderer;
vtkSmartPointer<vtkPolyData> polyData;
// vtkSmartPointer<vtkUnsignedCharArray> colors;
bool m_flag=false;
// QQuickVTKItem interface
};
#endif
#include "VTKItem.h"
VTKItem::VTKItem(QQuickItem *parent)
: QQuickVTKItem(parent)
// , m_cylinder(nullptr)
{}
VTKItem::~VTKItem()
{
// if (m_cylinder) {
// delete m_cylinder;
// m_cylinder = nullptr;
// }
}
QQuickVTKItem::vtkUserData VTKItem::initializeVTK(vtkRenderWindow *renderWindow)
{
// qDebug()<<"initialize VTK";
m_renderWin = renderWindow;
Init();
return nullptr;
}
void VTKItem::destroyingVTK(vtkRenderWindow *renderWindow, vtkUserData userData) {}
// void PFG3DVTKItem::Update()
// {
// if (m_cylinder!=nullptr)
// {
// // qDebug()<<"toUpdate";
// m_cylinder->generatePoints();
// }
// }
void VTKItem::SetView(vtkSmartPointer<vtkRenderer> renderer, const int& view) {
vtkSmartPointer<vtkCamera> camera = renderer->GetActiveCamera();
if (view == 0) {
camera->SetPosition(0, 0, 1); // 从前面查看
camera->SetFocalPoint(0, 0, 0);
} else if (view == 1) {
camera->SetPosition(0, 0, -1); // 从后面查看
camera->SetFocalPoint(0, 0, 0);
} else if (view == 2) {
camera->SetPosition(-1, 0, 0); // 从左边查看
camera->SetFocalPoint(0, 0, 0);
} else if (view == 3) {
camera->SetPosition(1, 0, 0); // 从右边查看
camera->SetFocalPoint(0, 0, 0);
} else if (view == 4) {
camera->SetPosition(0, -1, 0); // 从上面查看
camera->SetFocalPoint(0, 0, 0);
} else if (view == 5) {
camera->SetPosition(0, 1, 0); // 从下面查看
camera->SetFocalPoint(0, 0, 0);
} else {
std::cerr << "Unknown view: " << view << std::endl;
return;
}
camera->SetViewUp(0, 1, 0); // 设置视图的上方向,视角的上下方向
renderer->ResetCamera(); // 重新设置相机
}
void VTKItem::resetCamera(int view)
{
SetView(m_renderer, view);
}
void VTKItem::updatePoints(vtkSmartPointer<vtkPoints> points,
vtkSmartPointer<vtkUnsignedCharArray> colors)
{
if(polyData==nullptr||m_renderWin==nullptr)
return;
polyData->SetPoints(points);
polyData->GetPointData()->SetScalars(colors);
polyData->Modified();
//TODO need reset button
if (!m_flag) {
// Reset the camera to view the entire point cloud
m_renderer->ResetCamera();
m_flag = true;
}
// Ensure the render window updates the renderer before resetting the camera
// m_renderer->GetRenderWindow()->Render();
m_renderWin->Render();
}
void VTKItem::clearPointCloud() {
if (!polyData) {
return;
}
auto points=vtkSmartPointer<vtkPoints>::New();
auto colors=vtkSmartPointer<vtkUnsignedCharArray>::New();
colors->SetNumberOfComponents(3);
colors->SetName("Colors");
colors->Reset();
points->Reset();
points->InsertNextPoint(0,0,0);
colors->InsertNextTuple3(255,255,255);
// Clear the points and reset the polyData
polyData->SetPoints(points);
polyData->GetPointData()->SetScalars(colors);
polyData->Modified(); // Mark the polyData as modified
// Clear the renderer
m_renderWin->Render(); // Request a render update
}
void VTKItem::Init()
{
// // 保存相机状态
// vtkSmartPointer<vtkCamera> camera = this->renderer->GetActiveCamera();
// double position[3];
// double focalPoint[3];
// double viewUp[3];
// camera->GetPosition(position);
// camera->GetFocalPoint(focalPoint);
// camera->GetViewUp(viewUp);
// this->renderer->RemoveAllViewProps();
// this->renderWindow->RemoveRenderer(this->renderer);
// this->renderer=nullptr;
auto renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(0, 0, 0);
m_renderer = renderer;
// 清除以前的演员
// this->renderer->RemoveAllViewProps();
// Create initial point cloud
auto pointCloud = vtkSmartPointer<vtkPoints>::New();
double diameter = 100;
int height = 200;
diameter = diameter + (QRandomGenerator64::global()->bounded(11) - 5); // Radius of the cylinder
const int numCircles = 100; // Number of circles along the height
const int numPointsPerCircle = 100; // Number of points per circle
const double radius = diameter / 2.0;
const double angleStep = 2.0 * vtkMath::Pi() / numPointsPerCircle;
const double heightStep = height / numCircles;
auto colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
colors->SetNumberOfComponents(3);
colors->SetName("Colors");
for (int i = 0; i < numCircles; ++i) {
double z = i * heightStep;
for (int j = 0; j < numPointsPerCircle; ++j) {
double angle = j * angleStep;
double x = radius * cos(angle);
double y = radius * sin(angle);
pointCloud->InsertNextPoint(x, y, z);
colors->InsertNextTuple3(192, 192, 192);
}
}
// 创建PolyData对象
polyData = vtkSmartPointer<vtkPolyData>::New();
polyData->SetPoints(pointCloud);
polyData->GetPointData()->SetScalars(colors);
// 创建顶点过滤器
vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
glyphFilter->SetInputData(polyData);
glyphFilter->Update();
// 创建映射器
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetColorModeToDefault();
mapper->SetScalarVisibility(1);
mapper->SetInputConnection(glyphFilter->GetOutputPort());
// 创建演员
auto actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
actor->GetProperty()->SetPointSize(3); // 设置点的大小
// 随机设置演员位置
// actor->SetPosition(static_cast<double>(rand()) / RAND_MAX * 20.0 - 10.0,
// static_cast<double>(rand()) / RAND_MAX * 20.0 - 10.0,
// static_cast<double>(rand()) / RAND_MAX * 20.0 - 10.0);
actor->SetPosition(0, 100, 0);
// 将演员添加到渲染器
renderer->AddActor(actor);
m_renderWin->AddRenderer(renderer);
m_renderWin->Render();
// qDebug()<<"Init 3DItem";
}
然后是main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "VTKItem.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<VTKItem>("TESTQml", 1, 0, "VTKItem");
QQmlApplicationEngine engine;
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreationFailed,
&app,
[]() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.loadFromModule("vtk_qml_test", "Main");
return app.exec();
}
然后是Main.qml
import QtQuick
import TESTQml
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
VTKItem {
id: testVTKItem
anchors.fill: parent
opacity: 0.7
}
}
代码里包含了一些缩放和角度设置,界面代码暂时没用到,后面代码会更新
运行结果
VTK编译可以参考
https://blog.csdn.net/caz28/article/details/131466946
opengl安装可以参考
sudo apt-get install build-essential libxmu-dev libxi-dev libgl-dev