Integrating C++ code with QML

Overview

The Qt Quick Ultralite applications use C++ code for business logic, interfacing with hardware, and working with other software modules. The C++ part of the application interacts with the QML presentation layer. Therefore the C++ classes as well as their functions, properties, and signals are exposed to QML code.

Interface headers

An application can contain interface headers that describe the components that shall be exposed to QML. These are normal C++ header files that are registered in CMake with the qul_register_types() macro.

When you register a header with the macro, it has two effects on the build:

  • the qmlinterfaceextractor tool will be run on the header, creating one or more QML interface files
  • the generated files will be fed to qmltocpp when translating the project's QML code

The steps enable QML code to make use of the C++ interfaces.

Exporting C++ APIs

Components

Class and struct declarations are the building blocks for exposing C++ functionality to QML. Each exported C++ class declaration defines a QML component of the same name.

The following example demonstrates how to use components:


  #include <qul/qtobject.h>
  #include <qul/property.h>
  #include <qul/signal.h>

  struct BigState : public Qul::Items::QtObject
  {
      Qul::Property<int> bigness;
      int feed(int amount);
      Qul::Signal<void()> gotFood;
  };


  Item {
      BigState {
          id: bigState
          bigness: 61
          onGotFood: bigness = 99
      }

      Component.onCompleted: bigState.feed(3)
  }

In order to be picked up by qmlinterfaceextractor, the declared class

Some base classes have special meaning. For more detailed information, see Singletons and Models

Singletons

If the class derives directly from Qul::Items::Singleton<Itself>, it is marked with pragma singleton and an instance is available globally.

The following example demonstrates how to use Singletons:


  struct MyApi : public Qul::Items::Singleton<MyApi>
  {
      void start();
  };


  Item {
      Component.onCompleted: MyApi.start()
  }

Models

The class can directly derive from from Qul::Model<T> to export a list model. See the Qul::Model documentation for details.

Functions

All public non-overloaded member functions in an exported class declaration are available in QML. The function's parameter types and return type are mapped to their QML counterparts.

The following example demonstrates how to use functions:


  struct DirLookup : public Qul::Items::QtObject
  {
      Qul::Property<const char *> basePath;
      int lookup(const char *path);
  };
  struct Simulator : public Qul::Items::Singleton<Simulator>
  {
      void run(DirLookup *lookup);
  };


  Item {
      DirLookup {
          id: look
          basePath: "/objects"
      }

      Component.onCompleted: Simulator.run(look)
  }

Properties

Public fields in exported class declarations become component properties if the field has the type Qul::Property<T>. Here T describes the property's C++ type and is mapped to a corresponding QML type.

The properties defined in this way behave like built-in properties. In particular they can be assigned bindings in QML and can be used as a data source in other bindings.

The following example demonstrates how to use properties:


  struct MyData : public Qul::Items::QtObject
  {
      Qul::Property<int> val;
      void update(int x) {
          // can get and set property values from C++
          val.setValue(x);
      }
  };


  Item {
      Item {
          // can bind QML property to exported property
          x: mydata_x.val
          color: "red"
          width: 50
          height: 50
      }
      MyData {
          id: mydata_x
          val: 100
      }
      MyData {
          id: mydata_width
          // can bind exported property
          val: parent.width
      }
      Component.onCompleted: {
          mydata_x.update(200);
          console.log(mydata_width.val);
      }
  }

Grouped properties

Properties can be grouped together as follows:


  struct MyObject : public Qul::Items::QtObject
  {
      struct Grouped {
          Qul::Property<int> val1;
          Qul::Property<int> val2;
      };
      Grouped group;
  };


  Item {
      MyObject {
          group.val1: 42
          group.val2: 43
      }
  }

The grouping happens by placing the properties inside a struct or class S and then having a field of type S inside the exported class. The type S must not itself be derived from Qul::Items::QtObject. Only its public fields of type Qul::Property are exposed as properties within the group. Groups are only for properties and can not have signals, enums or functions.

Signals

Public fields of type Qul::Signal<Fn> are translated to signals on the QML component.

The template argument Fn must be a function type that describes the signal's parameter types. As usual, these types are mapped to the matching QML types. Similarly, the parameter names used in Fn becomes a QML signal's parameter names.

The following code demonstrates how use signals:


  struct MyItem : public Qul::Items::QtObject
  {
      Qul::Signal<void(int sigValue)> triggered;
      void callTriggered() {
          triggered(42);
      }
  };


  Item {
      MyItem {
          id: myitem
          onTriggered: console.log(sigValue);
      }
      Component.onCompleted: myitem.callTriggered()
  }

Enums

Public enum declarations in an exported class declaration are translated to QML enums.

The following code demonstrates how use public enums:


  struct MyStates : public Qul::Items::Singleton<MyStates>
  {
      enum State {
          On,
          Off,
          Broken
      };
  };


  Item {
      property MyStates.State state: MyStates.On
  }

C++ to QML type mapping

C++ typeQML type
boolbool
integral (char, short, int, long, ...)int
floating point (float, double)real
T* where T derives from QtObjectmatching component type
enum in exported classmatching enum type