QML

Loading

We support three methods of loading a QML file: QML.QQmlApplicationEngine, QML.QQuickView and QQmlComponent. These behave equivalently to the corresponding Qt classes. They have different advantages:

Interaction

Interaction with Julia happens through the following mechanisms:

Note that Julia slots appear missing, but they are not needed since it is possible to directly connect a Julia function to a QML signal in the QML code, e.g. with QTimer.

Type conversion

Most fundamental types are converted implicitly. Mind that the default integer type in QML corresponds to Int32 in Julia.

We also convert QVariantMap, exposing the indexing operator [] to access element by a string key. This mostly to deal with arguments passed to the QML append function in list models.

Interface

QML.QMLModule

Module for building Qt5 QML graphical user interfaces for Julia programs. Types starting with Q are equivalent of their Qt C++ counterpart, so, unless otherwise noted, they have no Julia docstring and we refer to the Qt documentation for details instead.

source
QML.JuliaDisplayType
struct JuliaDisplay

You can use display to send images to a JuliaDisplay. There is a corresponding QML block called JuliaDisplay. Of course the display can also be added using pushdisplay!, but passing by value can be more convenient when defining multiple displays in QML. See below for syntax.

julia> using QML

julia> using Plots: plot

julia> function simple_plot(julia_display::JuliaDisplay)
          x = 0:1:10
          display(julia_display, plot(x, x, show = false, size = (500, 500)))
          nothing
        end;

julia> @qmlfunction simple_plot

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          import QtQuick.Layouts 1.0
          import org.julialang 1.0
          ApplicationWindow {
            visible: true
            Column {
              Button {
                text: "Plot"
                onClicked: Julia.simple_plot(julia_display)
              }
              JuliaDisplay {
                id: julia_display
                width: 500
                height: 500
              }
              Timer {
                running: true
                onTriggered: Qt.quit()
              }
            }
          }
          """)
          load(path)
          exec()
        end
source
QML.JuliaPropertyMapMethod
function JuliaPropertyMap(pairs...)

Store Julia values for access from QML. Observables are connected so they change on the QML side when updated from Julia and vice versa only when passed in a property map. Note that in the example below, if you run output[] = new_value from within Julia, the slider in QML will move.

julia> using QML

julia> using Observables: Observable, on

julia> output = Observable(0.0);

julia> on(println, output);

julia> mktempdir() do folder
         path = joinpath(folder, "main.qml")
         write(path, """
         import QtQuick 2.0
         import QtQuick.Controls 1.0
         ApplicationWindow {
           visible: true
           Slider {
             onValueChanged: {
               observables.output = value;
             }
           }
           Timer {
             running: true
             onTriggered: Qt.quit()
           }
         }
         """)
         load(path; observables = JuliaPropertyMap("output" => output))
         exec()
       end
source
QML.ListModelMethod
function ListModel(items::AbstractVector, addroles::Bool = true)

Constructor for a ListModel. The ListModel type allows using data in QML views such as ListView and Repeater, providing a two-way synchronization of the data. A ListModel is constructed from a 1D Julia array. To use the model from QML, it can be exposed as a context attribute.

A constructor (the eltype) and setter and getter "roles" based on the fieldnames of the eltype will be automatically created if addroles is true.

If new elements need to be constructed from QML, a constructor can also be provided, using the setconstructor method. QML can pass a list of arguments to constructors.

In Qt, each of the elements of a model has a series of roles, available as properties in the delegate that is used to display each item. The roles can be added using the addrole function.

julia> using QML

julia> mutable struct Fruit
          name::String
          cost::Float64
        end

julia> fruits = ListModel([Fruit("apple", 1.0), Fruit("orange", 2.0)]);

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          import QtQuick.Layouts 1.0
          ApplicationWindow {
            visible: true
            ListView {
              model: fruits
              anchors.fill: parent
              delegate:
                Row {
                  Text {
                    text: name
                  }
                  Button {
                    text: "Sale"
                    onClicked: cost = cost / 2
                  }
                  Button {
                    text: "Duplicate"
                    onClicked: fruits.append([name, cost])
                  }
                  Timer {
                    running: true
                    onTriggered: Qt.quit()
                  }
                }
              }
            }
          """)
          load(path; fruits = fruits)
          exec()
        end
source
QML.QQmlApplicationEngineType
struct QQmlApplicationEngine

One of 3 ways to interact with QML (the others being QQuickView and QQmlComponent. You can load a QML file to create an engine with load. Use exec to execute a file after it's been loaded.

The lifetime of the QQmlApplicationEngine is managed from C++ and it gets cleaned up when the application quits. This means it is not necessary to keep a reference to the engine to prevent it from being garbage collected prematurely.

julia> using QML

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          ApplicationWindow {
            visible: true
            Text {
              text: greeting
            }
            Timer {
              running: true
              onTriggered: Qt.quit()
            }
          }
          """)
          load(path; greeting = "Hello, World!")
          exec()
        end
source
QML.QQmlComponentType
struct QQmlComponent

One of 3 ways to interact with QML (the others being QQmlApplicationEngine and QQuickView. Make from an engine from e.g.init_qmlengine. Use set_data to set the QML code, create to create the window, and exec to fill the window.

julia> using QML

julia> component = QQmlComponent(init_qmlengine());

julia> set_data(component, QByteArray("""
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          ApplicationWindow {
            visible: true
            Rectangle {
              Text {
                text: "Hello, World!"
              }
              Timer {
                running: true
                onTriggered: Qt.quit()
              }
            }
          }
        """), QUrl())

julia> create(component, qmlcontext())

julia> exec()
source
QML.QQuickViewType
struct QQuickView

One of 3 ways to interact with QML (the others being QQmlApplicationEngine and QQmlComponent. QQuickView creates a window, so it's not necessary to wrap the QML in ApplicationWindow. Use init_qquickview to create a quick view, set_source to set the source for the quick view, QML.show to view, and exec to execute.

julia> using QML

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          Rectangle {
            Text {
              text: "Hello, World!"
            }
            Timer {
              running: true
              onTriggered: Qt.quit()
            }
          }
          """)
          quick_view = init_qquickview()
          set_source(quick_view, QUrlFromLocalFile(path))
          QML.show(quick_view)
          exec()
        end
source
QML.QTimerType
struct QTimer

You can use QTimer to simulate running Julia in the background. Note that QML provides the infrastructure to connect to the QTimer signal through the Connections item.

julia> using QML

julia> counter = Ref(0);

julia> increment() = counter[] += 1;

julia> @qmlfunction increment

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          import org.julialang 1.0
          ApplicationWindow {
              visible: true
              Connections {
                target: timer
                function onTimeout() {
                  Julia.increment()
                }
              }
              Button {
                  text: "Start counting"
                  onClicked: timer.start()
              }
              Timer { // unrelated, this is a timer to stop and continue testing
                running: true
                onTriggered: Qt.quit()
              }
          }
          """)
          load(path, timer=QTimer())
          exec()
        end
source
QML.addroleFunction
function addrole(model::ListModel, name::String, getter, [setter])

Add your own getter (and optionally, setter) functions to a ListModel for use by QML. setter is optional, and if it is not provided the role will be read-only. getter will process an item before it is returned. The arguments of setter will be collection, new_value, index as in the standard setindex! function. If you would like to see the roles defined for a list, use roles. To remove a role, use removerole.

julia> using QML

julia> items = ["A", "B"];

julia> array_model = ListModel(items, false);

julia> addrole(array_model, "item", identity, setindex!)

julia> roles(array_model)
1-element QML.QStringListAllocated:
 "item"

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          import QtQuick.Layouts 1.0
          ApplicationWindow {
            visible: true
            ListView {
              model: array_model
              anchors.fill: parent
              delegate: TextField {
                placeholderText: item
                onTextChanged: item = text;
              }
            }
            Timer {
              running: true
              onTriggered: Qt.quit()
            }
          }
          """)
          load(path; array_model = array_model)
          exec()
        end

julia> removerole(array_model, "item")
source
QML.content_itemFunction
function content_item(quick_view::QQuickView)

Get the content item of a quick view. Equivalent to QQuickWindow::contentItem.

julia> using QML

julia> using CxxWrap.CxxWrapCore: CxxPtr

julia> quick_view = mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          Rectangle {
            Timer {
              running: true
              onTriggered: Qt.quit()
            }
          }
          """)
          quick_view = init_qquickview()
          set_source(quick_view, QUrlFromLocalFile(path))
          @assert content_item(quick_view) isa CxxPtr{QQuickItem}
          exec()
        end
source
QML.createFunction
function create(component::QQmlComponent, context::QQmlContext)

Equivalent to QQmlComponent::create. This creates a component defined by the QML code set using set_data. It also makes sure the newly created object is parented to the given context. See the example for set_data.

source
QML.engineFunction
function engine(quick_view::QQuickView)

Equivalent to QQuickView::engine. If you would like to modify the context of a QQuickView, use engine to get an engine from the window, and then root_context to get the context from the engine.

julia> using QML

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          Rectangle {
            Text {
              text: greeting
            }
            Timer {
              running: true
              onTriggered: Qt.quit()
            }
          }
          """)
          quick_view = init_qquickview()
          context = root_context(engine(quick_view))
          set_context_property(context, "greeting", "Hello, World!")
          set_source(quick_view, QUrlFromLocalFile(path))
          QML.show(quick_view)
          exec()
        end
source
QML.exec_asyncFunction
function exec_async()

Similar to exec, but will not block the main process. This method keeps the REPL active and polls the QML interface periodically for events, using a timer in the Julia event loop.

source
QML.init_qmlengineFunction
function init_qmlengine()

Create a QML engine. You can modify the context of an engine using root_context. You can use an engine to create QQmlComponents. See the example for set_data. Note that you can also get the engine for a QQuickView using engine.

source
QML.qmlfunctionFunction
function qmlfunction(function_name::String, a_function)

Register a_function using function_name. If you want to register a function under it's own name, you can use @qmlfunction. Note, however, that you can't use the macro for registering functions from a non-exported module or registering functions wtih ! in the name.

source
QML.qt_prefix_pathFunction
function qt_prefix_path()

Equivalent to QLibraryInfo::location(QLibraryInfo::PrefixPath). Useful to check whether the intended Qt version is being used.

julia> using QML

julia> isdir(qt_prefix_path())
true
source
QML.root_contextFunction
root_context(an_engine::QQmlEngine)

Get the context of an_engine. Equivalent to QQmlEngine::rootContext. Use set_context_property to modify the context. Use context_property to get a particular property. Use to get the context of an engine created with init_qmlengine before using set_data or from engine.

julia> using QML

julia> an_engine = init_qmlengine();

julia> context = root_context(an_engine);

julia> set_context_property(context, "greeting", "Hello, World!");

julia> context_property(context, "greeting")
QVariant of type QML.QString with value Hello, World!

julia> component = QQmlComponent(an_engine);

julia> set_data(component, QByteArray("""
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          ApplicationWindow {
            visible: true
            Rectangle {
              Text {
                text: greeting
              }
              Timer {
                running: true
                onTriggered: Qt.quit()
              }
            }
          }
        """), QUrl())

julia> create(component, qmlcontext())

julia> exec()
source
QML.set_dataFunction
function set_data(component::QQmlComponent, data::QByteArray, file::QUrl)

Equivalent to QQmlComponent::setData. Use this to set the QML code for a QQmlComponent from a Julia string literal wrapped in a QByteArray. Also requires an empty QUrl. See QQmlComponent for an example.

source
QML.set_sourceFunction
function set_source(window::QQuickView, file::QUrl)

Equivalent to QQuickView::setSource. See the example for init_qquickview. The file path should be a path wrapped with QUrl.

source
QML.setconstructorMethod
function setconstructor(model::ListModel, constructor)

Add a constructor to a ListModel. The constructor will process appended items before they are added. Note that you can simply pass a list of arguments from QML, and they will be interpret in Julia as positional arguments.

julia> using QML

julia> items = ["A", "B"];

julia> array_model = ListModel(items, false);

julia> setconstructor(array_model, uppercase);

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          import QtQuick.Layouts 1.0
          ApplicationWindow {
            visible: true
            Button {
              text: "Add C"
              onClicked: array_model.append(["c"])
            }
            Timer {
              running: true
              onTriggered: Qt.quit()
            }
          }
          """)
          load(path; array_model = array_model)
          exec()
        end
source
QML.to_stringFunction
function to_string(data::QByteArray)

Equivalent to QByteArray::toString. Use to convert a QByteArray back to a string.

julia> using QML

julia> to_string(QByteArray("Hello, World!"))
"Hello, World!"
source
QML.@emitMacro
@emit signal_name(arguments...)

Emit a signal from Julia to QML. Handle signals in QML using a JuliaSignals block. See the example below for syntax.

Warning

There must never be more than one JuliaSignals block in QML

julia> using QML

julia> duplicate(value) = @emit duplicateSignal(value);

julia> @qmlfunction duplicate

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import QtQuick 2.2
          import QtQuick.Controls 1.1
          import QtQuick.Layouts 1.1
          import org.julialang 1.0
          ApplicationWindow {
              visible: true
              Column {
                TextField {
                    id: input
                    onTextChanged: Julia.duplicate(text)
                }
                Text {
                    id: output
                }
                JuliaSignals {
                  signal duplicateSignal(var value)
                  onDuplicateSignal: output.text = value
                }
                Timer {
                  running: true
                  onTriggered: Qt.quit()
                }
              }
          }
          """)
          load(path)
          exec()
        end
source
QML.@expand_dotsMacro
QML.@expand_dots object_.field_ func

Expand an expression of the form a.b.c to replace the dot operator by function calls.

julia> using QML

julia> @macroexpand QML.@expand_dots a.b.c.d f
:(f(f(f(a, "b"), "c"), "d"))
source
QML.@qmlfunctionMacro
@qmlfunction function_names...

Register Julia functions for access from QML under their own name. Function names must be valid in QML, e.g. they can't contain !. You can use your newly registered functions in QML by first importing org.julialang 1.0, and then calling them with Julia.function_name(arguments...). If you would like to register a function under a different name, use qmlfunction. This will be necessary for non-exported functions from a different module or in case the function contains a ! character.

julia> using QML

julia> greet() = "Hello, World!";

julia> @qmlfunction greet

julia> mktempdir() do folder
          path = joinpath(folder, "main.qml")
          write(path, """
          import org.julialang 1.0
          import QtQuick 2.0
          import QtQuick.Controls 1.0
          ApplicationWindow {
            visible: true
            Text {
              text: Julia.greet()
            }
            Timer {
              running: true
              onTriggered: Qt.quit()
            }
          }
          """)
          load(path)
          exec()
        end
source