VTK开发笔记(二):Qt5.9.3+VS2017x64+VTK8.2创建兼容dll和嵌入源码窗口两种方式的Qt嵌入VTK8.2模板Demo

前言

前面编译好了Qt5.9.3+VS2017x64+VTK8.2的开发环境,但是Qt结合VTK没有。

本篇Qt融合VTK,搭建基础Qt界面搭载VTK显示引擎的模板Demo。

Demo

QVTKWidget

QVTKWidget+MyVTKWidget

注意

这是上一篇install的结果:

建立模板工程(QVTKWidget.dll方式)

步骤一:建立vtkDemo工程

步骤二:模块化

文件夹modules,老规矩:

建立模块文件夹:

建立vtkWIdget.pri

工程包含pri:

建立VTKWidget界面类,方便提升:

步骤三:引入vtk的库和开发环境

先把install复制过来,标记好qt和msvc版本,为了以后多版本vtk预留(能就别少,作为代码模块集合):

添加进pri:

直接从文件夹复制进入pri,会是路径,再修改:

cpp 复制代码
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD


HEADERS += \
    $$PWD/VTKWidget.h SOURCES += \ $$PWD/VTKWidget.cpp FORMS += \ $$PWD/VTKWidget.ui # vtk库包含 INCLUDEPATH += $$PWD/vtk-8.2.0-msvc2015x64-install/include \ $$PWD/vtk-8.2.0-msvc2015x64-install/include/vtk-8.2 LIBS += -L$$PWD/vtk-8.2.0-msvc2015x64-install/lib LIBS += -lvtkzlib-8.2 \ -lvtkChartsCore-8.2 \ -lvtkCommonColor-8.2 \ -lvtkCommonComputationalGeometry-8.2 \ -lvtkCommonCore-8.2 \ -lvtkCommonDataModel-8.2 \ -lvtkCommonExecutionModel-8.2 \ -lvtkCommonMath-8.2 \ -lvtkCommonMisc-8.2 \ -lvtkCommonSystem-8.2 \ -lvtkCommonTransforms-8.2 \ -lvtkDICOMParser-8.2 \ -lvtkDomainsChemistry-8.2 \ -lvtkDomainsChemistryOpenGL2-8.2 \ -lvtkdoubleconversion-8.2 \ -lvtkexodusII-8.2 \ -lvtkexpat-8.2 \ -lvtkFiltersAMR-8.2 \ -lvtkFiltersCore-8.2 \ -lvtkFiltersExtraction-8.2 \ -lvtkFiltersFlowPaths-8.2 \ -lvtkFiltersGeneral-8.2 \ -lvtkFiltersGeneric-8.2 \ -lvtkFiltersGeometry-8.2 \ -lvtkFiltersHybrid-8.2 \ -lvtkFiltersHyperTree-8.2 \ -lvtkFiltersImaging-8.2 \ -lvtkFiltersModeling-8.2 \ -lvtkFiltersParallel-8.2 \ -lvtkFiltersParallelImaging-8.2 \ -lvtkFiltersPoints-8.2 \ -lvtkFiltersProgrammable-8.2 \ -lvtkFiltersSelection-8.2 \ -lvtkFiltersSMP-8.2 \ -lvtkFiltersSources-8.2 \ -lvtkFiltersStatistics-8.2 \ -lvtkFiltersTexture-8.2 \ -lvtkFiltersTopology-8.2 \ -lvtkFiltersVerdict-8.2 \ -lvtkfreetype-8.2 \ -lvtkGeovisCore-8.2 \ -lvtkgl2ps-8.2 \ -lvtkglew-8.2 \ -lvtkGUISupportQt-8.2 \ -lvtkGUISupportQtSQL-8.2 \ -lvtkhdf5_hl-8.2 \ -lvtkhdf5-8.2 \ -lvtkImagingColor-8.2 \ -lvtkImagingCore-8.2 \ -lvtkImagingFourier-8.2 \ -lvtkImagingGeneral-8.2 \ -lvtkImagingHybrid-8.2 \ -lvtkImagingMath-8.2 \ -lvtkImagingMorphological-8.2 \ -lvtkImagingSources-8.2 \ -lvtkImagingStatistics-8.2 \ -lvtkImagingStencil-8.2 \ -lvtkInfovisCore-8.2 \ -lvtkInfovisLayout-8.2 \ -lvtkInteractionImage-8.2 \ -lvtkInteractionStyle-8.2 \ -lvtkInteractionWidgets-8.2 \ -lvtkIOAMR-8.2 \ -lvtkIOAsynchronous-8.2 \ -lvtkIOCityGML-8.2 \ -lvtkIOCore-8.2 \ -lvtkIOEnSight-8.2 \ -lvtkIOExodus-8.2 \ -lvtkIOExport-8.2 \ -lvtkIOExportOpenGL2-8.2 \ -lvtkIOExportPDF-8.2 \ -lvtkIOGeometry-8.2 \ -lvtkIOImage-8.2 \ -lvtkIOImport-8.2 \ -lvtkIOInfovis-8.2 \ -lvtkIOLegacy-8.2 \ -lvtkIOLSDyna-8.2 \ -lvtkIOMINC-8.2 \ -lvtkIOMovie-8.2 \ -lvtkIONetCDF-8.2 \ -lvtkIOParallel-8.2 \ -lvtkIOParallelXML-8.2 \ -lvtkIOPLY-8.2 \ -lvtkIOSegY-8.2 \ -lvtkIOSQL-8.2 \ -lvtkIOTecplotTable-8.2 \ -lvtkIOVeraOut-8.2 \ -lvtkIOVideo-8.2 \ -lvtkIOXML-8.2 \ -lvtkIOXMLParser-8.2 \ -lvtkjpeg-8.2 \ -lvtkjsoncpp-8.2 \ -lvtklibharu-8.2 \ -lvtklibxml2-8.2 \ -lvtkLocalExample-8.2 \ -lvtklz4-8.2 \ -lvtklzma-8.2 \ -lvtkmetaio-8.2 \ -lvtkNetCDF-8.2 \ -lvtkogg-8.2 \ -lvtkParallelCore-8.2 \ -lvtkpng-8.2 \ -lvtkproj-8.2 \ -lvtkpugixml-8.2 \ -lvtkRenderingAnnotation-8.2 \ -lvtkRenderingContext2D-8.2 \ -lvtkRenderingContextOpenGL2-8.2 \ -lvtkRenderingCore-8.2 \ -lvtkRenderingFreeType-8.2 \ -lvtkRenderingGL2PSOpenGL2-8.2 \ -lvtkRenderingImage-8.2 \ -lvtkRenderingLabel-8.2 \ -lvtkRenderingLOD-8.2 \ -lvtkRenderingOpenGL2-8.2 \ -lvtkRenderingQt-8.2 \ -lvtkRenderingVolume-8.2 \ -lvtkRenderingVolumeOpenGL2-8.2 \ -lvtksqlite-8.2 \ -lvtksys-8.2 \ -lvtktheora-8.2 \ -lvtktiff-8.2 \ -lvtkverdict-8.2 \ -lvtkViewsContext2D-8.2 \ -lvtkViewsCore-8.2 \ -lvtkViewsGeovis-8.2 \ -lvtkViewsInfovis-8.2 \ -lvtkViewsQt-8.2 # win平台的复制dll win32{ # copy vtk bin src_file = $$PWD/vtk-8.2.0-msvc2015x64-install/bin/* dst_file = $$OUT_PWD target_file = $$DESTDIR src_file ~= s,/,\\,g dst_file ~= s,/,\\,g target_file ~= s,/,\\,g system(xcopy $$src_file $$dst_file /y /s/q/e) system(xcopy $$src_file $$target_file /y /s/q/e) } 

至此,运行环境引入好了。

步骤四:vkt引擎融入Qt界面

这里我们是直接通过QVTKWidget.h头文件和其lib、dll的方式引入:

建立模板工程(MyVTKWidget.cpp方式)

为了最大化兼容代码,这里QVTKWidget.dll就不动了,QVTKWidget源码里面实际是跟osgWidget一样,映射了窗口对于渲染器的操作,这里我们可以直接拿过来改个名字即可,所以先找到QVTKWidget.h和QVTKWIdget.cpp的源码,改成MyVTKWidget.h,MyVTKWidget.cpp:

复制改名:

加入源码:

修改里面的类名为MyVTKWidget,注意修改头文件宏:

编译通过,修改加入之后,出现一些错误:

这是因为代码默认定义了宏是导出dll的,所以需要修改,先去掉类的导出dll宏,然后编译,出错如下:

检查:

点开查看:

是因为这个类在这个库里面压根没有导出来cpp。

现在直接反过来去添加导出,修改源码(测试加头文件定义宏也报错,直接加入导出的代码吧):

然后重新编译:

安装并将库引过去替换试一试:

再次编译,解决了库定义的问题,还剩一个问题:

还是往回退到VS2017去搜索,搜索不到,这就是windows的函数,连接到对应的库就好了:

编译成功,运行失败:

怀疑是没包含dll,下面直接exe进行探测:

先补完qt的库:

然后点击运行:

是因为更新了这个lib,但是没有更新对应的dll过来:

Ok了,运行起来了,使用修改 源码大法可以 搞定,这样以后做其他的对映射操作就很方便了:

运行起来一个黑色一个白色:

去掉父类,测试左侧这种就是白色的。

为了确保可靠性,咱们还需要确认下,黑色和白色是否都是正常的,黑色肯定是正常的,写个常规代码:

跑起来后:

怀疑是哪个地方没有跑,导致初始化没有黑色,但是有场景的时候不影响。

建立模板工程(MyVTKWidget.cpp方式,失败,仅供参考)

为了最大化兼容代码,这里QVTKWidget.dll就不动了,QVTKWidget源码里面实际是跟osgWidget一样,映射了窗口对于渲染器的操作,这里我们可以直接拿过来改个名字即可,所以先找到QVTKWidget.h和QVTKWIdget.cpp的源码,改成MyVTKWidget.h,MyVTKWidget.cpp:

复制改名:

加入源码:

修改里面的类名为MyVTKWidget,注意修改头文件宏:

编译通过,修改加入之后,出现一些错误:

这是因为代码默认定义了宏是导出dll的,所以需要修改,先去掉类的导出dll宏,然后编译,出错如下:

检查是需要包含win:

继续编译:

#error: "No Target Architecture"

直接返回vs2017编译源码,查找编译的时候定义了哪些宏:

cpp 复制代码
WIN32
_WINDOWS
NDEBUG
QT_NO_DEBUG
_CRT_SECURE_NO_DEPRECATE
_CRT_NONSTDC_NO_DEPRECATE
_CRT_SECURE_NO_WARNINGS
_SCL_SECURE_NO_DEPRECATE
_SCL_SECURE_NO_WARNINGS
VTK_IN_VTK
QT_WIDGETS_LIB
QT_GUI_LIB
QT_CORE_LIB
QT_UIPLUGIN_LIB
CMAKE_INTDIR="Release"
QVTKWidgetPlugin_EXPORTS

此方式行不通!!!

换个方式,退回去

注意

对比下osg和vtk的库大小:

源码

VTKWidget.h

cpp 复制代码
#ifndef VTKWIDGET_H
#define VTKWIDGET_H #include <QWidget> #include "QVTKWidget.h" #include "MyVTKWidget.h" #include "vtkSphereSource.h" #include "vtkPolyDataMapper.h" #include "vtkActor.h" #include "vtkProperty.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkInteractorStyleTrackballCamera.h" #include "vtkOutputWindow.h" #include "vtkAutoInit.h" //VTK_MODULE_INIT(vtkRenderingOpenGL); VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle); namespace Ui { class VTKWidget; } class VTKWidget : public QWidget { Q_OBJECT public: explicit VTKWidget(QWidget *parent = 0); ~VTKWidget(); protected: void initControl(); protected: void createSphere(QVTKWidget *pVTKWidget); // 临时代码,忽略代码规则 void createSphere(MyVTKWidget *pVTKWidget); // 临时代码,忽略代码规则 protected: void resizeEvent(QResizeEvent *event); private: Ui::VTKWidget *ui; private: QVTKWidget *_pQVTKWidget; MyVTKWidget *_pMyVTKWidget; }; #endif // VTKWIDGET_H 

VTKWidget.cpp

cpp 复制代码
#include "VTKWidget.h"
#include "ui_VTKWidget.h" #include <QDebug> #include <QDateTime> //#define LOG qDebug()<<__FILE__<<__LINE__ //#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__ //#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread() //#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd") #define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz") // QtCreator在msvc下设置编码也或有一些乱码,直接一刀切,避免繁琐的设置 #define MSVC #ifdef MSVC #define QSTRING(s) QString::fromLocal8Bit(s) #else #define QSTRING(s) QString(s) #endif VTKWidget::VTKWidget(QWidget *parent) : QWidget(parent), ui(new Ui::VTKWidget), _pQVTKWidget(0), _pMyVTKWidget(0) { ui->setupUi(this); initControl(); } VTKWidget::~VTKWidget() { delete ui; } void VTKWidget::initControl() { // 下面二选一,注意:这里必须在new之前设置好,new QVTKWidget窗口之后是无效的 vtkOutputWindow::GlobalWarningDisplayOff(); // 两段代码效果应该一致 vtkOutputWindow::SetGlobalWarningDisplay(0); // 设置消息登记显示,0-都不显示 _pQVTKWidget = new QVTKWidget(this); createSphere(_pQVTKWidget); _pMyVTKWidget = new MyVTKWidget(this); createSphere(_pMyVTKWidget); } void VTKWidget::createSphere(QVTKWidget *pVTKWidget) { // 1. 创建球体数据 vtkSphereSource* sphere = vtkSphereSource::New(); sphere->SetRadius(1.0); // 球体半径 sphere->SetThetaResolution(30); // 纬线方向分辨率 sphere->SetPhiResolution(30); // 经线方向分辨率 // 2. 创建映射器(连接数据与渲染) vtkPolyDataMapper* mapper = vtkPolyDataMapper::New(); mapper->SetInputConnection(sphere->GetOutputPort()); // 3. 创建演员(设置显示属性) vtkActor* actor = vtkActor::New(); actor->SetMapper(mapper); actor->GetProperty()->SetColor(0.3, 0.6, 0.9); // 蓝色球体 // 4. 创建渲染器并添加球体 vtkRenderer* renderer = vtkRenderer::New(); renderer->AddActor(actor); renderer->SetBackground(0.1, 0.1, 0.1); // 深灰色背景 // 5. 关联到QVTKWidget的渲染窗口 pVTKWidget->GetRenderWindow()->AddRenderer(renderer); // 6. 设置交互(支持鼠标旋转、缩放) vtkRenderWindowInteractor* interactor = vtkRenderWindowInteractor::New(); interactor->SetRenderWindow(pVTKWidget->GetRenderWindow()); interactor->SetInteractorStyle(vtkInteractorStyleTrackballCamera::New()); interactor->Initialize(); } void VTKWidget::createSphere(MyVTKWidget *pVTKWidget) { // 1. 创建球体数据 vtkSphereSource* sphere = vtkSphereSource::New(); sphere->SetRadius(1.0); // 球体半径 sphere->SetThetaResolution(30); // 纬线方向分辨率 sphere->SetPhiResolution(30); // 经线方向分辨率 // 2. 创建映射器(连接数据与渲染) vtkPolyDataMapper* mapper = vtkPolyDataMapper::New(); mapper->SetInputConnection(sphere->GetOutputPort()); // 3. 创建演员(设置显示属性) vtkActor* actor = vtkActor::New(); actor->SetMapper(mapper); actor->GetProperty()->SetColor(0.3, 0.6, 0.9); // 蓝色球体 // 4. 创建渲染器并添加球体 vtkRenderer* renderer = vtkRenderer::New(); renderer->AddActor(actor); renderer->SetBackground(0.1, 0.1, 0.1); // 深灰色背景 // 5. 关联到QVTKWidget的渲染窗口 pVTKWidget->GetRenderWindow()->AddRenderer(renderer); // 6. 设置交互(支持鼠标旋转、缩放) vtkRenderWindowInteractor* interactor = vtkRenderWindowInteractor::New(); interactor->SetRenderWindow(pVTKWidget->GetRenderWindow()); interactor->SetInteractorStyle(vtkInteractorStyleTrackballCamera::New()); interactor->Initialize(); } void VTKWidget::resizeEvent(QResizeEvent *event) { if(_pQVTKWidget) { _pQVTKWidget->setGeometry(rect().x(), rect().y(), rect().width() / 2 - 10, rect().height()); } if(_pMyVTKWidget) { _pMyVTKWidget->setGeometry(rect().width() / 2 + 10, rect().y(), rect().width() / 2 - 10, rect().height()); } } 

工程模板v1.0.0

入坑

入坑一:运行渲染窗口弹出8.1remove

问题

出错点击关闭,提示截图图下:

尝试

添加头文件

初始化

然后结果:

调试窗口还是存在,关闭或者设置等级0,即可消息窗口输出即可。

解决

头文件

cpp 复制代码
#include "vtkAutoInit.h"

初始化

cpp 复制代码
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle); 

添加关闭打印信息输出或者设置输出等级

cpp 复制代码
// 下面二选一,注意:这里必须在new之前设置好,new QVTKWidget窗口之后是无效的
vtkOutputWindow::GlobalWarningDisplayOff(); // 两段代码效果应该一致 vtkOutputWindow::SetGlobalWarningDisplay(0); // 设置消息登记显示,0-都不显示