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:
- You can simply
load
a QML file as aQML.QQmlApplicationEngine
. QML.QQuickView
creates a window, so it's not necessary to wrap the QML inApplicationWindow
.- You can run QML code contained in a string with
QQmlComponent
.
Interaction
Interaction with Julia happens through the following mechanisms:
- Call Julia functions from QML, e.g. with
@qmlfunction
. - Read and set context properties from Julia and QML, e.g. with keywords to
load
orset_context_property
. - Emit signals from Julia to QML, e.g. with
@emit
. - Use data models, e.g.
ListModel
orJuliaPropertyMap
.
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.QML
QML.JuliaDisplay
QML.JuliaPropertyMap
QML.ListModel
QML.QByteArray
QML.QQmlApplicationEngine
QML.QQmlComponent
QML.QQuickView
QML.QTimer
QML.QUrl
QML.addrole
QML.content_item
QML.context_property
QML.create
QML.engine
QML.exec
QML.exec_async
QML.init_qmlengine
QML.init_qquickview
QML.qmlcontext
QML.qmlfunction
QML.qt_prefix_path
QML.removerole
QML.roles
QML.root_context
QML.set_context_property
QML.set_data
QML.set_source
QML.setconstructor
QML.show
QML.to_string
QML.@emit
QML.@expand_dots
QML.@qmlfunction
QML.QML
— ModuleModule 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.
QML.JuliaDisplay
— Typestruct 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
QML.JuliaPropertyMap
— Methodfunction 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
QML.ListModel
— Methodfunction 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
QML.QByteArray
— Typefunction QByteArray(a_string::String)
Use to pass text to set_data
.
QML.QQmlApplicationEngine
— Typestruct 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
QML.QQmlComponent
— Typestruct 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()
QML.QQuickView
— Typestruct 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
QML.QTimer
— Typestruct 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
QML.QUrl
— Typestruct QUrl([filename::String])
Used to pass filenames to set_source
. Pass an empty url (no arguments) to set_data
.
FileIO.load
— Methodfunction load(qml_file::String; properties...)
Load a QML file, creating a QML.QQmlApplicationEngine
, and setting the context properties
supplied in the keyword arguments. Will create and return a QQmlApplicationEngine
. See the example for QML.QQmlApplicationEngine
.
QML.addrole
— Functionfunction 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")
QML.content_item
— Functionfunction 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
QML.context_property
— Functionfunction context_property(context::QQmlContext, item::AbstractString)
Get a context property. See the example for root_context
.
QML.create
— Functionfunction 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
.
QML.engine
— Functionfunction 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
QML.exec
— Functionfunction exec()
Fill out a window. Use with a QQmlApplicationEngine
, QQuickView
, or QQmlComponent
. Note that after calling exec
, you will need to reregister functions, e.g. with [@qmlfunction
], if you want to exec
again.
QML.exec_async
— Functionfunction 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.
QML.init_qmlengine
— Functionfunction init_qmlengine()
Create a QML engine. You can modify the context of an engine using root_context
. You can use an engine to create QQmlComponent
s. See the example for set_data
. Note that you can also get the engine for a QQuickView
using engine
.
QML.init_qquickview
— Functionfunction init_qquickview()
Create a QQuickView
.
QML.qmlcontext
— Functionfunction qmlcontext()
Create an empty context for QML. Required for create
.
QML.qmlfunction
— Functionfunction 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.
QML.qt_prefix_path
— Functionfunction 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
QML.removerole
— Methodfunction removerole(model::ListModel, name::AbstractString)
Remove one of the roles
from a ListModel
. See the example for addrole
.
QML.roles
— MethodQML.root_context
— Functionroot_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()
QML.set_context_property
— Functionset_context_property(context::QQmlContext, name::String, value::Any)
Set properties. See root_context
for an example.
QML.set_data
— Functionfunction 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.
QML.set_source
— Functionfunction 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
.
QML.setconstructor
— Methodfunction setconstructor(model::ListModel, constructor)
Add a constructor to a ListModel
. The constructor
will process append
ed 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
QML.show
— Function function QML.show()
Equivalent to QQuickView::show
. See example for QQuickView
.
QML.to_string
— Functionfunction 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!"
QML.@emit
— Macro@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.
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
QML.@expand_dots
— MacroQML.@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"))
QML.@qmlfunction
— Macro@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