Building the official minimal Qt VTK example with Qt6 uses Qt5 instead

I am trying to build the minimal Qt VTK app which is part of the official VTK examples (shown here). The C++ code for the app itself is mostly the same as specified via the link:

#include <QVTKOpenGLNativeWidget.h>
#include <vtkActor.h>
#include <vtkDataSetMapper.h>
#include <vtkDoubleArray.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkPointData.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkSphereSource.h>

#include <QApplication>
#include <QDebug>
#include <QDockWidget>
#include <QGridLayout>
#include <QLabel>
#include <QMainWindow>
#include <QPointer>
#include <QPushButton>
#include <QVBoxLayout>

#include <cmath>
#include <cstdlib>
#include <random>

namespace {
/**
 * Deform the sphere source using a random amplitude and modes and render it in
 * the window
 *
 * @param sphere the original sphere source
 * @param mapper the mapper for the scene
 * @param window the window to render to
 * @param randEng the random number generator engine
 */
void Randomize(vtkSphereSource* sphere, vtkDataSetMapper* mapper,
               vtkGenericOpenGLRenderWindow* window, std::mt19937& randEng);
} // namespace

int main(int argc, char* argv[])
{
  QSurfaceFormat::setDefaultFormat(QVTKOpenGLNativeWidget::defaultFormat());

  QApplication app(argc, argv);

  qDebug() << "VERSION" << qVersion();

  // Main window.
  QMainWindow mainWindow;
  mainWindow.resize(1200, 900);

  // Control area.
  QDockWidget controlDock;
  mainWindow.addDockWidget(Qt::LeftDockWidgetArea, &controlDock);

  QLabel controlDockTitle("Control Dock");
  controlDockTitle.setMargin(20);
  controlDock.setTitleBarWidget(&controlDockTitle);

  QPointer<QVBoxLayout> dockLayout = new QVBoxLayout();
  QWidget layoutContainer;
  layoutContainer.setLayout(dockLayout);
  controlDock.setWidget(&layoutContainer);

  QPushButton randomizeButton;
  randomizeButton.setText("Randomize");
  dockLayout->addWidget(&randomizeButton);

  // Render area.
  QPointer<QVTKOpenGLNativeWidget> vtkRenderWidget =
      new QVTKOpenGLNativeWidget();
  mainWindow.setCentralWidget(vtkRenderWidget);

  // VTK part.
  vtkNew<vtkGenericOpenGLRenderWindow> window;
  vtkRenderWidget->setRenderWindow(window.Get());

  vtkNew<vtkSphereSource> sphere;
  sphere->SetRadius(1.0);
  sphere->SetThetaResolution(100);
  sphere->SetPhiResolution(100);

  vtkNew<vtkDataSetMapper> mapper;
  mapper->SetInputConnection(sphere->GetOutputPort());

  vtkNew<vtkActor> actor;
  actor->SetMapper(mapper);
  actor->GetProperty()->SetEdgeVisibility(true);
  actor->GetProperty()->SetRepresentationToSurface();

  vtkNew<vtkRenderer> renderer;
  renderer->AddActor(actor);

  window->AddRenderer(renderer);

  // Setup initial status.
  std::mt19937 randEng(0);
  ::Randomize(sphere, mapper, window, randEng);

  // connect the buttons
  QObject::connect(&randomizeButton, &QPushButton::released,
                   [&]() { ::Randomize(sphere, mapper, window, randEng); });

  mainWindow.show();

  return app.exec();
}

namespace {
void Randomize(vtkSphereSource* sphere, vtkDataSetMapper* mapper,
               vtkGenericOpenGLRenderWindow* window, std::mt19937& randEng)
{
  // Generate randomness.
  double randAmp = 0.2 + ((randEng() % 1000) / 1000.0) * 0.2;
  double randThetaFreq = 1.0 + (randEng() % 9);
  double randPhiFreq = 1.0 + (randEng() % 9);

  // Extract and prepare data.
  sphere->Update();
  vtkSmartPointer<vtkPolyData> newSphere;
  newSphere.TakeReference(sphere->GetOutput()->NewInstance());
  newSphere->DeepCopy(sphere->GetOutput());
  vtkNew<vtkDoubleArray> height;
  height->SetName("Height");
  height->SetNumberOfComponents(1);
  height->SetNumberOfTuples(newSphere->GetNumberOfPoints());
  newSphere->GetPointData()->AddArray(height);

  // Deform the sphere.
  for (int iP = 0; iP < newSphere->GetNumberOfPoints(); iP++)
  {
    double pt[3] = {0.0};
    newSphere->GetPoint(iP, pt);
    double theta = std::atan2(pt[1], pt[0]);
    double phi =
        std::atan2(pt[2], std::sqrt(std::pow(pt[0], 2) + std::pow(pt[1], 2)));
    double thisAmp =
        randAmp * std::cos(randThetaFreq * theta) * std::sin(randPhiFreq * phi);
    height->SetValue(iP, thisAmp);
    pt[0] += thisAmp * std::cos(theta) * std::cos(phi);
    pt[1] += thisAmp * std::sin(theta) * std::cos(phi);
    pt[2] += thisAmp * std::sin(phi);
    newSphere->GetPoints()->SetPoint(iP, pt);
  }
  newSphere->GetPointData()->SetScalars(height);

  // Reconfigure the pipeline to take the new deformed sphere.
  mapper->SetInputDataObject(newSphere);
  mapper->SetScalarModeToUsePointData();
  mapper->ColorByArrayComponent("Height", 0);
  window->Render();
}
} // namespace

The only real difference is that I’ve added the line qDebug() << "VERSION" << qVersion(); to indicate which version is actually used by the compiled executable.
The CMakelists.txt file has been heavily edited by me and looks like the following:

cmake_minimum_required(VERSION 3.12 FATAL_ERROR)

PROJECT(MinimalQtVTKApp)

find_package(VTK COMPONENTS 
  CommonCore
  CommonDataModel
  FiltersSources
  GUISupportQt
  InteractionStyle
  RenderingContextOpenGL2
  RenderingCore
  RenderingFreeType
  RenderingGL2PSOpenGL2
  RenderingOpenGL2
  GUISupportQt
  RenderingQt
)

set(VTK_QT_VERSION 6)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets OpenGLWidgets)

message (STATUS "VTK_VERSION: ${VTK_VERSION}, Qt Version: ${Qt${VTK_QT_VERSION}Widgets_VERSION}")

# Instruct CMake to run moc and uic automatically when needed.
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)

# CMAKE_AUTOMOC in ON so the MOC headers will be automatically wrapped.
add_executable(MinimalQtVTKApp MACOSX_BUNDLE
  MinimalQtVTKApp.cxx
)
target_link_libraries(MinimalQtVTKApp ${VTK_LIBRARIES} Qt6::Core Qt6::Gui Qt6::Widgets Qt6::OpenGLWidgets)

vtk_module_autoinit(
  TARGETS MinimalQtVTKApp
  MODULES ${VTK_LIBRARIES}
)

So basically, the CMake file finds VTK and all necessary, required packages, along with all required Qt6 packages, adds the executable and then links Qt and VTK to it.
Next, I created a build directory, entered it and hit cmake ... This will lead to the following error message:
CMake Error: The INTERFACE_QT_MAJOR_VERSION property of "Qt6::Core" does not agree with the value of QT_MAJOR_VERSION already determined for "MinimalQtVTKApp".
I can built the application nonetheless, but it will rely on Qt5 then.

I am really confused by this error message and by the application using Qt5, because the file specifically tells CMake to use Qt6 instead of Qt5. Also, I am on Ubuntu 22.04 and using VTK version 9.1, but afaik this version already supports Qt6.
What might be the reason for the application displaying this error message? Is it even related to VTK at all? Or is there an error with my CMakeLists.txt?

Thanks in advance!

It seems like your environment is still providing Qt5 when configuring the example. Please make sure that all the Qt* entries in the CMakeCache.txt (in the build directory) point to Qt6.

I can do that, the following entries are located inthe CMakeCache.txt:


//The directory containing a CMake configuration file for Qt5Core.
Qt5Core_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5Core

//The directory containing a CMake configuration file for Qt5Gui.
Qt5Gui_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5Gui

//The directory containing a CMake configuration file for Qt5OpenGL.
Qt5OpenGL_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5OpenGL

//The directory containing a CMake configuration file for Qt5Widgets.
Qt5Widgets_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5Widgets

//The directory containing a CMake configuration file for Qt5.
Qt5_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5

I can change these to point to Qt6 instead, but if I run cmake .. or make again, it will just reset the CMakeCache file. So unfortunately, the problem still exists. :-\
Is there a more permanent way to tell my environment to provide Qt6 instead?

All the Qt5 variables are fine. All you need to do is be able to provide the Qt6 variables to CMake.

Once you do, cmake -DVTK_QT_VERSION=6 -DQt6_DIR=<path to qt6> , vtk should link against the Qt6 libraries.

I do not really understand what you mean with “providing the Qt6 variables to CMake”.
The CMakeCache.txt contains the following entries regarding Qt6:

//The directory containing a CMake configuration file for Qt6CoreTools.
Qt6CoreTools_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6CoreTools

//The directory containing a CMake configuration file for Qt6Core.
Qt6Core_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6Core

//The directory containing a CMake configuration file for Qt6DBusTools.
Qt6DBusTools_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6DBusTools

//The directory containing a CMake configuration file for Qt6DBus.
Qt6DBus_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6DBus

//The directory containing a CMake configuration file for Qt6GuiTools.
Qt6GuiTools_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6GuiTools

//The directory containing a CMake configuration file for Qt6Gui.
Qt6Gui_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6Gui

//The directory containing a CMake configuration file for Qt6OpenGLWidgets.
Qt6OpenGLWidgets_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6OpenGLWidgets

//The directory containing a CMake configuration file for Qt6OpenGL.
Qt6OpenGL_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6OpenGL

//The directory containing a CMake configuration file for Qt6WidgetsTools.
Qt6WidgetsTools_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6WidgetsTools

//The directory containing a CMake configuration file for Qt6Widgets.
Qt6Widgets_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6Widgets

//The directory containing a CMake configuration file for Qt6.
Qt6_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt6

looks correct imo.
Now if I want to configure it with cmake -DVTK_QT_VERSION=6 -DQt6_DIR=/usr/lib/x86_64-linux-gnu/cmake/Qt6/ .., I still get the error message.
What am I doing wrong?

That looks correct to me. Try starting in a fresh directory. If you’d like to use Qt6, there’s no reason to provide Qt5_DIR values. And if it does end up in the cache after running cmake, it means something is trying to find Qt5 and linking against it.

I tried it in a new directory now, used the same command as before (cmake -DVTK_QT_VERSION=6 -DQt6_DIR=/usr/lib/x86_64-linux-gnu/cmake/Qt6/ ..). Same error message unfortunately…
CMakeCache.txt still contains Qt5 entries.
If I build vtk 9.4 manually with Qt6 and require that version in the CMakeLists.txt file, then it works. Maybe the reason is that I’ve installed vtk using apt (sudo apt install vtk9) and that this version (9.1) still links to Qt5 somehow?

Yes, that’s the problem right there. Whoever built and packaged vtk for your distribution would have built it against Qt5 which makes it depend on that version of Qt.

I understand. So effectively, if vtk is installed with the official package manager and links against Qt5, it is not possible to run this using Qt6.
Configuring and installing from source solves the problem, however.
Thanks for all the help, will remember this for the future. :slight_smile: