在所有 QML 文件中全局访问 C++ signals/slots
Access C++ signals/slots globally in all QML files
我想访问我所有 qml 文件中的 C++ class(信号和槽)。当我在 main.qml 中设置连接时,我能够接收到信号。但是,在任何其他 qml 文件(此处为 MainMenu.qml)中,我无法访问该信号。我可以使用槽函数从其他 qml 文件发送,但不能读取信号。知道如何解决吗?我对 QML 很陌生。
Main.cpp
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
Game game;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("_game", &game);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
Window {
id: root
width: 1240
height: 800
visible: true
title: qsTr("Horse Race")
color: "black"
Image {
id: backgroundImage
anchors.fill: parent
source: "Imgs/Background.png"
}
Loader {
id: mainLoader
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
horizontalCenterOffset: - 100
}
source: "MainMenu.qml"
}
Connections {
target: _game
onNameUpdate: {
console.log("updated name")
}
onStartedNewGame: {
console.log("new game")
}
}
}
MainMenu.qml
Rectangle {
Text {
id: header
anchors.horizontalCenter: root.horizontalCenter
anchors.horizontalCenterOffset: + 35
color: "white"
font.family: "Super Mario Bros."
font.pointSize: 30
text: "Game Menu"
}
Connections {
target: _game
onStartedNewGame: {
console.log("inside MainMenu")
header.color = "blue"
}
}
}
我知道有两种方法。
-
Game
class 是 QObject
。与其在 C++ 代码中实例化它并在 rootContext
上调用 setContextProperty
,不如将 Game
实例注册为 QML singleton
对象。然后,无论您将其导入何处,您都可以访问它。这是一个示例(TestClass 将是您的游戏 class):
#include <QQmlApplicationEngine>
#include <QCoreApplication>
#include <QDebug>
class TestClass : public QObject
{
Q_OBJECT
public:
explicit TestClass(QQmlEngine* engine, QObject *parent = nullptr);
Q_INVOKABLE void triggerSignal() { emit aSignal(); } ;
private:
QQmlEngine * m_engine;
signals:
void aSignal();
public slots:
void aSlot() { qDebug() << "aSlot invoked" };
};
static QObject *InstantiateTestClass(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(scriptEngine)
TestClass *singletonClass = new TestClass(engine);
return singletonClass;
}
static void registerTestClassSingleton()
{
qmlRegisterSingletonType<TestClass>("Globals", 1, 0, "TestClass", InstantiateTestClass);
}
Q_COREAPP_STARTUP_FUNCTION(registerTestClassSingleton)
并且在每个 QML 文件中您可以:
...
import Globals 1.0
Item {
...
Connections {
target: TestClass
onASignal: {
console.log("A Signal triggered")
}
}
}
请注意,当您第一次引用 Game
class 时,它会被实例化。创建 Connection
不会调用 InstantiateTestClass
函数。您应该在 TestClass 上调用函数,读取 属性 或编写 属性 来创建单例实例。因此,在创建 Window 对象时创建 TestClass 实例是个好主意,例如:
//main.qml
...
Windows{
...
Component.onCompleted: {
//Just to create TestClass instance. You can also refer to a property instead of calling a method on it.
//Refering to a propery is enough for the TestClass to be instantiated.
TestClass.aSlot();
}
}
- 您还可以创建一个 QML 单例类型,在其中创建一个
var
属性,然后在您的 main.qml
中将 var
设置为 _game
.然后您将能够从每个 QML 文件访问该成员。您应该在该 QML 单例文件旁边创建一个 qmldir
文件。
//GameHelper.qml
pragma Singleton
...
QtObject{
property var gameInstance
}
//main.qml
...
import "."
Window{
...
Component.onCompleted: {
GameHelper.gameInstance = _game;
}
}
//MainMenu.qml
...
Item{
Connections {
target: GameHelper.gameInstance
onASignal: {
console.log("A Signal triggered")
}
}
}
两种方法都可以。我会更喜欢第一个。
我想访问我所有 qml 文件中的 C++ class(信号和槽)。当我在 main.qml 中设置连接时,我能够接收到信号。但是,在任何其他 qml 文件(此处为 MainMenu.qml)中,我无法访问该信号。我可以使用槽函数从其他 qml 文件发送,但不能读取信号。知道如何解决吗?我对 QML 很陌生。
Main.cpp
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
Game game;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("_game", &game);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
Window {
id: root
width: 1240
height: 800
visible: true
title: qsTr("Horse Race")
color: "black"
Image {
id: backgroundImage
anchors.fill: parent
source: "Imgs/Background.png"
}
Loader {
id: mainLoader
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
horizontalCenterOffset: - 100
}
source: "MainMenu.qml"
}
Connections {
target: _game
onNameUpdate: {
console.log("updated name")
}
onStartedNewGame: {
console.log("new game")
}
}
}
MainMenu.qml
Rectangle {
Text {
id: header
anchors.horizontalCenter: root.horizontalCenter
anchors.horizontalCenterOffset: + 35
color: "white"
font.family: "Super Mario Bros."
font.pointSize: 30
text: "Game Menu"
}
Connections {
target: _game
onStartedNewGame: {
console.log("inside MainMenu")
header.color = "blue"
}
}
}
我知道有两种方法。
-
Game
class 是QObject
。与其在 C++ 代码中实例化它并在rootContext
上调用setContextProperty
,不如将Game
实例注册为 QMLsingleton
对象。然后,无论您将其导入何处,您都可以访问它。这是一个示例(TestClass 将是您的游戏 class):
#include <QQmlApplicationEngine>
#include <QCoreApplication>
#include <QDebug>
class TestClass : public QObject
{
Q_OBJECT
public:
explicit TestClass(QQmlEngine* engine, QObject *parent = nullptr);
Q_INVOKABLE void triggerSignal() { emit aSignal(); } ;
private:
QQmlEngine * m_engine;
signals:
void aSignal();
public slots:
void aSlot() { qDebug() << "aSlot invoked" };
};
static QObject *InstantiateTestClass(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(scriptEngine)
TestClass *singletonClass = new TestClass(engine);
return singletonClass;
}
static void registerTestClassSingleton()
{
qmlRegisterSingletonType<TestClass>("Globals", 1, 0, "TestClass", InstantiateTestClass);
}
Q_COREAPP_STARTUP_FUNCTION(registerTestClassSingleton)
并且在每个 QML 文件中您可以:
...
import Globals 1.0
Item {
...
Connections {
target: TestClass
onASignal: {
console.log("A Signal triggered")
}
}
}
请注意,当您第一次引用 Game
class 时,它会被实例化。创建 Connection
不会调用 InstantiateTestClass
函数。您应该在 TestClass 上调用函数,读取 属性 或编写 属性 来创建单例实例。因此,在创建 Window 对象时创建 TestClass 实例是个好主意,例如:
//main.qml
...
Windows{
...
Component.onCompleted: {
//Just to create TestClass instance. You can also refer to a property instead of calling a method on it.
//Refering to a propery is enough for the TestClass to be instantiated.
TestClass.aSlot();
}
}
- 您还可以创建一个 QML 单例类型,在其中创建一个
var
属性,然后在您的main.qml
中将var
设置为_game
.然后您将能够从每个 QML 文件访问该成员。您应该在该 QML 单例文件旁边创建一个qmldir
文件。
//GameHelper.qml
pragma Singleton
...
QtObject{
property var gameInstance
}
//main.qml
...
import "."
Window{
...
Component.onCompleted: {
GameHelper.gameInstance = _game;
}
}
//MainMenu.qml
...
Item{
Connections {
target: GameHelper.gameInstance
onASignal: {
console.log("A Signal triggered")
}
}
}
两种方法都可以。我会更喜欢第一个。