I am trying to build a Qt project with qml. The qml metatype registration looks into the top level qml directories only. I would like to have it look into subdirectories as well.
The project is structured like this:
./CMakeLists.txt
./ui
./ui/MainWindow.qml
./src
./src/controller
./src/controller/Foo.cpp
./src/controller/FileController.cpp
./src/controller/FileController.h
./main.cpp
My CMakeLists.txt looks as follows:
cmake_minimum_required(VERSION 3.25)
project(testproject)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick Widgets)
qt_standard_project_setup()
qt_add_executable(testproject)
target_sources(testproject PRIVATE
main.cpp
)
target_link_libraries(testproject PRIVATE
Qt6::Widgets
Qt6::Gui
Qt6::Quick
)
set_target_properties(testproject PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
OUTPUT_NAME some_test_exec
)
target_include_directories(testproject PUBLIC ${PROJECT_SOURCE_DIR}/include)
qt_add_qml_module(testproject
URI testproject
VERSION 1.0
SOURCES
src/controller/FileController.h
src/controller/FileController.cpp
QML_FILES
ui/MainWindow.qml
)
I have a main function which starts the application:
#include <iostream>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char* argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/testproject/ui/MainWindow.qml"_qs);
engine.load(url);
return app.exec();
}
I created a simple qml file in MainWindow.qml in here
ui/MainWindow.qml
which looks like this:
import QtQuick
import QtQuick.Controls
ApplicationWindow {
id: mainwindow
visible: true
width: 640
height: 480
menuBar: MenuBar {
Menu {
title: "File"
MenuItem { text: "open" }
}
}
}
in the
src/controller
subdirectory, I create the following header file FileController.h
#include <QObject>
#include <QtQml/qqmlregistration.h>
class FileController : public QObject {
Q_OBJECT
QML_ELEMENT // WITHOUT THIS IT COMPILES FINE
Q_PROPERTY(QString file READ name WRITE setName NOTIFY filenameChanged)
private:
QString filename;
void setName(QString name);
public:
FileController(QObject *parent);
QString name();
signals:
void filenameChanged();
};
and some implementation details in FileController.cpp
#include "FileController.h"
FileController::FileController(QObject *parent) : QObject(parent) {}
void FileController::setName(QString name) {filename = name;}
QString FileController::name() {return filename;}
When compiling, I get the following error:
[13/19] Building CXX object CMakeFiles/test....dir/testproject_qmltyperegistrations.cpp.o
FAILED: CMakeFiles/testproject.dir/testproject_qmltyperegistrations.cpp.o
/usr/bin/c -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_QMLINTEGRATION_LIB -DQT_QMLMODELS_LIB -DQT_QML_LIB -DQT_QUICK_LIB -DQT_WIDGETS_LIB -I/some/test/directory/build/testproject_autogen/include -I/ome/test/directory/include -I/some/test/directory -isystem /usr/include/qt6/QtQml/6.4.1 -isystem /usr/include/qt6/QtQml/6.4.1/QtQml -isystem /usr/include/qt6/QtCore/6.4.1 -isystem /usr/include/qt6/QtCore/6.4.1/QtCore -isystem /usr/include/qt6/QtCore -isystem /usr/include/qt6 -isystem /usr/lib/qt6/mkspecs/linux-g -isystem /usr/include/qt6/QtQml -isystem /usr/include/qt6/QtQmlIntegration -isystem /usr/include/qt6/QtNetwork -isystem /usr/include/qt6/QtWidgets -isystem /usr/include/qt6/QtGui -isystem /usr/include/qt6/QtQuick -isystem /usr/include/qt6/QtQmlModels -isystem /usr/include/qt6/QtOpenGL -fPIC -std=c 17 -MD -MT CMakeFiles/testproject.dir/testproject_qmltyperegistrations.cpp.o -MF CMakeFiles/testproject.dir/testproject_qmltyperegistrations.cpp.o.d -o CMakeFiles/testproject.dir/testproject_qmltyperegistrations.cpp.o -c /some/test/directory/build/testproject_qmltyperegistrations.cpp
/some/test/directory/qt/qml_in_other_dir/build/testproject_qmltyperegistrations.cpp:10:10: fatal error: FileController.h: No such file or directory
10 | #include <FileController.h>
Apparently when registering qml metatype, Qt is not looking in the subdirectories for the corresponding headers. Or it should copy them, but doesn't. It doesn't have FileController.h in a directory in its include path.
- Why isn't the directory structure preserved with CMake and Qt?
- I want to have some logic in the qml files (specifically here open a file dialog and pass the result to the C class). In order to have qml and C interact, I need to register the class with the QML_ELEMENT macro. Am I doing something fundamentally wrong? What is the correct way to do this? (I could always fiddle with the include paths or put everything in a base directory, but that seems wrong).
Edit: the issue still occurs e.g. when putting Filecontroller.h into
./include/controller/
and adding
include
to the target_include_directories and adapting the include path in FileController.cpp to something like
#include <controller/FileController.h>
CodePudding user response:
You either need to add include_directories(src/controller)
to your CMake file or set the include to #include <src/controller/FileController.h>
.
Have a look at this SO question.
EDIT: I found these bugreports QTBUG-87221, QTBUG-101146 . The patch adds a note to the documentation:
All headers that declare QML types need to be accessible without any prefix from the project's include path.