Supporting Qt 6 - What's needed?

Agreed – my thought process on keeping it this way was a) to make extra sure I wasn’t breaking anyone’s Qt5 compilation as we start into this process, and b) to ease the transition for everyone running cMake from the command line (e.g. they don’t have to change anything in their cMake commands except QT5->QT6). I’m not sure either of those is a terribly strong reason, and the switch to an enumeration-style version selection, or even just purely numerical, is not a big undertaking. I’m in favor if wmayer thinks it’s reasonable.

Regarding some of the other “Qt5” things – Qt is helping us out here in Qt6, renaming things like “QT5_WRAP_UI” to “QT_WRAP_UI” as of Qt 5.15. For older builds of Qt5 we can probably define that ourselves, which will eliminate most of the BUILD_QT5 flag checks in the Mod subdirectories. Same goes for QT5_ADD_RESOURCES, and QT5_WRAP_CPP.

I don’t think it was ever planned to have two or three different variables but one suffices because it will never happen that the FreeCAD code will support more than two major versions of Qt.

Sure, if it helps to simplify things it’s fine to go this way.

VTK uses an IMO good process to handle both Qt5 and Qt6. In https://gitlab.kitware.com/vtk/vtk/-/blob/v9.2.2/CMake/vtkQt.cmake they define a VTK_QT_VERSION variable, which will subsequently be used throughout the cmake files in the form of Qt${VTK_QT_VERSION}_Found on all Qt variables which use a version. Maybe this is a way we could use as well to simplify the cmake code for using different versions of Qt?

That VTK setup is nice – with the minor change of defaulting to Qt5 (for now) I think we can probably use it almost exactly. I’ll have to look at how they do their variable substitution, the way I’m doing it right now is following what appears to be Qt’s overall strategy of dropping the version number from their macro names. So in the current PR I’ve got things like:

set(QtCore_LIBRARIES ${Qt5Core_LIBRARIES})
set(QtCore_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS})
set(QtCore_MOC_EXECUTABLE ${Qt5Core_MOC_EXECUTABLE})

and the matching code for Qt6. Then all other code outside of the SetupQt.cmake file can just use the non-versioned variable, and not need any kind of switch on the Qt version (except in cases like Spacemouse or TechDraw, where there is some kind of Qt5-only dependency).

Update: I’ve updated PR 7647 to adopt VTK’s cMake Qt-selection strategy, to remove BUILD_QT5 and add FREECAD_QT_VERSION instead, and to consolidate almost all Qt5/Qt6 switches. I’ve also switched that PR to Draft since it now breaks compilation on Qt5 due to some missing header search location – obviously I have to get that fixed before merging! I think there’s probably still some refactoring and cleanup that could be done, but it’s getting pretty close to being usable enough to start working from.

wmayer, can we drop support for QtWebKit? My recollection is that you still had a use-case for it – is that still true?

pyside2: 239 occurrences
shiboken2: 56 occurrences

src/Gui/NaviCube.cpp

I changed:
sansFont.setWeight(hGrp->GetInt("FontWeight", 87));
to:
sansFont.setWeight(QFont::Weight(hGrp->GetInt("FontWeight", 87)));

a) It feels wrong to use arbitary values instead of enum value names.
b) Reasonably, Qt had a reason for the change. Which would suggest FC does the same? Havn’t looked to see how far such a change would extend.

I once needed the Qt Webkit module because the nouveau driver has problems with Qt’s Webengine module. Luckily, the user vanuan gave me a hint to use the option of Software OpenGL and that indeed fixed the issue. It’s now quite a while that I use the Webengine module without bigger problems.

So, long story short: we can drop support of the Webkit module. Btw, does it even exist for Qt6?

No, Qt6 only supports the QtWebEngine module.

That’s excellent – it doesn’t exist for Qt6, and removing it from the tests in SetupQt.cmake has that file under 50 lines now, which I feel pretty good about. Of course, now I have to change our CI setup! It turns out it was using WebKit when it compiled, so this PR is failing CI. I should be able to get to that later today.

ContextMenu.cpp and others.
Am seeing issues e.g.

QObject::connect(rendermodegroup,
                 SIGNAL(triggered(QAction *)),
                 this,
                 SLOT(changeRenderMode(QAction *)));

“No matching member function for call to ‘connect’”.
AFAICT it is only where the types passed in functions to SLOT or SIGNAL are pointers.
Only found one maybe useful reference at qt centre
Is there a simpler solution?

I’ve merged PR 7647 (hopefully I haven’t broken anyone’s Qt5 build in the process…), so now we have a FREECAD_QT_VERSION variable in cMake that you can set to “6” to try to compile against Qt6.

A request for those working on this: it would be nice if we can keep the PRs small, so IMO once you’ve got a small problem resolved with getting Qt6 working, go ahead and submit a PR – this will get you a CI run to make sure you don’t break Qt5 builds, and we can merge the changes in bit by bit, rather than trying to do it all in one go. I think once we’ve done a few rounds and narrowed in on the appropriate solutions, we can go quite quickly.

I’d also suggest focusing on C++ first – we can’t even test the Python until the C++ compiles and runs.

I’ve merged the updated cmake stuff into my experimental build.

I have accumulated a bunch of changes to cmake files, which I am now reapplying to the new setup.

Most of it is cosmetic, bringing consistency, shortening lines so they don’t keep breaking, simple stuff like spaces on if’s etc, restating conditions etc in else and end.

There are many occurrences of “if(DEFINED ” that don’t consider might be “falsey” (empty, OFF, “”, etc). (probably been there for years)

In almost every case I think a simple “if(condition” is less error-prone.

Am I missing something?

I just realized an issue with removing QtWebkit. On MSYS2 a package Qt5WebEngineWidgets doesn’t exist because the code currently doesn’t compile and the only alternative is QtWebkit. For now I have to disable the Web module.

That’s unfortunate (especially considering Qt announced this deprecation in 2013!!). Do you think we should just put it back in our builds? It’s not that big of a deal while we still support Qt5. I have no idea what the state of Qt6 is on Mingw.

For me it’s not a big loss not to have the StartPage for a Mingw build. Sooner or later we anyway have to live without it once we drop Qt5 support – unless the problematic code will be ported to Mingw. From what I have seen it’s apparently the Chromium engine that currently doesn’t compile with Mingw.

So, I don’t mind if we leave it like this now. I only wanted to report the issue.

It is possible to replace the qtwebengine respectively chromium with newer versions. Here is an example:

1. Get QT

wget https://download.qt.io/official_releases/qt/5.15/5.15.6/single/qt-everywhere-opensource-src-5.15.6.tar.xz
tar -xf qt-everywhere-opensource-src-5.15.6.tar.xz
cd qt-everywhere-src-5.15.6/

2. Remove qtwebengine

rm -R qtwebengine

3. Get current version

git clone --recursive https://code.qt.io/qt/qtwebengine.git
cd qtwebengine 
git checkout tags/v5.15.11-lts

4. Fix version conflict

cd ..
sed -i 's/5.15.11/5.15.6/g' qtwebengine/.qmake.conf

You can try the same with chromium:

5. Remove

rm -R qtwebengine/src/3rdparty/chromium

6. Get current Qt6.4 chromium version
Check the ChromiumVersions

cd qtwebengine/src/3rdparty 
git clone --recursive https://github.com/chromium/chromium
cd chromium
…

Here is a screenshot of a FreeCAD build using Qt6:
FreeCAD_with_Qt6.png
There are a couple of problems that we have to master:

  • We explicitly use QtCore_MOC_EXECUTABLE for a handful of files. The variable is set to the content of
${Qt${FREECAD_QT_MAJOR_VERSION}Core_MOC_EXECUTABLE}

but because Qt6Core_MOC_EXECUTABLE is not defined QtCore_MOC_EXECUTABLE will be empty and causes a build failure. I had to manually create the CMake variable Qt6Core_MOC_EXECUTABLE and set the path to moc

  • The compiler produces a couple of dozens of warnings. Nothing serious and everything is easy to fix.
  • TechDraw is the only module that is not yet ported. This will be the most difficult part.
  • When starting FreeCAD the first time it has crashed immediately after showing the splash screen. This is because some modules import PySide2 modules which pull in Qt5 binaries and thus causes the crash. So, we need a mechanism where PySide2 calls are redirected to PySide6. Therefore we can use the same trick as we did with PySide vs. PySide2
  • Closing FreeCAD causes another crash. That should be easy to fix.

Regarding to MOC, when I try to build i get error:

[ 12%] Automatic MOC and UIC for target FreeCADGui
AutoMoc: /home/senja/FCQt6/FreeCAD/src/Gui/UiLoader.h:0:1: note: No relevant classes found. No output generated.
[ 12%] Built target FreeCADGui_autogen
[ 12%] Generating qrc_translation.cpp
[ 13%] Building files out of FreeCADGuiInit.py
[ 13%] Generating QSint/actionpanel/moc_actionbox.cpp
[ 13%] Generating QSint/actionpanel/moc_actiongroup.cpp
[ 13%] Generating QSint/actionpanel/moc_actionlabel.cpp
[ 13%] Generating QSint/actionpanel/moc_actionpanel.cpp
[ 13%] Generating QSint/actionpanel/moc_taskheader_p.cpp
[ 13%] Generating Quarter/eventhandlers/moc_DragDropHandler.cpp
[ 13%] Generating Quarter/eventhandlers/moc_EventFilter.cpp
[ 13%] Generating Quarter/eventhandlers/moc_FocusHandler.cpp
[ 13%] Generating Quarter/moc_ContextMenu.cpp
[ 13%] Generating Quarter/moc_InteractionMode.cpp
[ 13%] Generating Quarter/moc_QuarterWidget.cpp
[ 13%] Generating Quarter/moc_SensorManager.cpp
[ 13%] Generating Quarter/moc_SignalThread.cpp
[ 13%] Generating moc_GraphvizView-internal.cpp
/bin/sh: line 1: -o: command not found
make[2]: *** [src/Gui/CMakeFiles/FreeCADGui.dir/build.make:586: src/Gui/moc_GraphvizView-internal.cpp] Error 127
make[1]: *** [CMakeFiles/Makefile2:6722: src/Gui/CMakeFiles/FreeCADGui.dir/all] Error 2
make: *** [Makefile:136: all] Error 2

So generally, generated

freecad-build/src/Gui/CMakeFiles/FreeCADGui.dir/build.make

is missing MOC command in lines 586 and 590 beginning with cd /home/senja/FCQt6/freecad-build/src/Gui &&

src/Gui/moc_GraphvizView-internal.cpp: /home/senja/FCQt6/FreeCAD/src/Gui/GraphvizView.cpp
	@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --blue --bold --progress-dir=/home/senja/FCQt6/freecad-build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_29) "Generating moc_GraphvizView-internal.cpp"
	cd /home/senja/FCQt6/freecad-build/src/Gui && -o /home/senja/FCQt6/freecad-build/src/Gui/moc_GraphvizView-internal.cpp /home/senja/FCQt6/FreeCAD/src/Gui/GraphvizView.cpp

src/Gui/moc_GraphvizView.cpp: /home/senja/FCQt6/FreeCAD/src/Gui/GraphvizView.h
	@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --blue --bold --progress-dir=/home/senja/FCQt6/freecad-build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_30) "Generating moc_GraphvizView.cpp"
	cd /home/senja/FCQt6/freecad-build/src/Gui && -o /home/senja/FCQt6/freecad-build/src/Gui/moc_GraphvizView.cpp /home/senja/FCQt6/FreeCAD/src/Gui/GraphvizView.h

If I add

/usr/lib/qt6/moc

between && and -o build continues . In rest of file reference to /usr/lib/qt6/moc is OK

I am using Manjaro

[code]
OS: Manjaro Linux (KDE/plasma)
Word size of FreeCAD: 64-bit
Version: 0.21.30828 (Git)
Build type: Unknown
Branch: master
Hash: 860ce0a78e386c8a83c0835e6ff80454935763e5
Python 3.10.8, Qt 6.4.0, Coin 4.0.1, Vtk 9.1.0, OCC 7.5.3
Locale: English/United States (en_US)
Installed mods:

[/code]