在我的项目中,我试图将单例类型从 C++ 公开到 QML。最初我用
qmlRegisterSingletonInstance
注册它,效果很好。但是当我尝试用 QML_ELEMENT
和 QML_SINGLETON
宏替换这种方式时,它编译得很好,但是当 QML 尝试使用该类时,它会抱怨类型未定义。
我的项目有两个模块,一个仅由 qml 文件组成,第二个由 C++ 文件组成,我尝试在第一个模块中公开该类并使用它。结构有点复杂,所以我用一个更简单的测试项目重现了这个问题。我注意到 QML 既不能识别单例类型,也不能识别非单例类型。
该项目具有以下结构:
│ CMakeLists.txt
│ main.cpp
│
└───logicmodule
CMakeLists.txt
Main.qml
Message.h
MySingleton.h
代码:
// logicmodule/MySingleton.h
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <QString>
class MySingleton : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
MySingleton(QObject *parent = nullptr)
: QObject{parent}
{}
public slots:
QString exec(const QString &str) { return "somestring"; }
};
// logicmodule/Message.h
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <QString>
class Message : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString author MEMBER m_author NOTIFY authorChanged)
public:
Message() {}
signals:
void authorChanged();
private:
QString m_author;
};
// logicmodule/Main.qml
import QtQuick
import QtQuick.Controls
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
// property Message msg: Message {}
Button {
id: button
anchors.centerIn: parent
text: "Click me"
onClicked: {
label.text = MySingleton.exec(label.text)
}
}
Label {
id: label
anchors.top: button.bottom
}
}
// logicmodule/CMakeLists.txt
project(logicmodule VERSION 0.1 LANGUAGES CXX)
qt_add_library(${PROJECT_NAME} STATIC)
qt6_add_qml_module(${PROJECT_NAME}
URI Main
VERSION 1.0
NO_PLUGIN
RESOURCE_PREFIX "/qt/qml"
QML_FILES
Main.qml
SOURCES
MySingleton.h
Message.h
)
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt6::Core
Qt6::Gui
Qt6::Qml
Qt6::Quick
)
target_include_directories(${PROJECT_NAME}
PUBLIC
.
)
// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "Message.h"
#include "MySingleton.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/qt/qml/Main/Main.qml"_qs);
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreated,
&app,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
engine.addImportPath(":/");
engine.load(url);
if (engine.rootObjects().isEmpty()) {
return -1;
}
return app.exec();
}
// CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(typetest VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 6.5 REQUIRED COMPONENTS Quick)
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory(logicmodule)
qt_add_executable(apptypetest
main.cpp
)
target_link_libraries(apptypetest PUBLIC
logicmodule
)
# No effect
# target_include_directories(apptypetest
# PUBLIC
# "${PROJECT_SOURCE_DIR}/logicmodule"
# logicmodule
# )
# include_directories(logicmodule)
一切都编译得很好,但在运行时我在尝试调用 MySingleton 时收到错误:
qrc:/qt/qml/Main/Main.qml:16: ReferenceError: MySingleton is not defined
我尝试研究这个问题,但找不到确切的案例,我得到的最接近的是这个: qmltyperegistration 包含路径不承认子目录
但是具有自定义类型的文件似乎包含得很好,没有任何前缀。
我还注意到,在构建文件夹中我有一个文件
build\Debug\logicmodule\logicmodule_qmltyperegistrations.cpp
,它包含对qmlRegisterTypesAndRevisions
的调用,但似乎没有效果?
还尝试了一种没有任何附加库的项目结构(因此没有
logicmodule
lib,所有内容都在项目根目录中,我只有一个 CMakeLists),它似乎工作正常,所有自定义类型都被 QML 识别。
有人可以指出我在图书馆做错了什么吗?为什么 QML 无法识别在库中定义的类型?
问题出在
NO_PLUGIN
调用中使用 qt_add_qml_module()
。删除它并将可执行文件链接到 *plugin 目标而不是支持 lib 后,一切正常。
最终 CMakeLists:
# logicmodule/CMakeLists.txt
project(logicmodulelib VERSION 0.1 LANGUAGES CXX)
qt_add_library(${PROJECT_NAME} STATIC)
qt_add_qml_module(${PROJECT_NAME}
URI logicmodule
VERSION 1.0
RESOURCE_PREFIX "/qt/qml"
QML_FILES
Main.qml
SOURCES
MySingleton.h
Message.h
)
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt6::Core
Qt6::Gui
Qt6::Qml
Qt6::Quick
)
target_include_directories(${PROJECT_NAME}
PUBLIC
.
)
# CmakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(typetest VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 6.5 REQUIRED COMPONENTS Quick)
qt_policy(SET QTP0001 NEW)
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qt/qml)
set(QML_IMPORT_PATH "${CMAKE_BINARY_DIR}/qt/qml" CACHE STRING "Qt Creator extra qml import paths"
FORCE)
qt_standard_project_setup(REQUIRES 6.5)
add_subdirectory(logicmodule)
qt_add_executable(apptypetest
main.cpp
)
target_link_libraries(apptypetest PUBLIC
Qt6::Gui
Qt6::Qml
logicmodulelibplugin
)
感谢qt论坛上的人帮我解决了这个问题: