在所有 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"
        }
    }
}

我知道有两种方法。

  1. 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();
}
}
  1. 您还可以创建一个 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")
        }
    }

}

两种方法都可以。我会更喜欢第一个。