1

I'm quite knew to Qt and would like to understand if there is a solution for my issue.

I would like to define in C++ a global function that should be called from the QML side. I found a solution on StackOverflow that would let me invoke the function like:

myObject.myFunction();

But I would prefer to avoid the object name like:

myFunction();

that is the same we do, for example, when we need to translate a string:

qsTr("my string");

Is it possible anyway?

Brutus
  • 790
  • 3
  • 10
  • 27

2 Answers2

1

The closest you can get to that is by using a singleton:

import Foo 1.0

Window {
    // ...

    Component.onCompleted: MySingleton.myFunction()
}

Taking the example from the documentation:

// First, define your QObject which provides the functionality.
class SingletonTypeExample : public QObject
{
    Q_OBJECT
    Q_PROPERTY (int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)

public:
    SingletonTypeExample(QObject* parent = 0)
        : QObject(parent), m_someProperty(0)
    {
    }

    ~SingletonTypeExample() {}

    Q_INVOKABLE int doSomething() { setSomeProperty(5); return m_someProperty; }

    int someProperty() const { return m_someProperty; }
    void setSomeProperty(int val) { m_someProperty = val; emit somePropertyChanged(val); }

signals:
    void somePropertyChanged(int newValue);

private:
    int m_someProperty;
};

// Second, define the singleton type provider function (callback).
static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)

    SingletonTypeExample *example = new SingletonTypeExample();
    return example;
}

// Third, register the singleton type provider with QML by calling this function in an initialization function.
qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider);

Then, in QML:

import QtQuick 2.0
import Qt.example.qobjectSingleton 1.0
Item {
    id: root
    property int someValue: MyApi.someProperty

    Component.onCompleted: {
        someValue = MyApi.doSomething()
    }
}
Mitch
  • 23,716
  • 9
  • 83
  • 122
  • Could you please elaborate a little bit how and where to define the singleton? Thanks! – Brutus May 10 '18 at 21:15
  • @Mitch thanks, it's working! I would prefer a solution where I could replace `MyApi.doSomething()` with just `doSomething()`. Anyway your solution is useful and thought me something! – Brutus May 11 '18 at 08:06
  • 1
    @Brutus I don't think that there is a way to do that without using private Qt API, which I don't think is worth it. – Mitch May 11 '18 at 08:48
1

You can try to use the setContextObject method of the QQmlContext if the QQmlEngine you are using. I created a minimal Gist which demonstrates the approach.

Basically, you create a custom class deriving from QObject, say, MyApi:

class MyAPI : public QObject
{
    Q_OBJECT
public:
    explicit MyAPI(QObject *parent = nullptr);

    // This method will be visible as function in your QML code:
    Q_INVOKABLE QString makeUpperCase(const QString &text) const;
};

In you main.cpp, set an instance of this class as the context object of the engine's QQmlContext:

QQmlApplicationEngine engine;
engine.rootContext()->setContextObject(new MyAPI(&app));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

In the QML code, you can now access all properties, signals, slots and Q_INVOKABLE methods of the MyAPI class:

Window {
    visible: true
    width: 640
    height: 480

    // Call the `makeUpperCase` method of the context object:
    title: makeUpperCase("Hello World")
}
Martin Höher
  • 715
  • 5
  • 9
  • Thank you very much for sharing your code, it is definitely the solution I was looking for. I had already tried a similar approach before asking here on StackOverflow but for some reason it didn't work, probably I was doing some little mistake. As an addition I will use this [mechanism](https://wiki.qt.io/How_to_do_dynamic_translation_in_QML) to let the bindings reevaluate when I know that the function/API changes its behavior. Do you think it's ok or there is another way of doing it? Really thanks a lot for your help! :-) – Brutus May 12 '18 at 10:26
  • 1
    I am not aware of another solution, so just go ahead and implement the mentioned trick in your app. ;-) – Martin Höher May 12 '18 at 11:06