VS2019+QT+VTK 9.1 最新正确结合方法

我一直在用VTK开发,最近因为要用QT做界面,就需要将VTK和QT结合用起来,简单说目的就是将VTK的渲染窗口放在QT的控件下显示。我花了几天时间,看了网上很多教程,因为我本身不熟悉QT开发,需要边用边学,所以我还是没法根据网上教程实现我的目的,失败原因都是网上教程过时了(教程的VTK版本是8.2比较多,我用的是9.1,蛮多差别的),或者有门槛,讲不清楚(不写清楚ui中的qvtkwidget怎么来的),我也看不明白。所以踩了很多坑后终于摸索出正确的方法,其实很简单,大佬应该看不上眼,这里分享给同为不熟悉QT的朋友。

效果图:
QT+VTK


标题1,2是百度常搜到的两个坑,正确方法可以直接看5和6

PS:5是在Designer中使用VTK窗口的方法,6是无需ui文件直接在代码中使用VTK窗口的方法


1.在QT Designer中加入QVTKWidgetPlugin(血坑方法,网上看到马上避开)

最重要差别是8.2版本中在编译时会提供是否生成QT Designer的QVTKWidget插件,如下图:
在这里插入图片描述
请添加图片描述
8.2版本可以生成一个QVTKWidgetPlugin加入到QT Designer中,就能够多出一个QVTKWidget的窗口控件,可以拖拽它放进主窗口里面,然后在代码里通过访问ui的成员变量就能获取这个窗口控件,然后调用SetRenderWindow函数,把VTK的RenderWindow变量传入就能实现QT中显示VTK的渲染窗口。

但是VTK从9.0版本后在Cmake编译时不再提供生成QVTKWidgetPlugin插件的选项,因此这个方法属于过期方法,对使用新版本VTK的你来说,见到这种方法的教程可以直接关掉了。

我试过编译VTK 8.2生成这个插件并加入到QT Designer中,然后用9.1版本去使用这个控件,结果是会报错,因为VTK 9.1后不再包含“QVTKWidget.h”。

其实说白了,QVTKWidgetPlugin就是QT的“自定义控件”,8.2版本中提供这玩意只是为了方便用户,免去了自己手动去添加“自定义控件”这个操作,而9.1版本中不提供意味着你需要自己去完成这一步。因此解决方法其实就是学会添加“自定义控件”。具体教程在下面标题3中


2.将QTDesigner中的QWidget提升为QVTKWidget(血坑描述,网上看到马上避开,正确方法描述转看5)

很多比较新的教程写着在QT Designer中将QWidget提升为QVTKWidget。我在左边的Widget Box一栏也没找到QWidget,只找到OpenGL Widget和QQuickWidget这些。唯一找到的QWidget就只有对象查看器中的centralWidget是Qwidget类,我根据教程将它提升为QVTKWidget后(如下图,头文件使用QVTKOpenGLNativeWidget.h)好像也不起作用(指在代码里使用ui对象没法像教程那样访问qvtkwidget这个变量),也不知道是不是我没有往QT项目的.pro文件中配置VTK的include路径和lib路径,关键我用VS2019创建的QT项目,没有.pro文件(这个文件需要用QT Creator来创建项目并选择qmake编译才会出现)。然后我在VS2019里的QT项目属性里project setting中加入vtk的include和lib路径似乎也没什么效果,所以怎么才能起效真是没弄懂。
在这里插入图片描述


3.QT中加入VTK 9.1的正确方法(该方法要修改配置文件,很蠢,建议看5和6)

1.在VS2019中正确配置好VTK和QT环境。(判断是否正确:能够在VS2019中单独使用VTK和QT生成各自的窗口)。

2.用文本文档的方式打开你的项目中 “.ui” 后缀文件,这个文件时你的界面配置文件。

3.找到以下这一行,这个是你的主窗口

 <widget class="QWidget" name="centralWidget">

然后在下面先空一格,然后输入下面这行:代表你在主窗口下加入一个QVTKOpenGLNativeWidget控件,这个控件名字是qvtkWidget,你在访问ui的变量时可以访问到qvtkWidget。

<widget class="QVTKOpenGLNativeWidget" name="qvtkWidget" native="true">

连起来看就是:

<widget class="QWidget" name="centralWidget">
   <widget class="QVTKOpenGLNativeWidget" name="qvtkWidget" native="true">
    <property name="geometry">
     <rect>
      <x>9</x>
      <y>9</y>
      <width>221</width>
      <height>241</height>
     </rect>
    </property>
   </widget>  //注意记得在这里也加入这一行 </widget>
  </widget>

其实可以直接复制粘贴我上面的代码。

4.声明自定义控件,在文件末 "resources/"前加入如下代码:

 <customwidgets>
  <customwidget>
   <class>QVTKOpenGLNativeWidget</class>
   <extends>QWidget</extends>
   <header>QVTKOpenGLNativeWidget.h</header>
  </customwidget>
 </customwidgets>
 <resources/>   //注意是在这一行前加入以上代码
 <connections/>
</ui>

customwidget 就是声明自定义控件,然后这个控件类是QVTKOpenGLNativeWidget,对应上面步骤3中的class名字,extends于QWidget(基类是QWidget),最重要是header这个字段,要填QVTKOpenGLNativeWidget.h,对应VTK提供的头文件名字。

最后再编译一下项目,就能够生成QVTKOpenGLNativeWidget这个控件。成功的前提是正确配置了VTK,否则会报错提示缺失QVTKOpenGLNativeWidget.h。同时自动生成的“ui_***.h”文件中也会出现qvtkwidget这个变量。

如下图:
在这里插入图片描述
意味着,可以在QMainWindow中,调用ui->qvtkwidget->setRenderWindow(VTK的vtkGenericOpenGLRenderWindow),把VTK的窗口绑定到QT的qvtkwidget控件中。

总结:
简单地说就是往QT项目的UI配置文件中,利用VTK提供的QVTKOpenGLNativeWidget.h文件来配置一个自定义控件(customwidget ),然后再往主窗口控件(centralWidget)下声明一个你刚配置好的自定义控件即可。


4.最基本的方法,无需对配置文件做任何修改(最基本的使用,功能有限,当HelloWorld使用,建议看5和6)

最基本的方法就是正确配置好VTK和QT后直接输入代码实现(但是功能有限),代码来自官方示例:

#include <QApplication>

#include <vtkActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkSphereSource.h>
#include <vtkVersion.h>

#if VTK_VERSION_NUMBER >= 89000000000ULL
#define VTK890 1
#endif

#include <QSurfaceFormat>
#include <QVTKOpenGLNativeWidget.h>

int main(int argc, char** argv)
{
  // Needed to ensure appropriate OpenGL context is created for VTK rendering.
  QSurfaceFormat::setDefaultFormat(QVTKOpenGLNativeWidget::defaultFormat());

  QApplication app(argc, argv);

  QVTKOpenGLNativeWidget widget;

  vtkNew<vtkNamedColors> colors;

  vtkNew<vtkGenericOpenGLRenderWindow> renderWindow;
#if VTK890
  widget.setRenderWindow(renderWindow);
#else
  widget.SetRenderWindow(renderWindow);
#endif

  widget.resize(600, 600);

  vtkNew<vtkSphereSource> sphereSource;

  vtkNew<vtkPolyDataMapper> sphereMapper;
  sphereMapper->SetInputConnection(sphereSource->GetOutputPort());

  vtkNew<vtkActor> sphereActor;
  sphereActor->SetMapper(sphereMapper);
  sphereActor->GetProperty()->SetColor(colors->GetColor4d("Tomato").GetData());

  vtkNew<vtkRenderer> renderer;
  renderer->AddActor(sphereActor);
  renderer->SetBackground(colors->GetColor3d("SteelBlue").GetData());

#if VTK890
  widget.renderWindow()->AddRenderer(renderer);
  widget.renderWindow()->SetWindowName("RenderWindowNoUIFile");
#else
  widget.GetRenderWindow()->AddRenderer(renderer);
  widget.GetRenderWindow()->SetWindowName("RenderWindowNoUIFile");
#endif
  widget.show();

  app.exec();

  return EXIT_SUCCESS;
}

这个方法甚至不需要UI文件都可以,就是将 QVTKOpenGLNativeWidget 视作QT应用的主窗口直接用,但是这样功能有限。


5.QT中加入VTK的最正确方法(该方法是在Designer中使用VTK的正确方法)

这个应该是最正确的方法了。方法的核心就是将QWidget提升为自定义控件

QVTKOpenGLNativeWidget 实际上是VTK给写好的一个自定义控件,继承自QWidget类,所以只需要将QWdiget类提升为QVTKOpenGLNativeWidget类就行了

1.用Designer打开项目的界面文件(.ui后缀文件)

2.在左边的控件栏中,将Containers下的“Widget”这个控件拖入到用户界面中,然后右边的对象查看器中就能查到你拖入的QWidget控件对象。
在这里插入图片描述
在这里插入图片描述

3.对该对象点右键,选择“提升为”,提升为可以理解是将QT自带的控件类提升为开发者编写的自定义控件类。而QVTKOpenGLNativeWidget就是VTK编写的自定义控件类。
在这里插入图片描述

4.然后提升的类名称填上“QVTKOpenGLNativeWidget”,头文件填写“QVTKOpenGLNativeWidget.h”。基类选择QWidget。然后点击“添加”-》“提升
在这里插入图片描述
提升的类名称:指自定义控件的类名称,VTK提供的自定义类名称就是QVTKOpenGLNativeWidget。
头文件:指自定义控件的头文件,VTK提供的自定义控件类的头文件就是QVTKOpenGLNativeWidget.h。注意大小写。如果不对应的话,会失效。
基类名称:指自定义的控件类的基类是谁,QVTKOpenGLNativeWidget的基类就是QWidget。选错了也是不行的。
添加:指保存这个设定,下次可以直接拿来使用,不用重新输入以上信息。
提升:指将你拖入UI中的QWidget控件,变成你写好的自定义控件类,在这里就是将QWidget类变成QVTKOpenGLNativeWidget类。

5.重命名该窗口控件的对象名,用来写代码的时候识别。网上所见通常命名为“qvtkWidget”
在这里插入图片描述
6.最后在你的主窗口类下调用ui的成员变量可以成功呼出qvtkWidget这个vtk窗口控件。
在这里插入图片描述
实际上以上就是自定义控件类的定义过程,区别是该自定义控件类不需要自己写一个类,而是VTK已经写好提供给我们,我们直接拿来提升就行了。


6.最方便方法,不使用Designer,直接用代码实现加入VTK窗口的方式(最推荐方法)

这是最简单的方法了,不要用到.ui文件,不需要用到Designer,直接在代码上用。都不需要提升类,直接包含头文件后拿来用就行。

在MainWindow的构造函数中写代码:(不需要照抄,理解以下几行代码在干嘛即可)

	//初始化VTK窗口,命名为qvtkWidget(原理和QPushButton这种QT自带的控件一样,只是该控件由VTK提供,用法跟QPushButton是一样的)
	QVTKOpenGLNativeWidget* qvtkWidget = new QVTKOpenGLNativeWidget();
	//初始化VTK的渲染器,平时用的比较多是vtkRenderWindow,但是在QT中要改用vtkGenericOpenGLRenderWindow,实质上与vtkRenderWindow功能一致
	vtkGenericOpenGLRenderWindow* renderWindow = vtkGenericOpenGLRenderWindow()::New();
	//将渲染器加入到VTK窗口中。可以先写这一行,后续再将准备好的vtkRenderer加入到renderWindow中也是可以同步数据的
	qvtkWidget->setRenderWindow(renderWindow);
	//创建网格布局,只是为了方便布局,实际上可以直接调用qvtkwidget的baseSize函数指定窗口大小也行
	QGridLayout* displayGrid = new QGridLayout(this);
	displayGrid->addWidget(qvtkWidget);
	this->setLayout(displayGrid);
  1. 需要在MainWindow头文件中包含vtkGenericOpenGLRenderWindow.h和QVTKOpenGLNativeWidget.h头文件。
  2. 理解上面的代码,自己在MainWindow构造函数中去使用(在其他地方用也没问题,用法一样)
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐