Understanding QML_INTERFACE

Viewed 92

I've a really hard time understanding what the QML_INTERFACE and QML_IMPLEMENTS_INTERFACES macros are supposed to do. Naively I assumed that the macro does what it says it does, registering a non-instantiable ("uncreatable") type to the QML type system. A type I can then use as a custom property to bind to a specific implementation.

Let's get concrete (no pun intended) and elaborate with a simple example, an interface with a single QString property:

class QmlInterface : public QObject {
  Q_OBJECT
  QML_INTERFACE
  Q_PROPERTY(QString name READ get WRITE set NOTIFY changed)

public:
  virtual ~QmlInterface();
  virtual QString get() const = 0;
  virtual void set(QString const& name) = 0;

signals:
  void changed();
};

Q_DECLARE_INTERFACE(QmlInterface, "QmlInterface")

And a class which implements that interface (declarations omitted for simplicity):

class QmlImplA : public QmlInterface {
  Q_OBJECT
  QML_ELEMENT
  QML_IMPLEMENTS_INTERFACES(QmlInterface)

public:
  QString get() const final;
  void set(QString const& name) final;

private:
  QString name_;
};

An instance of this implementation is then created in C+ and an interface pointer gets passed to the QML engine right before loading the .qml file

QQmlApplicationEngine engine;
// url to .qml file
std::unique_ptr<QmlInterface> qml_impl_a{std::make_unique<QmlImplA>()};
engine.setInitialProperties({{"qml_impl_a", QVariant::fromValue(qml_impl_a.get())}});
engine.load(// url);

I then assumed I could just import the interface in my .qml file and use it with the underlying implementation like this:

import QtQuick
import QtQuick.Controls
import QmlInterface

ApplicationWindow {
    required property QmlInterface qml_impl_a
    // ... 
}

However this doesn't work. QML complains that QmlInterface is not a type

Upon googling for some QML_INTERFACE examples I found a passage in Cross-Platform Development with Qt 6 and Modern C++ which says that:

QML_INTERFACE registers an existing Qt interface type. The type is not instantiable from QML, and you cannot declare QML properties with it.

Wait... what? Is this true? The official documentation doesn't mention this anywhere. As this would practically render interfaces completely useless I assume the book is wrong here?

For now I got my example working by making the interface a normal QML_ELEMENT and adding QML_UNCREATABLE to prevent instantiation from inside QML. This works and does what I intended in the first place. I still wonder though did I do something wrong or are the macros supposed to do something else entirely and are just named badly?

0 Answers
Related