- QML
- What's that?
- QML Syntax Basics
- QML Object Attributes
- Property Binding
- Signal and Handler Event System
--
- Qt Quick
- What's that?
- Qt Quick 1? Qt Quick 2?
- Visual types in QML
- Responding to User Input in QML
- Animations in QML
- Displaying Text in QML
- Layouts and positioners in QML
- Style and Theme Support
--
- Advanced Application Development Topics
- Best Practices for QML and Qt Quick
- QML Coding Conventions
- Performance Considerations And Suggestions
- Scalable User Interfaces
- Best Practices for QML and Qt Quick
Consult "The QML Reference" in Qt Help
Consult "QML Applications" in Qt Help
QML is a user interface specification and programming language
--
Is a declarative language that allows user interfaces to be described in terms of their visual components and how they interact and relate with one another
--
It allows developers and designers alike to create highly performant, fluidly animated and visually appealing applications
--
QML offers a highly readable, declarative, JSON-like syntax with support for imperative JavaScript expressions combined with dynamic property bindings
Building applications for:
- Desktop devices (Linux, MacOS, Windows)
- Mobile platforms (Android, iOS/tvOS/watchOS, WinRT)
- Embedded platforms (Embedded Linux, INTEGRITY, QNX, VxWorks)
--
--
--
--
Consult "Syntax basics" in Qt Help
- Imports
- Object declarations
- Child objects
- Comments
--
import QtQuick 2.12
import QtQuick.Controls 2.12
/*
Creating an ApplicationWindow is a lot
better than creating just a Window.
*/
ApplicationWindow {
id: app
width: 400
height: 500
color: 'plum' // This is a HTML color
}
Consult "QML Object Attributes" in Qt Help
- The id Attribute
- Property Attributes
- Signal Attributes
- Signal Handler Attributes
- Method Attributes
- Enumeration Attributes
Consult "Property Binding" in Qt Help
- Creating bindings
- Removing bindings
Consult "Signal and Handler Event System" in Qt Help
- Receiving signals with signal handlers
- Property change signal handlers
- Attached signal handlers
- Adding signals to custom QML types
- Connecting signals to methods and signals
--
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Challenge 1")
Button {
id: myButton
property int numberOfClicks: 0
anchors.centerIn: parent
text: "Ha dado " + numberOfClicks + " clicks"
onClicked: {
numberOfClicks++
}
}
}
--
- Bind the
height
of the button to40 * (numberOfClicks + 1)
- Set the
highlighted
property of the button totrue
if the number of clicks is even
--
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Challenge 1.1")
Button {
id: myButton
property int numberOfClicks: 0
anchors.centerIn: parent
height: 40 * (numberOfClicks + 1)
text: "Ha dado " + numberOfClicks + " clicks"
highlighted: numberOfClicks % 2 === 0
onClicked: {
numberOfClicks++
}
}
}
Consult "QML Applications" in Qt Help
Qt Quick is the standard library of types and functionality for QML
--
It includes visual types, interactive types, animations, models and views, particle effects and shader effects. A QML application developer can get access to all of that functionality with a single import statement
Qt Quick Controls 1 was originally developed to support desktop platforms, with mobile and embedded support coming shortly afterwards
--
They have a very broad scope, in that they provide a styling system flexible enough to allow the development of applications that have either a platform-dependent or platform-independent style
--
On embedded systems, where the hardware has limited resources, this approach can be inefficient. Qt Quick Controls 2 was designed to solve this problem
In many cases, the internal state of a control can be more efficiently processed in C++. For example, handling input events in C++ makes a difference for controls that would otherwise need to create internal MouseAreas
and attached Keys
objects
Not only does handling events and logic in C++ increase performance, but it allows the visual QML layer to be a simple, declarative layer on top. This is reflected in the structure of the controls project: all visual implementations sit in the imports folder, so that users who want to create their own complete style can copy the folder and start tweaking
--
In Qt Quick Controls 2, styles no longer provide components that are dynamically instantiated by controls, but controls themselves consist of item delegates that can be replaced
--
In Qt Quick Controls 1:
ScrollView {
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
Flickable {
// ...
}
}
In Qt Quick Controls 2:
Flickable {
// ...
ScrollBar.vertical: ScrollBar { }
}
--
The API of Qt Quick Controls 2 aims to be clean and simple. Common operations are easy, and more advanced ones are liberally documented with snippets that can be copied into your code
-- Qt Docs
--
Consult "Qt Quick", "Qt Quick Controls 2", "Use Case - Visual Elements In QML", "Qt Quick QML Types" and "Qt Quick Controls Examples" in Qt Help
Consult "Button", "Qt Quick Controls Guidelines" and "First Steps with QML" in Qt Help
Consult "Styling Qt Quick Controls 2" and related styles, and "Qt Quick Controls Configuration File" in Qt Help
--
Qt Quick Controls Configuration File:
[Controls]
Style=Material
[Material]
Theme=Light
Accent=Blue
Primary=BlueGray
Foreground=Brown
Background=Grey
Variant=Dense
Consult "Use case - Animations In QML", "Important Concepts in Qt Quick - States, Transitions and Animations" and "Animations and Transitions In Qt Quick" in Qt Help
--
Animations are a primary concept in QML
--
QML was designed to facilitate the creation of fluid UIs. These are user interfaces where the UI components animate instead of appearing, disappearing, or jumping abruptly.
--
Qt Quick provides two simple ways to have UI components move with animation instead of instantly appearing at their new location:
- States and Transitions
- Animating Property Changes (behaviors)
--
- PropertyAnimation
- NumberAnimation
- ColorAnimation
- RotationAnimation
... and more
--
Rectangle {
id: button
anchors.centerIn: parent
width: 75; height: 75
state: "RELEASED"
MouseArea {
anchors.fill: parent
onPressed: button.state = "PRESSED"
onReleased: button.state = "RELEASED"
}
states: [
State {
name: "PRESSED"
PropertyChanges { target: button; color: "blue"}
},
State {
name: "RELEASED"
PropertyChanges { target: button; color: "#BBBBFF"}
}
]
transitions: [
Transition {
from: "PRESSED"
to: "RELEASED"
ColorAnimation { target: button; duration: 200 }
},
Transition {
from: "RELEASED"
to: "PRESSED"
ColorAnimation { target: button; duration: 200 }
}
]
}
--
Rectangle {
id: button
anchors.centerIn: parent
width: 75; height: 75
color: mouseArea.pressed ? "red" : "yellow"
Behavior on color { ColorAnimation {} }
MouseArea {
id: mouseArea
anchors.fill: parent
}
}
--
Animate all changes to the height
property of the button of the previous challenge
--
SequentialAnimation {
id: someAnimation
NumberAnimation { /* ... */ }
PauseAnimation { duration: 1000 }
ColorAnimation { /* ... */ }
// ...
}
ParallelAnimation {
id: someAnimation
NumberAnimation { /* ... */ }
ColorAnimation { /* ... */ }
// ...
}
--
Design the following UI:
--
- Square side:
100
- Square position:
Centered
- Square color:
"teal"
- Square border color:
Qt.darker("teal")
- Square border width:
10
- Button text:
"Start"
- Button horizontal position:
Horizontally centered
- Button vertical position:
Bottom
,margin=10
- Style:
Material
- Material theme:
Light
- Material accent:
Blue
--
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
id: applicationWindow
visible: true
width: 640
height: 480
title: qsTr("Challenge 3")
Rectangle {
id: square
anchors.centerIn: parent
width: 100
height: width
color: "teal"
border { color: Qt.darker("teal"); width: 10 }
}
Button {
id: button
anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; bottomMargin: 10 }
text: "Start"
highlighted: true
}
}
--
Make the button smoothly ascend and disappear (at the same time) in 0.5 seconds when clicked
--
This can be done by adding the following lines to the button's code:
onClicked: {
buttonAnimation.start()
}
ParallelAnimation {
id: buttonAnimation
NumberAnimation { target: button; property: "anchors.bottomMargin"; to: 30; duration: 500 }
NumberAnimation { target: button; property: "opacity"; to: 0.0; duration: 500 }
}
--
Make the square grow to 200 pixels of side and then back to 100, repeatedly. All every second, after the button is clicked.
--
This can be done by adding the following code to the square's code:
SequentialAnimation {
id: animation
loops: Animation.Infinite
NumberAnimation { target: square; property: "width"; to: 200; duration: 500 }
NumberAnimation { target: square; property: "width"; to: 100; duration: 500 }
}
or...
SequentialAnimation on width {
id: animation
loops: Animation.Infinite
running: false // necessary when applying over a property ("on width") to make it not running from the beginning
NumberAnimation { to: 200; duration: 500 }
NumberAnimation { to: 100; duration: 500 }
}
--
And adding the following function to the square to run the animation:
function runAnimation() {
animation.start()
}
And calling it when the button is clicked (in the button's code):
onClicked: {
square.runAnimation()
buttonAnimation.start()
}
--
Make the button rotate 360º over itself every second, indefinitely.
--
This can be done by enclosing the square animation into a ParallelAnimation
:
ParallelAnimation {
id: animation
RotationAnimation { target: square; to: from + 360; duration: 1000; loops: Animation.Infinite }
SequentialAnimation {
loops: Animation.Infinite
NumberAnimation { target: square; property: "width"; to: 200; duration: 500 }
NumberAnimation { target: square; property: "width"; to: 100; duration: 500 }
}
}
--
Create two new components: Square
and AnimatedButton
and put the respective objects there.
Consult "Text", "Use Case - Displaying Text In QML" and "Supported HTML Subset" in Qt Help
- Text properties
- HTML
--
Text {
text: "Hello World!"
color: "#9C27B0"
font { pointSize: 24; bold: true; italic: true }
}
Text {
text: "<i>Hello <b>World!</b></i>"
}
Consult "Use Case - Positioners and Layouts In QML" and all the links provided in that page in Qt Help
- Manual Positioning
- Anchors
- Layouts
--
--
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
RowLayout {
anchors.fill: parent
spacing: 6
Rectangle {
color: 'azure'
Layout.preferredWidth: 100
Layout.preferredHeight: 150
}
Rectangle {
color: "plum"
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
Consult "Best Practice Guides" in Qt Help
Consult "Best Practices for QML and Qt Quick" in Qt Help
Consult "QML Coding Conventions" in Qt Help
--
Throughout our documentation and examples, QML object attributes are always structured in the following order:
- id
- property declarations
- signal declarations
- JavaScript functions
- object properties
- child objects
- states
- transitions
For better readability, we separate these different parts with an empty line.
If using multiple properties from a group of properties, consider using group notation instead of dot notation if it improves readability
--
For example, this:
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.right: parent.right
anchors.leftMargin: 20
}
could be written like this:
Rectangle {
anchors {
left: parent.left
top: parent.top
right: parent.right
leftMargin: 20
}
}
If a list contains only one element, we generally omit the square brackets
--
For example, it is very common for a component to only have one state. In this case, instead of
states: [
State {
name: "open"
PropertyChanges { target: container; width: 200 }
}
]
We will write this:
states: State {
name: "open"
PropertyChanges { target: container; width: 200 }
}
If the script is a single expression, we recommend writing it inline:
Rectangle { color: "blue"; width: parent.width / 3 }
--
If the script is only a couple of lines long, we generally use a block:
Rectangle {
color: "blue"
width: {
var w = parent.width / 3
console.debug(w)
return w
}
}
--
If the script is more than a couple of lines long or can be used by different objects, we recommend creating a function and calling it like this:
function calculateWidth(object)
{
var w = object.width / 3
// ...
// more javascript code
// ...
console.debug(w)
return w
}
Rectangle { color: "blue"; width: calculateWidth(parent) }
--
For long scripts, we will put the functions in their own JavaScript file and import it like this:
import "myscript.js" as Script
Rectangle { color: "blue"; width: Script.calculateWidth(parent) }
Consult "Performance Considerations And Suggestions" in Qt Help
QML is more or less an interpreted programming language. This will radically change in Qt 6, as we want to make the QML core based on C++ instead of JavaScript
--
Currently, as Qt 5 uses a JavaScript backend for QML, there are some performance considerations that developers must take in account, mainly when targeting low-power devices such as mobile or embedded systems
As an application developer, you must strive to allow the rendering engine to achieve a consistent 60 frames-per-second refresh rate. 60 FPS means that there is approximately 16 milliseconds between each frame in which processing can be done, which includes the processing required to upload the draw primitives to the graphics hardware
--
In practice, this means that the application developer should:
- use asynchronous, event-driven programming wherever possible
- use worker threads to do significant processing
- never manually spin the event loop
- never spend more than a couple of milliseconds per frame within blocking functions
Failure to do so will result in skipped frames, which has a drastic effect on the user experience.
--
Note: A pattern which is tempting, but should never be used, is creating your own QEventLoop
or calling QCoreApplication::processEvents()
in order to avoid blocking within a C++ code block invoked from QML. This is dangerous, because when an event loop is entered in a signal handler or binding, the QML engine continues to run other bindings, animations, transitions, etc. Those bindings can then cause side effects which, for example, destroy the hierarchy containing your event loop.
Most QML applications will have a large amount of JavaScript code in them, in the form of dynamic functions, signal handlers, and property binding expressions
--
Due to the deep link between QML and JavaScript, loading a JS function can be faster than loading a C++ method. However, executing JS code is many times slower than executing C++ code. You should not use JS for long-time processing, use C++ instead.
--
Things to avoid in binding expressions to maximize optimizability:
- declaring intermediate JavaScript variables
- accessing "var" properties
- calling JavaScript functions
- constructing closures or defining functions within the binding expression
- accessing properties outside of the immediate evaluation scope
- writing to other properties as side effects
- Text:
- Use the
PlainText
format instead ofStyledText
wherever possible, setting it explicitly - Use only
StyledText
only if you want to use HTML tags - You should only use AutoText if the text might be (but probably isn't)
StyledText
as this mode will incur a parsing cost
- Use the
--
- Images:
- Use
asynchronous
loading on large images - Set an explicit
sourceSize
to keep in memory a smaller image - Avoid smoothing images by enabling
image.smooth
only if the size of the image is not equal to itssourceSize
- Avoid run-time composition by providing the pre-composed image resource with your application (for example, providing elements with shadow effects)
- Use
Avoid running JavaScript during animation. For example, running a complex JavaScript expression for each frame of an x
property animation should be avoided. Developers should be especially careful using script animations, as these are run in the main thread (and therefore can cause frames to be skipped if they take too long to complete).
--
Consult "Performance Considerations And Suggestions" in Qt Help
Consult "Scalability", "High DPI Displays" and "High-DPI Support in Qt Quick Controls 2" in Qt Help
Warning: Qt Quick Controls 1 are not compatible with Qt's automatic high-DPI scaling. There is currently no available solution for high-DPI support in applications wishing to mix Qt Quick Controls 1 and Qt Quick Controls 2.
--
-
Text:
- Use
font.pointSize
instead offont.pixelSize
- Use
-
Images and Icons:
- Use SVG for icons and small images
--
- Set the minutes and seconds in the
SpinBox
- Click the START button to begin the countdown using a
Timer
- Show the remaining time in the upper
Label
- The text of the button must be STOP while the timer is running or the alarm is active
- When the timeout comes, activate the alarm by animating the color of the window from "red" to "white" every 1s until the user clicks STOP to bring the application to the initial state
- Integrating QML with a backend language
- C++
- Python
- Go (no officially supported)
- Model/View Programming with QML
- Model
- Delegates
Thank you for coming!