About

In this tutorial we will see how to create simple QtQuick applications and how they will communicate with OCaml. QtQuick code will be very short because this tutorial is about using OCaml and QtQuick together. To get more information about programming in QtQuick you can visit official help or QML book.

Demos described below and sources of this tutorial can be found in Github repository.

Changelog

Starting from version 0.5 lablqt is known as lablqml. The rename was proposed because we don’t create binding for Qt itself, most of the library is strongly connected to QtQuick.

This tutorial is the second one about OCaml and QtQuick. You can get old one here. In lablqml starting from version 0.3 external JSON file for specifying API is deprecated, PPX extension is required. That’s why, lablqml 0.3 requires OCaml 4.02 or newer.

"Hello world!" in QtQuick

In this chapter a very simple QtQuick application is described. It interacts with OCaml only one-way: by calling handler on mouse click.

In piece of code below application’s window is created, with colored area at the top and some text centered in it. Some properties of application’s window are set. Position of inner rectangle is described using anchors.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import QtQuick 2.0
import QtQuick.Controls 1.0

ApplicationWindow {
    title: "Hello world!"
    color: "#FFFFDF"
    width: 400
    height: 450

    Rectangle {
        color:  "lightgreen"
        height: 150
        anchors {
            left:  parent.left
            right: parent.right
            top:   parent.top
        }
        Text {
            text: "Click me!"
            anchors.centerIn: parent
        }
    }
}
Note
QtQuick and QML are very similar concepts and can replace each other. In Qt Modeling Language (QML) we describe the user interface and store it in files with qml extension. QtQuick is a collection of classes from which GUI can be built.

This QML file is already a valid application. It can be executed without any compilation by command qmlscene Root.qml. Running the application should look like as on this picture:

images/tutorial2/img1.png

The normal way to interact between QtQuick and C++ is by exposing some objects into QtQuick engine. Lablqml has specific API for exposing objects and allows to declare them in OCaml (instead on C++) by using PPX extension. Build system will compile all into final executable. Let’s describe class controller in controller.ml:

open QmlContext

class virtual controller = object(self)
  method virtual onMouseClicked: unit -> unit[@@qtmeth]
end[@@qtclass]

The module QmlContext can be found in ocamlfind package named lablqml. This package and PPX extension named ppx_qt are located in OPAM’s package lablqml. To apply syntax extension on this file you need to execute the following command:

ocamlopt -I `ocamlfind query lablqml` -c -dsource -ppx "ppx_qt -destdir . -ext cpp" controller.ml
Note
In the summer 2017 lablqml has started to depend on the OCaml package ocaml-migrate-parsetree. Compilation commands will probably require small adjustments for lablqml >= 0.6.

In controller.ml you can see the class controller with attribute qtclass and its method onMouseClicked with attribute qtmeth. Without this attributes the class and its method will not be available in QtQuick.

After preprocessing the source file a virtual class called controller will be generated. Let’s implement a virtual method onMouseClicked in program.ml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
open QmlContext

(* Helper function to expose the object 'obj' into QtQuick engine.
 * Argument 'name' means the name for this object in QtQuick
 *
 * It is not interesting function. You can simply copy and paste it.
 *)
let expose ~name obj =
  (* 'obj#handler' is raw C++ pointer to object. *)
  set_context_property ~ctx:(get_view_exn ~name:"rootContext") ~name obj#handler

(* This function is called once before constructing window. It exposes some
 * objects from OCaml to QtQuick engine
 *)
let init: unit -> unit = fun () ->
  let controller = object(self)
    inherit Controller.controller (Controller.create_controller ()) as super
    (* 'onMouseClicked' is the method marked with [@@qtmeth] attribute in 'controller.ml'
     * We need to implement it. *)
    method onMouseClicked () =
      print_endline "OCaml says: Mouse Clicked!"
  end in
  (* Once object is created we make it available in QtQuick engine *)
  expose ~name:"controller" controller

let () =
  let qmlfile = "Root.qml" in
  (* We start application and pass initialization function and main QML file *)
  run_with_QQmlApplicationEngine Sys.argv init qmlfile

In the snippet of code above, the path to the QML file is relative. QML files can be embedded into final executable using Qt Resource System.

The QML file doesn’t know anything about the exposed object yet. So, let’s create some area which will receive mouse events and call OCaml if it is clicked.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import QtQuick 2.0
import QtQuick.Controls 1.0

ApplicationWindow {
    title: "Hello world!"
    color: "#FFFFDF"
    width: 400
    height: 450

    Rectangle {
        color:  "lightgreen"
        height: 150
        anchors {
            left:  parent.left
            right: parent.right
            top:   parent.top
        }
        Text {
            text: "Click me!"
            anchors.centerIn: parent
        }
        MouseArea {
            anchors.fill: parent
            onClicked: {
              // call OCaml there
              controller.onMouseClicked()
            }
        }
    }
}
Note
Where is the entry point?
Our applications can start either from OCaml and C++. Example above starts up from OCaml. See demos from previous tutorial to get examples of startup from C++.

Full code of first demo is available on github.

Qt object’s metamodel

Qt classes based on QObject can have properties, signals, slots and methods invokable from QtQuick. All information about this stuff is generated from C++ header files during compilation phase.

For example, our method onMouseClicked declared above is invokable, that’s why we can use it from QML. If it was not, we would get the following type error:

Property 'onMouseClicked' of object controller(0x153cd00) is not a function

Properties always have name, type and methods that return its values. They can also have methods that set values. When a property changes it can send a signal with the updated value.

Signals usually are connected with slots. See this C++ code, for example:

auto listener = new PortListener();
auto parser = new MessageParser();
connect(listener, SIGNAL(gotData(QByteArray)), parser, SLOT(onDataReceived(QByteArray)) );

As you can see the relation between objects is established independently from them, i.e. object’s classes are independent: we don’t need to think about passing callbacks or creating some heavy-weight design patterns. That’s the Qt way of avoiding dependency injection.

Adding properties

Let’s extend the first demo by counting how many times we have clicked on the window. We will do it by declaring a property of type int. To do this we need to improve our class definition in controller.ml:

open QmlContext

class virtual controller = object(self)
  method virtual onMouseClicked: unit -> unit[@@qtmeth]
  method virtual clicksCount: int[@@qtprop]
end[@@qtclass]

Let’s extend the object implementation by adding a click counter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
open QmlContext

let expose ~name obj =
  set_context_property ~ctx:(get_view_exn ~name:"rootContext") ~name obj#handler

let init () =
  let controller = object(self)
    inherit Controller.controller (Controller.create_controller ()) as super

    val mutable counter = 0
    method getclicksCount () = counter

    method onMouseClicked () =
      print_endline "OCaml says: Mouse Clicked!";
      counter <- counter+1;
      self#emit_clicksCountChanged counter
  end in

  expose ~name:"controller" controller

let () =
  run_with_QQmlApplicationEngine Sys.argv init "Root.qml"

When the user hits the mouse button the counter is incremented and a signal with the updated counter value is emitted by OCaml method emit_$(PROPERTY_NAME)Changed. If Qt properties don’t notify GUI about these changes old values will be used and no changes will be applied.

How QtQuick will react to these changes will be defined in Root.qml. The new rectangle with text inside will show how many times user have clicked. In this demo QtQuick layouts are used to place rectangles vertically. Anchors are not used to implement it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import QtQuick 2.0
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0

ApplicationWindow {
    title: "OCaml&QtQuick demo 2"
    property string backgroundColor: "#FFFFDF"

    color: backgroundColor
    width: 400
    height: 450

    ColumnLayout {
      Rectangle {
        color: "lightgreen"
        Layout.preferredHeight: 150
        Layout.preferredWidth: 400
        Text {
          text: "Click me!"
          anchors.centerIn: parent
        }
        MouseArea {
          anchors.fill: parent
          onClicked: controller.onMouseClicked()
        }
      }
      Rectangle {
        color: "#ebf097"
        Layout.preferredHeight: 150
        Layout.preferredWidth:  400
        Text{
          anchors.centerIn: parent
          text: "You have clicked " + controller.clicksCount + " times"
        }
      }
    }
}

Our property is used in line

text: "You have clicked " + controller.clicksCount + " times"

At the right of the colon character should be a valid ECMAScript expression which is evaluated into a property value at runtime. It can be multi line, call some functions and methods and use many properties inside. When one of properties used inside changes the whole expression is reevaluated and the text property gets updated with a new value.

Sources of this demo can be found on github. Final window should be similar to the one on this picture:

images/tutorial2/img2.png

Adding signals

When a property is declared a signal is automatically created. Signals can be declared separately using the same PPX syntax extension.

1
2
3
4
5
6
7
8
open QmlContext

class virtual controller = object(self)
  method virtual hiGotten: message:string -> unit[@@qtsignal]
  method virtual onMouseClicked: unit -> unit[@@qtmeth]
  method virtual clicksCount: int[@@qtprop]

end[@@qtclass]

Now signal hiGotten can be connected to a handler in QtQuick:

...
Connections {
    target: controller
    onHiGotten: console.log(message)
}
...

The on prefix is a syntax convention to declare signal handlers (behind the scene they are slots which were mentioned above). Arguments of the signal (the message in our example) are made available in the context by QtQuick engine. I.e. Qt infers these names from signal’s declaration. If a signal is declared in the OCaml side without labels Qt will not be able to get the signal argument names. That’s why signals should be declared using labeled arguments.

Syntax extension is expanded into method emit_$(SIGNAL_NAME) which can be used in main OCaml file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
open QmlContext

let main () =
  let controller_cppobj = Controller.create_controller () in
  let controller = object(self)
    inherit Controller.controller controller_cppobj as super

    val mutable counter = 0
    method getclicksCount () = counter

    method onMouseClicked () =
      counter <- counter+1;
      self#emit_clicksCountChanged counter;
      (* Send signal with a new message *)
      self#emit_hiGotten (Printf.sprintf "Hi %d times" counter);
  end in

  set_context_property ~ctx:(get_view_exn ~name:"rootContext") ~name:"controller" controller#handler

let () =
  run_with_QQmlApplicationEngine Sys.argv main "Root.qml"

Full code of this demo is available on github.

Supported types

QML has its own type system. It is not as strong or static as the OCaml one but basic types exist. When values are passed from C++ to QML they are wrapped into QVariant, which is a universal container (or universal type). For simple types QML basic types suit well enough. But for more elaborated types (like structures with a number of fields inside) then the right approach is to create a QObject-derived class and put these field inside. At the moment objects support in lablqml is a little bit awkward but an application like QOcamlBrowser (screenshot below) can be created without this feature. If you have a better way to implement it, I would be glad to hear it.

images/tutorial2/img3.png

The demo can be found there. In controller.ml some methods and properties, which use the universal type Variant.t, are declared.

open QmlContext

class virtual controller = object(self)
  method virtual getobj: unit -> QVariant.t[@@qtmeth]
  method virtual setobj: QVariant.t -> unit[@@qtmeth]

  method virtual person: QVariant.t[@@qtprop]

end[@@qtclass]

We also need to create a class for the object that will be manipulated by these methods.

open QmlContext

class virtual item = object(self)
  method virtual name: string[@@qtprop]
  method virtual age : int[@@qtprop]
end[@@qtclass]

The methods and properties (declared with attributes) of the controller will be used in the main QML file when the construction of root object is completed (in Component.completed signal handler).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import QtQuick 2.0
import QtQuick.Controls 1.0

ApplicationWindow {
    Component.onCompleted: {
      // get object from OCaml as method's result
      var o = controller.getobj();
      console.log(o);
      // use some methods
      console.log(o.getname());
      console.log(o.getage() );

      // We pass three values to OCaml side
      // N.B. method is the same
      controller.setobj(o);
      controller.setobj("asdf");
      controller.setobj(15);

      // get object from OCaml as property's value
      // to get a property's value we don't need paretheses at the end
      var p = controller.person;
      console.log(p);
      console.log(p.getname())
      console.log(p.getage());
    }
}

In the OCaml side, the value of universal variant is matched and a specific string is printed. The constructor `qobject contains a value of type cppobj which is a raw pointer to an instance of a C++ QObject class. That’s why an OCaml object is constructed from this value in the first clause.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class myitem cppObj = object(self)
  inherit Item.item cppObj as super
  method getname () = "Vasya"
  method getage () = 11
end

...

    method setobj  = function
      | `qobject o ->
         let item = new myitem o in
         printf "qobject: %s %d\n%!" (item#getname ()) (item#getage())
      | `string s -> printf "String: '%s'\n%!" s
      | `int  x -> printf "int %d\n%!" x
      | `empty -> print_endline "empty"
...

To avoid pattern matching we can allow the use of a specific type in properties and methods which will represent a raw C++ pointer to QObject. It’s not obvious at the moment how good it will be because the current approach is more powerful but more verbose. If you have any ideas on this topic I will be glad to read e-mails from you.

At the moment not many types can be used in methods' signatures but any complex type can be implemented using variant type. It should not be a problem to support QML basic types in syntax extension if needed.

Compiling applications using lablqml

OCaml+QtQuick applications can start up both from C++ and OCaml. Examples using makefiles are available in links above and at lablqml’s repository (This one seems to be full enough). Also QOCamlBrowser is available, it uses ocamlbuild as the build system (but is has some issues at the moment: we need to generate some code on ./configure stage).

To compile sources manually we need to execute following commands. Let’s suppose that PPX syntax extension was used at files module1.ml and module2.ml. To compile them we need to execute:

ocamlopt -I `ocamlfind query lablqml` -c -ppx "ppx_qt -destdir . -ext cpp" module1.ml
ocamlopt -I `ocamlfind query lablqml` -c -ppx "ppx_qt -destdir . -ext cpp" module2.ml

Let’s suppose that files module1.ml and module2.ml declare classes called class1 and class2 respectively. After executing of two commands above four files should be created: class1.h, class1_c.cpp, class2.h and class2_c.cpp. These C++ files should be compiled as they are:

g++ `pkg-config --cflags Qt5Quick` -fPIC -c class2_c.cpp -o class2_c.o
g++ `pkg-config --cflags Qt5Quick` -fPIC -c class1_c.cpp -o class1_c.o

Header files contain meta-information about types. So, we need to apply Qt MetaObject compiler (a.k.a. moc) and compile generated C++ files:

moc class1.h -o moc_class1.cpp
g++ `pkg-config --cflags Qt5Quick` -fPIC -c moc_class1.cpp -o moc_class1.o
moc class2.h -o moc_class2.cpp
g++ `pkg-config --cflags Qt5Quick` -fPIC -c moc_class2.cpp -o moc_class2.o

Also we need main .ml file which uses classes Module1.class1 and Module2.class2:

ocamlfind opt -package lablqml -c program.ml

And now we can link everything into final executable:

ocamlfind opt -package lablqml -linkpkg \
  -cclib -lstdc++ -ccopt -L`qmake -query QT_INSTALL_LIBS` \
  -cclib -lQt5Quick -cclib -lQt5Qml -cclib -lQt5Network \
  -cclib -lQt5Widgets -cclib -lQt5Gui -cclib -lQt5Core \
  module1.cmx module1.cmx program.cmx \
  class1_c.o class2_c.o moc_class1.o moc_class2.o \
  -o a.out

Using lablqml without code generation

The one may not be very happy about printing some C++ code to the source files and compiling them separately. This approach may introduce compilations when the generated code is far from ideal and requires adjustments. However, Qt follows the approach with compile-time code generation and lablqml supports it as described above.

Also, QtQuick has convenience class QQmlPropertyMap which allows you to dynamically (without compile-time code generation) add new properties to the subclasses of QQmlPropertyMap. The support of this class contributed to lablqml 0.5 by Orbital Fox.

Conclusion

There are many aspects of QtQuick which are not mentioned in this tutorial. For example, in the previous tutorial you can read about classes for desktop applications which live in harmony with your platform’s style. There is Model-View framework available both in Qt and lablqml which is used rather much in QOcamlBrowser. QtQuick has an embedded ECMAScript engine in it and you can compile OCaml code using js_of_ocaml and run it as a crossplatform application. QtQuick renders itself with OpenGL, so particle simulations and shader effects are built in.

lablqml was used in some industrial applications. Ask Orbital Fox for more detailed feedback.

P.S. Getting and installing Qt5

Using package repository

There is Qt 5.2.1 in Ubuntu Trusty and Qt5.3 in current Debian/testing (July 1, 2014). I have got Qt 5.3 using official installer but all code should work with Qt 5.2 too. If it doesn’t, please report me somehow.

Official packages

are available on Qt Project.

Building from Git

Official Qt Project wiki is there. Below you can see my-init and configure scripts which configure Qt5 to build only modules needed by lablqml.

My init-repository script
#!/usr/bin/env bash
MODULES=qtbase,qtdeclarative,qtjsbackend,qtactiveqt,qtquickcontrols
perl ./init-repository --module-subset=$MODULES -f $@
My configure script
#!/bin/bin/env bash
set -x
./configure -developer-build -opensource -nomake examples -nomake tests -confirm-license \
  -no-gtkstyle -no-glib -no-cups  $@

Qt compiles some code during ./configure stage, don’t worry, it’s normal.

Compilation time depends on your machine. Some fellas have finished with -j8 in 5 minutes. On my Intel Core i3 I can do it with -j2 in 30 minutes.

P.S.

I have discovered some issues with compilation my QOCamlBrowser app recently on Ubuntu 13.10 32bit on Intel Core 2 Duo CPU. My application was crashing in qt_memfill32_sse2 (dest=0x8910e10, value=0, count=194) at painting/qdrawhelper_sse2.cpp:264. So, I have decided to disable special instruction sets for Qt5 on this machine by improving ./my-configure script. I put this stuff in P.S. section because I haven’t tested it on my Intel i3 machine.

#!/usr/bin/env bash
set -x
NOFB="-no-directfb -no-linuxfb -no-eglfs -no-kms"
SQL="-no-sql-mysql -no-sql-sqlite"
NOSSE="-no-sse2 -no-sse3 -no-ssse3 -no-sse4.1 -no-sse4.2 -no-avx -no-avx2"
./configure -developer-build -opensource -nomake examples -nomake tests -confirm-license \
  -no-gtkstyle -no-glib -no-cups -no-nis \
  $SQL $NOFB $NOSSE \
  $@

Value $NOFB disables frame buffer and KMS support. I think these low-level interfaces are not needed for our OCaml GUI projects. Also we disable SQLite and MySQL DBMS in Qt. Value $NOSSE disables all special instruction sets.With that ./my-configure QtQuick examples in Qt still work and my QOcamlBrowser startups without crash.

If you want to compile Qt faster you can study next options of ./configure script:

    -no-feature-<feature> Do not compile in <feature>.
    -feature-<feature> .. Compile in <feature>. The available features
                          are described in src/corelib/global/qfeatures.txt
P.P.S. MacOS remark

There are some issues on Qt bugtracker about using pkg-config on MacOS. It seems that they handle -F option wrong. The solution is mentioned here.

If you should compile Qt from source you will be able to avoid this issue by passing special options to ./configure script: -prefix /opt/Qt/5.2.1 -opensource -nomake tests -no-xcb -no-framework.

P.P.P.S.

Next script will be useful (as a part of ~/.bashrc) for setting local environment for your Qt5 build:

with_qt5() {
  export PATH=~/mand/prog/qt/qt5/qtbase/bin:$PATH  # where Qt5 has been build
  qmake -query QT_VERSION
  export LD_LIBRARY_PATH=`qmake -query QT_INSTALL_LIBS`:$LD_LIBRARY_PATH
  export PKG_CONFIG_PATH=`qmake -query QT_INSTALL_LIBS`/pkgconfig:$PKG_CONFIG_PATH
}