# QML

# QML vs Qt Quick

  • QML is a language: Qt Markup Language
  • QML is the user interface to a C++ or Python (PySide2) application
  • QML applications can use javascript and interface with the backend C++/Python.
  • QML application has access to all Qt through bindings and connections.

Qt Quick is the standard library for all QML applications, providing the basic types for creating animating visual elements.


QML focuses on the user interface, rendering, and visual effects!

TIP

You should not try to write high end encryption or anything like that in QML, You should let that be handled in the back end where it belongs.

So the question becomes why would you use QML over Qt Widgets?


Qt Widgets is only a desktop, specifically Linux Mac Windows.

QML on the other hand runs on all of these things, so you can just build your app on Linux and it'll run on Mac, Windows, Android, IOS and even on a Raspberry Pi, although, you will have to tweak it for the different interfaces and screen sizes and things like that.

But if you use you know good layouts and things of that nature, it will scale seamlessly.

Limitations of QML

It's really hard to say because it depends on your specific use case.

Anything non graphical, for example a command line, is when you should not use QML

Anything graphical, my first go to would be QML.

# QML vs QtWidgets

Qt Widgets QML
Legacy Newer
Long term support Long term support
Tight desktop integration Easy animations
Windows, Mac, Linux major desktops States and transitions
Really attemps to look native Separates GUI from implementation
Runs pretty much anywhere desktop, mobile, embedded

Anything a QtWidgets application can do, a QML application can do slightly better.

Cross Platform

How to Write once build anywhere?

Build on target platforms - copy the code to the target platform and build it.

# Exploring Qt Creator

First, to know what you have or reinstall some components you can go to the Qt folder and run the MaintenanceTool.

# Qt Creator Kits

You have the same code but then you have different ways of compiling it.

These are called kits and builds.


Qt Creator Kits

You can configure your kit by going to Tools --> Options --> Kits.

TIP

For multiple open projects, when you add an class the class maybe put in the other projects folder. That's why its better to work one at a time.

# First QML application

Remember QML is the language Qt Quick is the foundation. The actual components that we're going to use, to build that application.


So, go New Project --> Qt Quick application and choose:


QML application

Now the difference between QML and Qt Quick is right here.

This is all QML, but these individual widgets or components those are Qt Quick.




 





 


 










import QtQuick 2.12
import QtQuick.Controls 2.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Scroll")

    ScrollView {
        anchors.fill: parent

        ListView {
            width: parent.width
            model: 20
            delegate: ItemDelegate {
                text: "Item " + (index + 1)
                width: parent.width
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

at the main.cpp we have:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Here We are just simply setting QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); that's for devices. Some devices enable that some don't.

They are specific for cell phones, really, actually shouldn't say specific for cell phones, but cell phones use that quite a bit.


Then, we're making a QGuiApplication app(argc, argv); and just starting out our event loop here return app.exec();


Then, we have a QQmlApplicationEngine engine; application engine, big long class name, But basically, this is the engine that's going to run the actual file, and you notice right off the bat this is in a resource const QUrl url(QStringLiteral("qrc:/main.qml"));.

Previous versions would actually have these QML files out on the hard drive, and you would have to distribute the binary and the QML file.

That's all a thing of the past.

Now, everything's compiled right in as a resource.

So, you just have to distribute your binary with the cute libraries, of course.


Then we have a QObject::connect Object.

Really what that's doing is just some safety checking. Saying, hey, if it's not the right url and object then we're just going to exit with -1.

The magic really is the engine.load(url);

So we're just loading this into the engine, and what is getting load is the QML file.

# Creating an empty Qt Quick application

This would be the "Hello World" of QML


QML Editor vs Designer

TIP

CRTL + Space for intellisense

and if you select something and F1 you bring the integrated help file.

# Interesting questions


Questions


Questions

# Section 3: Diving into QML

# First Program with an image

Image from the web:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Image {
        id: myimage
        source: "https://upload.wikimedia.org/wikipedia/commons/0/0b/Qt_logo_2016.svg"
        anchors.centerIn: parent
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Images

TIP

Comments are like normal C++

//This is a comment!!!

/*
  This is a multi-line comment
  */
1
2
3
4
5

Image from the computer:


First we have to add the source to the qml.qrc. So right click on qml.qrc -> Open With -> Resource Editor.

Then right click on the image and copy URL.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    //This is a comment!!!

    /*
      This is a multi-line comment
      */

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        anchors.centerIn: parent
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

TIP

ID must be unique

Root Object - There can be only one


Images

An image with a rectangle inside of it:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100

        Rectangle {
            color: "red"
            width: parent.width
            height: parent.height
            opacity: 0.5 // 50%
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Images

Centering in the window:
















 










import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        anchors.centerIn: parent

        Rectangle {
            color: "red"
            width: parent.width
            height: parent.height
            opacity: 0.5 // 50%
        }
    }
}
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

Images

The Top level object is the root object and there can be only one root object

TIP

Now let's talk about why can there only be one.

It's the way Q does memory management if you understand QT core, QT uses what's called a parent child hierarchy to manage memory.

So when the parent is destroyed all subsequent children are destroyed also.

So it only has to track one object.

if we come to the main.cpp file we can see that the engine is loading the url.










 
 





 




#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# QML objects vs QObjects

Creating a text input on the image

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        anchors.centerIn: parent
        opacity: 0.25
    }

    TextInput {
        id:myinput
        text: "Hello World"
        anchors.centerIn: parent
    }
}
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

Images

Binding text with the input text

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        anchors.centerIn: parent
        opacity: 0.25
    }

    TextInput {
        id:myInput
        text: "Hello World"
        anchors.centerIn: parent
        font.pixelSize: 25
    }

    Text {
        id:myText
        text: myInput.text
        font.pixelSize: 25
    }
}
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

So we're gonna go to this text input and you can see it in real time changing up here so as we type that signal is emitted and it's going out to that slot and that's all happening because of property bindings.

There's a lot more that actually happens under the hood but I wanted you to be aware that that's kind of what's going on here.


Images

# X, Y, Z positioning

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        x: 100
        y: 100
    }

    Rectangle {
        color: "red"
        width: 200
        height: 200
        x: 50
        y: 50
        opacity: 0.5
    }

    Rectangle {
        color: "blue"
        width: 200
        height: 200
        x: 150
        y: 150
        opacity: 0.5
    }
}
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

XY

Using Z to position the layers

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        x: 100
        y: 100
        z: 4
    }

    Rectangle {
        color: "red"
        width: 200
        height: 200
        x: 50
        y: 50
        z: 2
        opacity: 0.5
    }

    Rectangle {
        color: "blue"
        width: 200
        height: 200
        x: 150
        y: 150
        z: 1
        opacity: 0.5
    }
}
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
38
39
40

XY

TIP

The lower the Z the lower the position

If the Z is all the same, the last image on the stack stays on top.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Rectangle {
        color: "red"
        width: 200
        height: 200
        x: 50
        y: 50
        z: 0
        opacity: 0.5
    }

    Rectangle {
        color: "blue"
        width: 200
        height: 200
        x: 150
        y: 150
        z: 0
        opacity: 0.5
    }

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        x: 100
        y: 100
        z: 0
    }
}
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
38
39
40

XY

# Parent and child transformations

# Parent and children Relationships

Because the rectangle is a child of the image the location is related to the image and not to the window

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        x: 100
        y: 100
        z: 0

        Rectangle {
            color: "red"
            width: 50
            height: 50
            x: 0
            y: 0
            z: 0
            opacity: 0.5
        }
    }
}
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

Parent Child

Another example where the relationship between parent and child is used to position a rectangle in the bottom right corner.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        x: 100
        y: 100
        z: 0

        Rectangle {
            color: "red"
            width: 50
            height: 50
            x: 0
            y: 0
            opacity: 0.5
        }

        Rectangle {
            color: "red"
            x: parent.width - width
            y: parent.height - height
            width: 50
            height: 50
            opacity: 0.5
        }
    }
}
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
38

Parent Child

An important thing to notice is that the parent will always be on the bottom layer no matter the z position. Although there are some ways to do that its not the rule.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    id: root

    Image {
        id: myimage
        source: "qrc:/IMAGES/16-ShadersClass-02.png"
        width: 150
        height: 100
        x: 100
        y: 100
        z: 3

        Rectangle {
            color: "red"
            width: 50
            height: 50
            x: -25
            y: -25
            z: 1
            opacity: 0.5
        }

        Rectangle {
            color: "red"
            x: parent.width - width
            y: parent.height - height
            z: 0
            width: 50
            height: 50
            opacity: 0.5
        }
    }
}
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
38
39
40

Parent Child

TIP

Consider the parent as a table top and the children are layed on top of the table.

# Object interaction - TapHandler

This program will change the color of the rectangle between blue and red when its pressed.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        anchors.centerIn: parent
        width: 100
        height: 100

        color: inputHandler.pressed ? "red" : "blue"

        TapHandler {
            id: inputHandler
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

TapHandle


TapHandle

Challenge: Code a program with 2 rectangles with independent TapHandle

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        anchors.centerIn: parent
        width: 100
        height: 100
        id: rect1

        color: inputHandler.pressed ? "red" : "blue"

        TapHandler {
            id: inputHandler
        }
    }

    Rectangle {
        anchors.right: rect1.left; anchors.rightMargin: 50
        anchors.bottom: rect1.bottom
        width: 100
        height: 100
        id: rect2

        color: inputHandler2.pressed ? "red" : "blue"

        TapHandler {
            id: inputHandler2
        }
    }
}
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

TapHandle


TapHandle

# Section 4: Basic types

# Item QML type

Item is the QML version of the QObject.

It defines anchors, it defines antiliasing, children, the children rectangle, clipping, data, on and on and on and on.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Item {
        id: myItem
        anchors.centerIn: parent
        width: 100
        height: 100

        Rectangle {
            color: "red"
            anchors.fill: parent
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Item QML Type

The power of the item component doesn't really come into play until you get a little bit more advanced; and that's why it's one of the most misunderstood terms.

They push this on you early because it is the base for all visual types, but it itself is non visual, and it makes no sense why this thing even exists.

It exists solely so that you can make your own components, and you can say I want to define my own custom object.

# Rectangle

Its an Item.

There is no circle

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myItem
        anchors.centerIn: parent
        color: "red"
        width: 200
        height: 200
        visible: true //code just to exemplify that you can have an invisible one.

        border.color: "black"
        border.width: 5
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Rectangle





















 



import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myItem
        anchors.centerIn: parent
        color: "red"
        width: 200
        height: 200
        visible: true //code just to exemplify that you can have an invisible one.

        border.color: "black"
        border.width: 5

        radius: 30
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Rectangle





















 



import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myItem
        anchors.centerIn: parent
        color: "red"
        width: 200
        height: 200
        visible: true //code just to exemplify that you can have an invisible one.

        border.color: "black"
        border.width: 5

        radius: width
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Rectangle

# Gradients

So what we're doing is we're saying the gradient property of the rectangle. We're going to create a new child called a gradient.























 
 
 




import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myItem
        anchors.centerIn: parent
        color: "red"
        width: 200
        height: 200
        visible: true //code just to exemplify that you can have an invisible one.

        border.color: "black"
        border.width: 5

        radius: width

        gradient: Gradient {
            GradientStop {position: 0.0; color: "red"}
            GradientStop {position: 1.0; color: "blue"}
        }
    }
}
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

Rectangle

# Image QML Type

This thing is awesome.

You can actually build upon this and do like an animated image or an animated sprite so you can actually do animations very quickly.

WARNING

Image source with local or remote file there's something you need to be aware of.

First off this uses open geo meaning it's lightning quick.

One of the caveats being is that if you have a local file, that local file is preloaded when your application starts. So if you have a massive graphic it's going to try and load that application start which will kill your startup time. So buyer beware.

Don't load massive graphics into a program

a .qrc file is a qT resource content file, meaning everything in this file gets compiled and baked right into the binary executable which includes our .qml file that we've been working with.


We're just gonna right click open with resource editor; and we're gonna add a prefix, prefix is just a fancy term for virtual directory. So we're gonna make a virtual directory called images.


Image

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Image {
        id: localImage
        source: "Images/IMAGES/SMILE FACE.jpg"

        width: 500
        fillMode: Image.PreserveAspectFit
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Image

Now we want to try and position this and I'm gonna make what's called a property, which has a type a name and a value.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    property var middle: height / 2

    Image {
        id: localImage
        source: "Images/IMAGES/SMILE FACE.jpg"

        width: 300
        fillMode: Image.PreserveAspectFit

        x: 300
        y: middle - 100
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Image

TIP

You can refer to a web url.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    property var middle: height / 2

    Image {
        id: localImage
        source: "Images/IMAGES/SMILE FACE.jpg"

        width: 300
        fillMode: Image.PreserveAspectFit

        x: 300
        y: middle - 100
    }

    Image {
        id: remoteImage
        source: "https://upload.wikimedia.org/wikipedia/commons/d/d3/Qt_logo_2015.svg"

        width: 100
        fillMode: Image.PreserveAspectFit

        x: 100
        y: middle

        onProgressChanged: console.log(remoteImage.progress)
        onStatusChanged: if(remoteImage.status == image.Ready) console.log("Remote image loaded")
    }
}
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

Image

# Text

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Text {
        id: myText
        text: qsTr("<html><b>Hello</b><i><font color='green'>World </font></i><html>")
        anchors.centerIn: parent
        font.pointSize: 35
        font.bold: true
        font.italic: false
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Text

TIP

If the inline html is overridden by the QML font property sometimes it will not work.

We can also format the html on the code like this:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Text {
        id: myText
        text: qsTr("
        <html>
        <b>Hello</b>
        <i><font color='green'>World</font></i><br>
        <font color='#C0C0C0'>This is a test</font>
        <html>
        ")
        anchors.centerIn: parent
        font.pointSize: 35
        font.bold: true
        font.italic: false
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Text

We can use a href from html:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Text {
        id: myText
        text: qsTr("
        <html>
        <b>Hello</b>
        <i><font color='green'>World</font></i><br>
        <font color='#C0C0C0'>This is a test</font><br>
        <a href='https://www.skinnyrobot.co.nz'>Skinny Robot</a>
        <html>
        ")
        onLinkActivated: Qt.openUrlExternally(link);
        anchors.centerIn: parent
        font.pointSize: 35
        font.bold: true
        font.italic: false
    }
}
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

TIP

Remember to use this onLinkActivated: Qt.openUrlExternally(link);, or it will not work


Text

We can also play with some basic javascript, and change the text to bold and back on mouse hover.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Text {
        id: myText
        text: qsTr("
        <html>
        <b>Hello</b>
        <i><font color='green'>World</font></i><br>
        <font color='#C0C0C0'>This is a test</font><br>
        <a href='https://www.skinnyrobot.co.nz'>Skinny Robot</a>
        <html>
        ")
        onLinkHovered: {
            console.log("Hover: " + link)
            if(link) {
                myText.font.bold = true
            } else {
                myText.font.bold = false
            }
        }

        onLinkActivated: Qt.openUrlExternally(link);
        anchors.centerIn: parent
        font.pointSize: 35
        font.bold: true
        font.italic: false
    }
}
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

Text

# Mouse Area

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myItem
        color: "blue"
        width: 200
        height: 200
        anchors.centerIn: parent

        MouseArea {
            id: myMouse
            anchors.fill: parent
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            hoverEnabled: true

            onClicked: {
                console.log("Clicked: " + mouse.button)
                if(mouse.button === Qt.LeftButton) parent.color = "green"
                if(mouse.button === Qt.RightButton) parent.color = "red"
            }

            onDoubleClicked: console.log("Double Clicked: " + mouse.button)
            onPositionChanged: console.log("Position X: " + mouseX + " Y: " + mouseY)
            onEntered: parent.color = "orange"
            onExited: parent.color = "blue"
        }
    }
}
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

Text

TIP

And let's just recap here is you have to acceptedButtons if you want anything more than a left button.

hoverEnabled has to be true, if you want to monitor position, onEntered or onExited.

Double click and click work by default, but again it's going to backbone on your accepted buttons and of course you have to have this fill some type of area.

You can either drop an anchor or you can do a width and height.

# Custom component

To create a custom component go to the resource file and Add New --> QT --> QML File --> Use CamelCase to name the file.

Check if it went well by checking the intellisense (Write the beginning of the component name) on the main.qml.


MyButton.qml

import QtQuick 2.0

Item {
    id: root
    width: 100
    height: 100
    property color color: "#C0C0C0"
    property color colorClicked: "green"
    property string title: "Click Me"


    Rectangle {
        id: myRec
        anchors.fill: parent
        color: root.color

        Text {
            id: display
            text: root.title
            anchors.centerIn: parent
        }

        MouseArea {
            id: mouseArea
            anchors.fill: parent
            onPressed: parent.color = root.colorClicked
            onReleased: parent.color = root.color
        }
    }
}
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

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    property var middle: (height / 2) - 50

    MyButton {
        id: button1
        x: 100
        y: middle
        color: "red"
        colorClicked: "orange"
        title: "Button 1"

    }

    MyButton {
        id: button2
        x: (parent.width / 2) - (width / 2)
        y: middle
        color: "blue"
        colorClicked: "orange"
        title: "Button 2"

    }

    MyButton {
        id: button3
        x: parent.width - 200
        y: middle
        color: "green"
        colorClicked: "orange"
        title: "Button 3 <br> Testing"

    }

    MyButton {
        id: button4
        x: (parent.width / 2) - (width / 2)
        y: middle + 120
        width: 500
        height: 40
        color: "lightblue"
        colorClicked: "orange"
        title: "<b>Super</b> <i>Long</i> <u>Button</u>"
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

Custom Component

# Section 5

# Object positioning using X, Y

TIP

Then anchors will always win the position settings.

When you set an anchors QML ignores any manual positioning

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myShape
        width: 100
        height: 100
        color: "orange"

        function update() {
            console.log(x + " x " + y)
            label.text = Math.round(x) + " x " + Math.round(y)
        }

        x: 100
        y: 100
//        anchors.centerIn: parent

        Component.onCompleted: update()

        Text {
            id: label
            text: qsTr("text")
            anchors.centerIn: parent
        }

        MouseArea {
            anchors.fill: parent
        }
    }
}
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

Positioning XY

and if we use the anchors the value will be wrong because the manual positioning will be ignored

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myShape
        width: 100
        height: 100
        color: "orange"

        function update() {
            console.log(x + " x " + y)
            label.text = Math.round(x) + " x " + Math.round(y)
        }

        x: 100
        y: 100
        anchors.centerIn: parent

        Component.onCompleted: update()

        Text {
            id: label
            text: qsTr("text")
            anchors.centerIn: parent
        }

        MouseArea {
            anchors.fill: parent
        }
    }
}
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

Positioning XY

Now let's update our rectangle position based on the mouse drag.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myShape
        width: 100
        height: 100
        color: "orange"

        function update() {
            console.log(x + " x " + y)
            label.text = Math.round(x) + " x " + Math.round(y)
        }

        x: 100
        y: 100

        onXChanged: update()
        onYChanged: update()
        // @disable-check M16
        Component.onCompleted: update()

        Text {
            id: label
            text: qsTr("text")
            anchors.centerIn: parent
        }

        MouseArea {
            anchors.fill: parent
            drag.target: parent
        }
    }
}
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
38
39
40

Positioning XY

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: myShape
        width: 100
        height: 100
        color: "orange"

        function update() {
            console.log(x + " x " + y)
            label.text = Math.round(x) + " x " + Math.round(y)
        }

        x: 100
        y: 100

        onXChanged: update()
        onYChanged: update()
        // @disable-check M16
//        Component.onCompleted: update()

        Text {
            id: label
            text: qsTr("text")
            anchors.centerIn: parent
        }

        MouseArea {
            anchors.fill: parent
            drag.target: parent
        }
    }
}
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
38
39
40

TIP

// @disable-check M16, is a necessary comment for a bug in the type info that is generated for controls for use of QtCreator

Obs.: Component.onCompleted: update() is necessary in this case because we want the numbers to appear as soon as the component is loaded, otherwise it would be just the text.


Positioning XY

# Object positioning using Z

This example will help us understand the z drag axis.

For this we will add a new QML file.

MyShape.qml

import QtQuick 2.0

Rectangle {
    width: 100
    height: 100
    color: "gray"
    
    MouseArea {
        anchors.fill: parent
        drag.target: parent
        onClicked: parent.z++
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

on this component we are going to increment the z position when clicked.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    MyShape {
        color: "red"
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

So with this we have a red rectangle that can be dragged around.


Positioning Z

Lastly, we are going to mess around with the initial positions se we can have it more organized on the begining of the program

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    property var midX: width / 2
    property var midY: height / 2

    MyShape {
        color: "red"
        x: midX - (width / 2)
        y: midY - (height / 2) - 30
    }

    MyShape {
        color: "green"
        x: midX - (width / 2) - 55
        y: midY - (height / 2) + 30
    }

    MyShape {
        color: "blue"
        x: midX - (width / 2) + 55
        y: midY - (height / 2) + 30
    }
}
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

Positioning Z

# Column layout

Using the same custom component from last example we are going to create a column with three components stacked with a spacing of 2 between them.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Column {
        anchors.centerIn: parent
        spacing: 2
        MyShape { color: "red" }
        MyShape { color: "green" }
        MyShape { color: "blue" }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Columns

Of course we can still move things around and play with the z position on click.


Columns

# Row layout

Self explanatory

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Row {
        anchors.centerIn: parent
        spacing: 2
        MyShape { color: "red" }
        MyShape { color: "green" }
        MyShape { color: "blue" }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Rows

# Grid Layout

If we don't specify the number of columns or row the grid auto arrange

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Grid {
        anchors.centerIn: parent
        spacing: 2

        MyShape { color: "red" }
        MyShape { color: "green" }
        MyShape { color: "blue" }
        MyShape { color: "yellow" }
        MyShape { color: "brown" }
        MyShape { color: "purple" }
        MyShape { color: "black" }
        MyShape { color: "gray" }
        MyShape { color: "teal" }
        MyShape { color: "orange" }
    }
}
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

Grid

Although we can specify the rows and columns, the components will be stacked in order of creation.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Grid {
        anchors.centerIn: parent
        spacing: 2
        rows: 1
        columns: 1

        MyShape { color: "red" }
        MyShape { color: "green" }
        MyShape { color: "blue" }
        MyShape { color: "yellow" }
        MyShape { color: "brown" }
        MyShape { color: "purple" }
        MyShape { color: "black" }
        MyShape { color: "gray" }
        MyShape { color: "teal" }
        MyShape { color: "orange" }
    }
}
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

Notice that the last created is the orange.


Grid

But if we move them around we can see the other ones.


Grid

3 rows and 3 columns


Grid

TIP

The order can be a little frustrating because the program will try to guess the order you like, normally it goes from top to bottom left to right.

# Flow layout

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        color: "pink"
        anchors.centerIn: parent
        width: 300
        height: 400

        clip: false
    }

    Flow {
        anchors.centerIn: parent

        MyShape { color: "red" }
        MyShape { color: "green" }
        MyShape { color: "blue" }
        MyShape { color: "yellow" }
        MyShape { color: "brown" }
        MyShape { color: "purple" }
        MyShape { color: "black" }
        MyShape { color: "gray" }
        MyShape { color: "teal" }
        MyShape { color: "orange" }
    }
}
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

Flow

We can define a flow orientation like TopToBottom, or LeftToRight

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        color: "pink"
        anchors.centerIn: parent
        width: 300
        height: 400

        clip: false
    }

    Flow {
        anchors.centerIn: parent

        flow: Flow.TopToBottom

        MyShape { color: "red" }
        MyShape { color: "green" }
        MyShape { color: "blue" }
        MyShape { color: "yellow" }
        MyShape { color: "brown" }
        MyShape { color: "purple" }
        MyShape { color: "black" }
        MyShape { color: "gray" }
        MyShape { color: "teal" }
        MyShape { color: "orange" }
    }
}
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

Flow

And we can use the clip option to click the components inside the rectangle

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        color: "pink"
        anchors.centerIn: parent
        width: 300
        height: 400

        clip: true


        Flow {
            anchors.centerIn: parent

            flow: Flow.LeftToRight
            spacing: 5

            MyShape { color: "red" }
            MyShape { color: "green" }
            MyShape { color: "blue" }
            MyShape { color: "yellow" }
            MyShape { color: "brown" }
            MyShape { color: "purple" }
            MyShape { color: "black" }
            MyShape { color: "gray" }
            MyShape { color: "teal" }
            MyShape { color: "orange" }
        }
    }
}
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

Flow

# Anchors

First we are going to improve the MyShape component, by encapsulating it in an item.

import QtQuick 2.0


Item {
    id: root
    property color color: "gray"
    property string text: "title"

    width: 100
    height: 100

    Rectangle {
        color: root.color
        anchors.fill: parent

        property string text: ""
        Text {
            text: root.text
            anchors.centerIn: parent
        }

        MouseArea {
            anchors.fill: parent
            drag.target: root.parent
            onClicked: root.parent.z++
        }
    }
}
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
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    MyShape {
        id: shareCentral
        anchors.centerIn: parent
        text: "Hello"
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Anchors

If we now set a rectangle inside the component but not use the anchors, it will be rendered inside the component but, sometimes, when you move the component the rectangle won't follow.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    MyShape {
        id: shareCentral
        anchors.centerIn: parent
        text: "Hello"

        Rectangle {
            width: 25
            height: 25
            color: "pink"
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

And we can play arround with the anchors

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    MyShape {
        id: shareCentral
        anchors.centerIn: parent
        text: "Hello"

        Rectangle {
            width: 25
            height: 25
            color: "pink"
//            anchors.centerIn: parent
//            anchors.fill: parent
            anchors.right: parent.right
            anchors.bottom: parent.bottom
        }
    }
}
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

Anchors

Making more MyShapes and anchoring them

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    MyShape {
        id: shapeCentral
        anchors.centerIn: parent
        text: "Center"

        Rectangle {
            width: 25
            height: 25
            color: "pink"
//            anchors.centerIn: parent
//            anchors.fill: parent
            anchors.right: parent.right
            anchors.bottom: parent.bottom
        }
    }

    MyShape {
        id: shapeTop
        text: "Top"
        color: "green"
        anchors.bottom: shapeCentral.top
        anchors.left: shapeCentral.left
    }

    MyShape {
        id: shapeBottom
        text: "Bottom"
        color: "blue"
        anchors.top: shapeCentral.bottom
        anchors.left: shapeCentral.left
    }

    MyShape {
        id: shapeLeft
        text: "Left"
        color: "red"
        anchors.right: shapeCentral.left
        anchors.bottom: shapeCentral.bottom
    }

    MyShape {
        id: shapeRight
        text: "Right"
        color: "yellow"
        anchors.left: shapeCentral.right
        anchors.bottom: shapeCentral.bottom
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

Anchors

And we can drag them all around together


Anchors

# Margins

First remember that if there is no anchor there is no margin, and there is no anchor by default.

So, this:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        anchors.centerIn: parent
        width: 300
        height: 400
        color: "red"

        Rectangle {
            width: 100
            height: 100
            color: "yellow"
            anchors.margins: 50
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Results in this:


Margins

The margins will only be set for the anchors that are set

So in this case:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        anchors.centerIn: parent
        width: 300
        height: 400
        color: "red"

        Rectangle {
            width: 100
            height: 100
            color: "yellow"
            anchors.margins: 50
            anchors.left: parent.left
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

There will be only left margin


Margins

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        anchors.centerIn: parent
        width: 300
        height: 400
        color: "red"

        Rectangle {
            width: 100
            height: 100
            color: "yellow"
            anchors.margins: 50
            anchors.left: parent.left
            anchors.bottom: parent.bottom
        }
    }
}
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

Margins

Also, we can set just one margin

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        anchors.centerIn: parent
        width: 300
        height: 400
        color: "red"

        Rectangle {
            width: 100
            height: 100
            color: "yellow"
            anchors.margins: 50
            anchors.left: parent.left
            anchors.bottom: parent.bottom
            anchors.leftMargin: 15
        }
    }
}
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

Margins

# Simple image viewer

First let's add the images and create a prefix


Simple Image Viewer

Now we create a new component called HoverButton

TIP

the alias type points to a child inside the stack that is being built.

Property alias basically points to another component, so they have all the properties for that component.

You use the alias property if you want to expose the child object, without having to make a property that points to every property of that child.

import QtQuick 2.0

Item {
    id: root
    property color color: "red"
    property color hoverColor: "pink"
    property color clickColor: "orange"
    property color textColor: "White"
    property color textHover: "Black"
    property alias title: display
    property alias area: mousearea

    Rectangle {
        height: root.height
        width: root.width
        color: root.color
        clip: true

        Text {
            id: display
            text: ""
            anchors.centerIn: parent
            color: root.textColor
            font.bold: true
        }

        MouseArea {
            id: mousearea
            anchors.fill: parent
            hoverEnabled: true

            onEntered: {
                parent.color = root.hoverColor
                display.color = root.textHover
            }

            onExited: {
                parent.color = root.color
                display.color = root.textColor
            }

            onPressed: parent.color = root.clickColor
            onReleased: parent.color = root.hoverColor
        }
    }
}
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
38
39
40
41
42
43
44
45
46

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Image Viewer")
    id: root

    property color bgcolor: "#5A6263"
    property color buttoncolor: "green"
    property color hovercolor: "limegreen"
    property color clickcolor: "yellow"
    property var size: 100

    Rectangle {
        id: background
        color: root.buttoncolor
        width: 100
        height: parent.height

        Column {
            id: column
            anchors.fill: parent

            HoverButton {
                width: 100
                height: 50
                title.text: "Tornado"
                color: root.buttoncolor
                hoverColor: root.hovercolor
                clickColor: root.clickcolor

                area.onPressed: image.source = "qrc:/Images/IMAGES/Tornado.png"
            }

            HoverButton {
                width: 100
                height: 50
                title.text: "Apples"
                color: root.buttoncolor
                hoverColor: root.hovercolor
                clickColor: root.clickcolor

                area.onPressed: image.source = "qrc:/Images/IMAGES/k12.png"
            }

            HoverButton {
                width: 100
                height: 50
                title.text: "Histogram"
                color: root.buttoncolor
                hoverColor: root.hovercolor
                clickColor: root.clickcolor

                area.onPressed: image.source = "qrc:/Images/IMAGES/Histogram-Threshold.png"
            }
        }
    }

    Rectangle {
        id: rectangle
        color: root.bgcolor
        x: 100
        y: 0
        width: parent.width - x
        height: parent.height

        Image {
            id: image
            anchors.margins: 25
            anchors.fill: parent
            fillMode: Image.PreserveAspectFit
            source: "qrc:/Images/IMAGES/SMILE FACE.jpg"
        }
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

Simple Image Viewer

Adding a home button:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Image Viewer")
    id: root

    property color bgcolor: "#5A6263"
    property color buttoncolor: "green"
    property color hovercolor: "limegreen"
    property color clickcolor: "yellow"
    property var size: 100

    Rectangle {
        id: background
        color: root.buttoncolor
        width: 100
        height: parent.height

        HoverButton {
            width: 100
            height: 50
            title.text: "Home"
            color: root.buttoncolor
            hoverColor: root.hovercolor
            clickColor: root.clickcolor

            area.onPressed: image.source = "qrc:/IMAGES/qml.png"
            anchors.bottom: background.bottom
        }

        Column {
            id: column
            anchors.fill: parent

            HoverButton {
                width: 100
                height: 50
                title.text: "Tornado"
                color: root.buttoncolor
                hoverColor: root.hovercolor
                clickColor: root.clickcolor

                area.onPressed: image.source = "qrc:/Images/IMAGES/Tornado.png"
            }

            HoverButton {
                width: 100
                height: 50
                title.text: "Apples"
                color: root.buttoncolor
                hoverColor: root.hovercolor
                clickColor: root.clickcolor

                area.onPressed: image.source = "qrc:/Images/IMAGES/k12.png"
            }

            HoverButton {
                width: 100
                height: 50
                title.text: "Histogram"
                color: root.buttoncolor
                hoverColor: root.hovercolor
                clickColor: root.clickcolor

                area.onPressed: image.source = "qrc:/Images/IMAGES/Histogram-Threshold.png"
            }
        }
    }

    Rectangle {
        id: rectangle
        color: root.bgcolor
        x: 100
        y: 0
        width: parent.width - x
        height: parent.height

        Image {
            id: image
            anchors.margins: 25
            anchors.fill: parent
            fillMode: Image.PreserveAspectFit
            source: "qrc:/IMAGES/qml.png"
        }
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

Simple Image Viewer

# Section 6 - Properties and animations

# Property and PropertyAnimation

Animating a square to cross the screen:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Image Viewer")
    id: root

    Rectangle {
        id: myShape
        color: "red"
        width: 100
        height: 100
        x: 0
        y: (parent.height / 2) - (height / 2)

        PropertyAnimation {
            id: animationRight
            target: myShape
            property: "x"
            to: root.width - myShape.width
            duration: 500
        }

        PropertyAnimation {
            id: animationLeft
            target: myShape
            property: "x"
            to: 0
            duration: 500
        }

        MouseArea {
            anchors.fill: parent
            onClicked: {
                if(myShape.x === 0) {
                    animationRight.start()
                } else animationLeft.start()
            }
        }
    }
}
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
38
39
40
41
42
43
44

Animation


Animation

# Rotation and RotationAnimation

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Image Viewer")
    id: root

    Rectangle {
        id: myShape
        color: "red"
        width: 200
        height: 200
        anchors.centerIn: parent

        Text {
            id: title
            text: Math.round(parent.rotation)
            anchors.centerIn: parent
            font.bold: true
            font.pointSize: 65
        }

        RotationAnimation {
            id:animation
            target: myShape
            loops: Animation.Infinite
            from: myShape.rotation
            to: 360 //-360 is BAD
            direction: RotationAnimation.Clockwise
            duration: 20000
            running: true
        }
    }
}
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

Rotation and RotationAnimation

TIP

The use of id like in myShape.rotation could be a problem if there is any spelling mistake. Be Aware.

Including a mouse area so we can pause the animation with a click:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Image Viewer")
    id: root

    Rectangle {
        id: myShape
        color: "red"
        width: 200
        height: 200
        anchors.centerIn: parent

        Text {
            id: title
            text: Math.round(parent.rotation)
            anchors.centerIn: parent
            font.bold: true
            font.pointSize: 65
        }

        RotationAnimation {
            id:animation
            target: myShape
            loops: Animation.Infinite
            from: myShape.rotation
            to: 360 //-360 is BAD
            direction: RotationAnimation.Clockwise
            duration: 20000
            running: true
        }

        MouseArea {
            id: area
            anchors.fill: parent
            onClicked: {
                if (animation.paused) {
                    animation.resume()
                } else {
                    animation.pause()
                }
            }
        }
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49

TIP

animation.paused returns a boolean saying if the animation was paused, and animation.pause() apuses the animation

# Scale and SequentialAnimation

Now using a sequential animation we are going to scale up the rectangle and scale down in a sequence. We are also cliping the text to the rectangle.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Image Viewer")
    id: root

    Rectangle {
        id: myShape
        color: "red"
        width: 200
        height: 200
        anchors.centerIn: parent
        clip: true

        Text {
            id: title
            text: qsTr("SCALE")
            font.bold: true
            font.pointSize: 67
            rotation: -45
            anchors.centerIn: parent
        }

        SequentialAnimation {
            id: animation
            running: true
            loops: Animation.Infinite

            ScaleAnimator {
                id: aniShrink
                target: myShape
                from: 1
                to: 0.5
                duration: 2000
                running: true
            }

            ScaleAnimator {
                id: aniGrow
                target: myShape
                from: 0.5
                to: 1
                duration: 2000
                running: true
            }
        }
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

Scale

# Opacity and OpacityAnimator

For the opacity example we are going to change the windows backgroung to black so we can have the desired effect and set an opacity animation.

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Image Viewer")
    id: root
    color: "black"

    Image {
        id: ghost
        source: "qrc:/IMAGES/Ghost.jfif"
        anchors.fill: parent
        opacity: 0
    }

    SequentialAnimation {
        loops: Animation.Infinite
        running: true

        OpacityAnimator {
            target: ghost
            from: 0
            to: 1
            duration:  500
        }

        OpacityAnimator {
            target: ghost
            from: 1
            to: 0
            duration:  3000
        }
    }
}
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

Opacity


Opacity

# SmoothedAnimation

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Animation test")

    Rectangle {
        anchors.fill: parent
        color: "black"
        focus: true

        Rectangle {
            width: 60
            height: 60
            color: "yellow"
            radius: width

            x: rect1.x -5
            y: rect1.y -5

            Behavior on x { SmoothedAnimation {velocity: 100}}
            Behavior on y { SmoothedAnimation {velocity: 100}}

        }

        Rectangle {
            id:rect1
            width: 50
            height: 50
            color: "red"
            radius: width
            x: (parent.width/2) - (width/2)
            y: (parent.height/2) - (height/2)
        }

        Keys.onRightPressed: rect1.x = rect1.x + 100
        Keys.onLeftPressed: rect1.x = rect1.x - 100
        Keys.onUpPressed: rect1.y = rect1.y - 100
        Keys.onDownPressed: rect1.y = rect1.y + 100
    }
}
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
38
39
40
41
42
43
44

TIP

focus: true, basically means that if you hit the keyboard this item will be affected because it is in focus.


SmoothAnimation

# Anti-aliasing

Anti-aliasing is turned on by default, but you can turn it off.

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Animation test")

    Row {
        anchors.centerIn: parent
        spacing: 20

        Rectangle {
            width: 200
            height: 200
            border.color: "red"
            border.width: 20
            radius: width

            Text {
                anchors.centerIn: parent
                text: qsTr("Turned On")
            }
        }

        Rectangle {
            width: 200
            height: 200
            border.color: "red"
            border.width: 20
            radius: width
            antialiasing: false

            Text {
                anchors.centerIn: parent
                text: qsTr("Turned Off")
            }
        }
    }
}
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
38
39
40
41

Anti-aliasing


Anti-aliasing

# Keyboard input

In this example we are going to control the width and height of the red rectangle.

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Animation test")

    Rectangle {
        id: background
        anchors.fill: parent
        color: "black"
        focus: true

        Rectangle {
            id: rect1
            anchors.centerIn: parent
            width: 100
            height: 100
            color: "red"
        }

        Rectangle {
            id: outline
            anchors.centerIn: parent
            width: 100
            height: 100
            color: "transparent"
            border.color: "yellow"
            border.width: 5
        }

        Keys.onUpPressed: rect1.height += 10
        Keys.onDownPressed: rect1.height -= 10
        Keys.onLeftPressed: rect1.width += 10
        Keys.onRightPressed: rect1.width -= 10
    }
}
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
38
39

Keyboard Input

# Transform and Translate

You can anchor something and using transform translate move it on an offset based on its anchor.

kind of has this dizzying effect because this yellow one is not pixel perfect right to the center where

the blue and red are but it really outlines the power of this.

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Animation test")

    Item {
        id: root
        rotation: 0
        anchors.centerIn: parent

        Rectangle {
            id: yellowRect
            color: "yellow"
            width: 300
            height: 300
            radius: width
            opacity: 0.5
            border.width: 2
            border.color: "black"
            anchors.centerIn: parent
            transform: Translate {y: -100}
        }

        Rectangle {
            id: blueRect
            color: "blue"
            width: 300
            height: 300
            radius: width
            opacity: 0.5
            border.width: 2
            border.color: "black"
            anchors.centerIn: parent
            transform: Translate {y: 100; x: -100}
        }

        Rectangle {
            id: redRect
            color: "red"
            width: 300
            height: 300
            radius: width
            opacity: 0.5
            border.width: 2
            border.color: "black"
            anchors.centerIn: parent
            transform: Translate {y: 100; x: 100}
        }

        Rectangle {
            id: centerArea
            color: "white"
            width: 50
            height: 50
            radius: width
            opacity: 1
            border.width: 2
            border.color: "black"
            anchors.centerIn: parent

            Rectangle {
                id: centerCircle
                color: "gray"
                width: 5
                height: 5
                radius: width
                opacity: 1
                border.width: 2
                border.color: "black"
                anchors.centerIn: parent
            }
        }

        RotationAnimator {
            target: root
            from: 360
            to: 0
            duration: 5000
            direction: RotationAnimator.Counterclockwise
            loops: Animation.Infinite
            running: true
        }
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

Transform and Translate


Transform and Translate

# Drag and drop

import QtQuick 2.15
import QtQuick.Window 2.15


Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("QML rules")

    property string thekey: "special key here"
    property color goalOn: "green"
    property color goalOff: "red"
    property color ballOn: "yellow"
    property color ballOff: "orange"

    DropArea {
        id: dropZone
        anchors.top: parent.top
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        width: 300
        Drag.keys: [thekey]
        onDropped: {
            console.log("dropped!!!")
            goal.color = goalOn
        }

        Rectangle {
            id: goal
            anchors.fill: parent
            color: goalOff
            border.color: "black"
            border.width: 5
        }
    }

    Rectangle {
        id: ball
        width: 100
        height: 100
        radius: width
        x: 25
        y: (parent.height / 2) - (height / 2)
        color: ballOff
        border.color: "black"
        border.width: 5

        Drag.active: dragArea.drag.active
        Drag.keys: [thekey]

        Text {
            id: title
            anchors.centerIn: parent
            text: Math.round(parent.x) + " x " + Math.round(parent.y)
        }

        MouseArea {
            id: dragArea
            anchors.fill: parent
            drag.target: parent
            onPressed: {parent.color = ballOn; goal.color= goalOff}
            onReleased: {parent.color = ballOff; parent.Drag.drop()}
        }
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

Drag and Drop


Drag and Drop

# Qt Designer

# Connections

onClicked is a bit misleading, because you have to have a mouse area on the object to be able to click. If you click on rectangle and add a connection onClicked, nothing happens.

import QtQuick 2.15
import QtQuick.Window 2.15


Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("QML rules")

    Rectangle {
        id: rectangle
        x: 125
        y: 92
        width: 100
        height: 100
        color: "#d80101"

        MouseArea {
            id: mouseArea
            anchors.fill: parent

            Connections {
                target: mouseArea
                onClicked: console.log("clicked")
            }
        }

    }

    Rectangle {
        id: rectangle1
        x: 270
        y: 92
        width: 100
        height: 100
        color: "#00dd0f"
    }

    Rectangle {
        id: rectangle2
        x: 413
        y: 92
        width: 100
        height: 100
        color: "#0212e4"
    }
}

/*##^##
Designer {
    D{i:0;formeditorZoom:0.6600000262260437}D{i:2}
}
##^##*/
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

QtDesigner


QtDesigner

# Bindings

We can also set bindings between the objects:


QtDesigner


QtDesigner

Now to test the binding lets make the mouse click change the width and height.

import QtQuick 2.15
import QtQuick.Window 2.15


Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("QML rules")

    Rectangle {
        id: rectangle
        x: 125
        y: 92
        width: 100
        height: 100
        color: "#d80101"

        MouseArea {
            id: mouseArea
            anchors.fill: parent

            Connections {
                target: mouseArea
                onClicked: {
                    print("clicked")
                    rectangle.width= 50
                    rectangle.height= 150
                }
            }
        }

    }

    Rectangle {
        id: rectangle1
        x: 270
        y: 92
        width: rectangle.width
        height: rectangle.height
        color: "#00dd0f"
    }

    Rectangle {
        id: rectangle2
        x: 413
        y: 92
        width: 100
        height: 100
        color: "#0212e4"
    }
}

/*##^##
Designer {
    D{i:0;formeditorZoom:0.6600000262260437}D{i:2}
}
##^##*/
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

QtDesigner

Now Let's make two rectangles and a text to one and make it change when the other one is clicked.

import QtQuick 2.15
import QtQuick.Window 2.15


Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("QML rules")

    Rectangle {
        id: rectangle
        x: 102
        y: 151
        width: 200
        height: 200
        color: "#2f04ea"

        MouseArea {
            id: mouseArea
            anchors.fill: parent

            Connections {
                target: mouseArea
                onClicked: text1.text = "Hello"
            }
        }
    }

    Rectangle {
        id: rectangle1
        x: 347
        y: 151
        width: 200
        height: 200
        color: "#118013"

        Text {
            id: text1
            color: "#ffffff"
            text: qsTr("Text")
            anchors.verticalCenter: parent.verticalCenter
            font.pixelSize: 35
            font.bold: true
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }
}

/*##^##
Designer {
    D{i:0;formeditorZoom:0.6600000262260437}D{i:2}
}
##^##*/
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

QtDesigner


QtDesigner

TIP

Remember that we have to add a mouse area and add the connection to the mouse area and not the rectangle.

# Properties

We can also add properties to the objects, like a title for instance; and it will appear on the qml code.


QtDesigner


QtDesigner

Now we can bind the title property to the text.

import QtQuick 2.15
import QtQuick.Window 2.15


Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("QML rules")

    Rectangle {
        id: rectangle
        x: 102
        y: 151
        width: 200
        height: 200
        color: "#2f04ea"

        MouseArea {
            id: mouseArea
            anchors.fill: parent

            Connections {
                target: mouseArea
                onClicked: rectangle1.title = "Hello"
            }
        }
    }

    Rectangle {
        id: rectangle1
        x: 347
        y: 151
        width: 200
        height: 200
        color: "#118013"
        property string title: "Title"

        Text {
            id: text1
            color: "#ffffff"
            text: parent.title
            anchors.verticalCenter: parent.verticalCenter
            font.pixelSize: 35
            font.bold: true
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }
}

/*##^##
Designer {
    D{i:0;formeditorZoom:0.6600000262260437}D{i:2}
}
##^##*/
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

And it works the same way.


QtDesigner


QtDesigner

# States

First, let's create another qml file called MyBox and add some properties. Also let's bind the color to the defaultColor propertie.

import QtQuick 2.0

Item {
    id: element
    property color offColor: "red"
    property color onColor: "green"
    property color defaultColor: "gray"
    
    Rectangle {
        id: rectangle
        x: 220
        y: 140
        width: 200
        height: 200
        color: defaultColor
    }   
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

States

Now let's fill the rectangle to the item,make it round, and add a state.

import QtQuick 2.0

Item {
    id: element
    property color offColor: "red"
    property color onColor: "green"
    property color defaultColor: "gray"

    Rectangle {
        id: rectangle
        color: defaultColor
        anchors.fill: parent
        radius: width
    }

}

/*##^##
Designer {
    D{i:0;autoSize:true;height:480;width:640}D{i:1}
}
##^##*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

States

Now let's add some other states

import QtQuick 2.0

Item {
    id: element
    property color offColor: "red"
    property color onColor: "green"
    property color defaultColor: "gray"

    Rectangle {
        id: rectangle
        color: defaultColor
        anchors.fill: parent
        radius: width
    }
    states: [
        State {
            name: "on"

            PropertyChanges {
                target: rectangle
                color: onColor
            }
        },

        State {
            name: "off"

            PropertyChanges {
                target: rectangle
                color: offColor
            }
        }
    ]

}

/*##^##
Designer {
    D{i:0;autoSize:true;formeditorZoom:0.6600000262260437;height:480;width:640}D{i:1}
}
##^##*/
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
38
39
40
41

States

TIP

States cannot be added to a Window, has to be a custom object.

Now the item is on My QML Components, and we can drag and drop it. Although we have to give it a width and height, otherwise is will be only a point.


States

we can change the state by adding a connextion on the mouse area:

import QtQuick 2.15
import QtQuick.Window 2.15


Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("QML rules")

    MyBox {
        id: myBox
        x: 220
        y: 140
        width: 200
        height: 200

        MouseArea {
            id: mouseArea
            anchors.fill: parent

            Connections {
                target: mouseArea
                onClicked: {
                    if(myBox.state === "on") {
                        myBox.state = "off"
                    } else {
                        myBox.state = "on"
                    }
                }
            }
        }
    }
}

/*##^##
Designer {
    D{i:0;formeditorZoom:0.6600000262260437}D{i:2}
}
##^##*/
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
38
39
40
41

States

or by adding an onclicked to the mouse area directly on the code. And in this case we can use parent instead of MyBox.

import QtQuick 2.15
import QtQuick.Window 2.15


Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("QML rules")

    MyBox {
        id: myBox
        x: 220
        y: 140
        width: 200
        height: 200

        MouseArea {
            id: mouseArea
            anchors.fill: parent

            onClicked: {
                if(parent.state === "on") {
                    parent.state = "off"
                } else {
                    parent.state = "on"
                }
            }
        }
    }
}

/*##^##
Designer {
    D{i:0;formeditorZoom:0.6600000262260437}D{i:2}
}
##^##*/
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
38

States

# Qt Material Design

CMakeLists.txt

cmake_minimum_required(VERSION 3.14)

project(QTMaterial_Design LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(Qt5 COMPONENTS Core Quick QuickControls2 REQUIRED)

if(ANDROID)
    add_library(QTMaterial_Design SHARED
      main.cpp
      qml.qrc
    )
else()
    add_executable(QTMaterial_Design
      main.cpp
      qml.qrc
    )
endif()

target_compile_definitions(QTMaterial_Design
  PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(QTMaterial_Design
  PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Quick Qt5::QuickControls2)
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
38
39
40
41
42
43
44

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>
#include <QtQuickControls2>
#include <QFontDatabase>


int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQuickStyle::setStyle("Material");

    QFontDatabase::addApplicationFont("qrc:/ICONS/ICONS/materialdesignicons-webfont.ttf");

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
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

main.qml

import QtQuick 2.15
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import QtQuick.Controls.Material 2.3
import "qrc:/ICONS/ICONS/Icon.js" as MdiFont


ApplicationWindow {
    width: 640
    height: 480

    visible: true

    Material.theme: Material.Dark

    menuBar: ToolBar {
        RowLayout {
            ToolButton {
                text: "A"
            }

            Label {
                text: "Travel Stream"
            }

            ToolButton {
                text: "148"
            }

            Button {
                text: qsTr("Button")
            }

            SwitchDelegate {
                text: qsTr("Switch Delegate")
            }

            ProgressBar {
                value: 0.5
            }
            Dial {
                height: 25
            }


        }

    }

    Button {
        id: button
        x: 301
        y: 186
        text: qsTr("Button")
    }

    SwitchDelegate {
        id: switchDelegate
        x: 323
        y: 352
        text: qsTr("Switch Delegate")
    }

    ProgressBar {
        id: progressBar
        x: 182
        y: 120
        value: 0.5
    }

    Dial {
        id: dial
        x: 40
        y: 220
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

# Qt Quick Controls

We have built a custom control, but there is no need to reinvent the wheel here, we can actually use what is called Qt Quick Controls.

First, we have to import the Qt Quick Controls, and we can do that from the Design window.


Qt Quick Controls

WARNING

To be done later

# QML Project Templates

WARNING

To be done later

# QML and C++ integration

  • QML lives on top of C++
  • You can expose C++ classes to QML
  • C++ and QMl communicate via signals and slots
#include <QGuiApplication>
#include <QQmlApplicationEngine> // This loads all the QML

//Entry point of the application
int main(int argc, char *argv[])
{
    // Application Attributes (this is for cell phones with high DPI screens)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
    // The Application class
    QGuiApplication app(argc, argv);

    // QML engine
    QQmlApplicationEngine engine;
    
    // The URL of the QML file, notice this is a compiled resource. 
    // Bassically it loads the QML file as a String into the engine that runs it as QML
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    
    // Connecting a signal and slot - making sure the object and url match
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    
    // The engine loading the QML file
    engine.load(url);

    // The application entering an event loop that keeps the application open
    return app.exec();
}
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

# Call C++ Slot from QML


Qt Quick Controls

First create a function with a slot, in this case will be the Test function.

public slots:
    void bark();
1
2

then, include on main.cpp:

#include <QQmlContext>
#include <test.h>
1
2

Then create an instance of the function and add the instance to the root context:

Test test; 
engine.rootContext()->setContextProperty("testing", &test);
1
2

Now you can call the slot from the QML file:

onClicked: {
    //Call a slot
    testing.bark();
}
1
2
3
4

CMakeLists:

cmake_minimum_required(VERSION 3.14)

project(QT5 LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Quick REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Quick QuickControls2 REQUIRED)

if(ANDROID)
    add_library(QT5 SHARED
      main.cpp
      qml.qrc
      test.cpp
      test.h
    )
else()
    add_executable(QT5
      main.cpp
      qml.qrc
      test.cpp
      test.h
    )
endif()

target_compile_definitions(QT5
  PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(QT5
  PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Quick Qt5::QuickControls2)
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
38
39
40
41
42
43
44
45
46
47
48
49

Main.cpp






 
 











 
 
 
 












#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>

//imports
#include <QQmlContext>
#include <test.h>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQuickStyle::setStyle("Material");

    QQmlApplicationEngine engine;

    //Add to the root context
    Test test; // Basically we are creating a instance of the function,
    engine.rootContext()->setContextProperty("testing", &test);
    // Then we are taking that instance and puting on the context proterty

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
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

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.3

// Test 

Window {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Column {
        id: column
        width: 200
        height: 168
        anchors.verticalCenter: parent.verticalCenter
        anchors.horizontalCenter: parent.horizontalCenter
        spacing: 25

        Label {
            id: label
            text: qsTr("Call a C++ Slot")
            anchors.horizontalCenter: parent.horizontalCenter
        }

        Button {
            id: button
            text: qsTr("Click Me")
            anchors.horizontalCenter: parent.horizontalCenter
            onClicked: {
                //Call a slot
                testing.bark();

                //Can not call a non-slot - must be a property
                var num = testing.number()
                print(num)
            }
        }
    }
}
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
38
39
40
41
42

Test.h

#ifndef TEST_H
#define TEST_H

#include <QObject>
#include <QDebug>

class Test : public QObject
{
    Q_OBJECT
public:
    explicit Test(QObject *parent = nullptr);

    //Normal Function
    int number();

signals:

public slots:
    void bark();

};

#endif // TEST_H

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

Test.cpp

#include "test.h"

Test::Test(QObject *parent) : QObject(parent)
{

}

int Test::number()
{
    qInfo() << "Called Number()";
    return 42;
}

void Test::bark()
{
    qInfo() << "Bark Bark Bark";
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Output:

Bark Bark Bark
qrc:/main.qml:35: TypeError: Property 'number' of object Test(0x79fd90) is not a function
Bark Bark Bark
qrc:/main.qml:35: TypeError: Property 'number' of object Test(0x79fd90) is not a function
Bark Bark Bark
qrc:/main.qml:35: TypeError: Property 'number' of object Test(0x79fd90) is not a function
1
2
3
4
5
6

TIP

If something shows up as "Its not a function", first check the spelling and then check if it is a slot. QML can not call a non-slot - must be a property.


In this case the problem was that the function number() was not a slot.

# Call a QML slot from C++

Here, we're going to connect to a signal.

We have a start button a stop button and a status labels going to start a timer stop a timer and get the current timer status.

That being said we're gonna have a back in class and a signal is going to be emitted. and we're going to read or connect to that signal inside of QML and that's how this whole magic happens.


Qt Quick Controls

TIP

it is important we use QVariant, Because the data we pass back and forth from C++ to QML is going to be wrapped inside of a QVariant.


main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.3

// This is the root context

Window {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    property string timerlabel: "Counter"

    Connections {
        target: testing //our C++ object on the root context
        onNotice: {
            console.log("Slot called from C++")
            timerlabel = data // get this from the signal
            if(data === "Starting...") labelStatus.text = "Counter Started"
            if(data === "Stopped") labelStatus.text = "Counter Stopped"
        }
    }

    Column {
        id: column
        width: 228
        height: 159
        anchors.verticalCenter: parent.verticalCenter
        anchors.horizontalCenter: parent.horizontalCenter
        spacing: 25

        Label {
            id: labelTitle
            text: "Connecting to Signals"
            font.pointSize: 10
            anchors.horizontalCenter: parent.horizontalCenter
        }

        Label {
            id: labelStatus
            text: "Status"
            font.pointSize: 25
            font.bold: true
            anchors.horizontalCenter: parent.horizontalCenter
        }

        Row {
            id: row
            spacing: 25
            anchors.horizontalCenter: parent.horizontalCenter

            Button {
                id: buttonStart
                text: qsTr("Start")
                onClicked: {
                    //Call a slot
                    testing.start();
                    }
                }
            Button {
                id: buttonStop
                text: qsTr("Stop")
                onClicked: {
                    //Call a slot
                    testing.stop();
                    }
            }
        }

        Label {
            id: labelCounter
            text: timerlabel
            font.pointSize: 20
            font.bold: true
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>

//imports
#include <QQmlContext>
#include "test.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQuickStyle::setStyle("Material");

    QQmlApplicationEngine engine;

    //Add to the root context
    Test test; // Basically we are creating a instance of the function,
    engine.rootContext()->setContextProperty("testing", &test);
    // Then we are taking that instance and puting on the context proterty

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
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

test.h

#ifndef TEST_H
#define TEST_H

#include <QObject>
#include <QDebug>

#include <QTimer>
#include <QDateTime>
#include <QVariant>


class Test : public QObject
{
    Q_OBJECT
public:
    explicit Test(QObject *parent = nullptr);

signals:
    void notice(QVariant data);

private slots:
    void timeout();

public slots:
    void start();
    void stop();

private:
    QTimer m_timer;
    QString m_display;
};

#endif // TEST_H
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

test.cpp


So our test class is going to create we're going to connect our signal slot we're going to configure the timer emit our initial display.

When that timer fires we're going to actually update it with the current time. Actually get the current date time and commit our notice and then starting and stopping the timer

#include "test.h"

Test::Test(QObject *parent) : QObject(parent)
{
    connect(&m_timer, &QTimer::timeout, this, &Test::timeout);
    m_timer.setInterval(1000);
    m_display = "Starting";
    qInfo() << m_display;
    emit notice(QVariant(m_display));
}

void Test::timeout()
{
    m_display = QDateTime::currentDateTime().toString();
    qInfo() << m_display;
    emit notice(QVariant(m_display));
}

void Test::start()
{
    m_display = "Starting...";
    emit notice(QVariant(m_display));
    m_timer.start();
}

void Test::stop()
{
    m_timer.stop();
    m_display = "Stopped";
    qInfo() << m_display;
    emit notice(QVariant(m_display));
}
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

output:

"Starting"
"Wed Jan 6 10:35:30 2021"
"Wed Jan 6 10:35:31 2021"
"Wed Jan 6 10:35:32 2021"
"Wed Jan 6 10:35:33 2021"
"Wed Jan 6 10:35:34 2021"
"Wed Jan 6 10:35:35 2021"
"Stopped"
1
2
3
4
5
6
7
8

TIP

I don't like working with connections but this is a perfect example of why you would Somerset connections targets and you guessed it.

Our target's going to be testing and this is our C++ object on the root context so we can actually connect to this because it is a queue object and it has signals and slots.


Qt Quick Controls

# Register a C++ Class with QML

The previous videos had a bit of a problem and that problem was putting something well globally is not really necessarily the best design practice.


So what we're gonna do is we're going to actually register a C++ class.


This is what we need to do.

We're going to register a type in QML.

we need to give it some sort of URI. In this case it's going to be com.company.test, That's basically the namespace that we're shoving in the, tube but it's not really a namespace.

Then we're going to give it a major version a minor version and we need some sort of name that it can be used by

  qmlRegisterType<Test>("com.company.test", 1, 0, "Test");
1

Once we get to this point this class can be created in QML.

Now this is different than previous videos where we've actually created an instance of the class and then shoved it into the context.

What we're doing now is we're saying this type test is actual class can be created inside QML.


So in the QML we import com.company.test 1.0


Now this is different than previous videos because what we're doing here is we are actually creating this inside of QML, so it's not available up in the root context, it's actually available in the context of our window , so we could actually create different versions of this in different QML files.

TIP

First and foremost, signals and slots we're not working with public and private functions.

In the main.cpp you have to follow this QML registered type give it the class type, give it a URI, major and minor version, and a name in QML that you can actually create, that's the actual QML type name.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include <QQmlContext>
#include "test.h"


/*
Registering a C++ class as QML Type
allows you to use it in QML directly
*/


int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    //Register a type in QML
    qmlRegisterType<Test>("com.company.test", 1, 0, "Test");

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
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

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.3

// This is the root context

import com.company.test 1.0

Window {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Test {
        id: test
        onStatus: labelStatus.text = data;
    }

    Column {
        id: column
        width: 228
        height: 159
        anchors.verticalCenter: parent.verticalCenter
        anchors.horizontalCenter: parent.horizontalCenter
        spacing: 25

        Label {
            id: labelTitle
            text: "Connecting to Signals"
            font.pointSize: 10
            anchors.horizontalCenter: parent.horizontalCenter
        }

        Label {
            id: labelStatus
            text: "Status"
            font.pointSize: 25
            font.bold: true
            anchors.horizontalCenter: parent.horizontalCenter
        }

        Row {
            id: row
            spacing: 25
            anchors.horizontalCenter: parent.horizontalCenter

            Button {
                id: buttonStart
                text: qsTr("Start")
                onClicked: {
                    var num = Math.round(Math.random() * 100)
                    test.work(num)
                    }
                }
            Button {
                id: buttonStop
                text: qsTr("Stop")
                onClicked: {
                    //Call a slot
                    }
            }
        }
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

test.h

#ifndef TEST_H
#define TEST_H

#include <QObject>
#include <QDebug>
#include <QVariant>
#include <QRandomGenerator>

class Test : public QObject
{
    Q_OBJECT
public:
    explicit Test(QObject *parent = nullptr);

signals:

    void status(QVariant data);

public slots:
    void work(QVariant data);

};

#endif // TEST_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

test.cpp

#include "test.h"

Test::Test(QObject *parent) : QObject(parent)
{

}

void Test::work(QVariant data)
{
    bool ok;
    int numIn = data.toInt(&ok);
    int numOut = 0;

    if(!ok) {
        qWarning() << "Not a valid number";
    } else {
        int numRand = QRandomGenerator::global()->bounded(100);
        numOut = numIn * numRand;
        qInfo() << "In: " << numIn;
        qInfo() << "Random: " << numRand;
        qInfo() << "Out: " << numOut;
    }

    emit status(QVariant(numOut));
}
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

Registering a C++ Class with QML

# C++ integration example

CMakeLists

cmake_minimum_required(VERSION 3.14)

project(Cpp_Integration_Example LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Quick REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Quick REQUIRED)


if(ANDROID)
    add_library(Cpp_Integration_Example SHARED
      main.cpp
      qml.qrc
    )
else()
    add_executable(Cpp_Integration_Example
      main.cpp
      qml.qrc
    )
endif()

target_sources(Cpp_Integration_Example
    PRIVATE
        machine.cpp
        machine.h
    )


target_compile_definitions(Cpp_Integration_Example
  PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(Cpp_Integration_Example
  PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Quick)
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "machine.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    qmlRegisterType<Machine>("com.skinnyrobot.machine", 1, 0, "Machine");

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

machine.h

#ifndef MACHINE_H
#define MACHINE_H

#include <QObject>
#include <QTimer>
#include <QVariant>
#include <QDebug>

class Machine : public QObject
{
    Q_OBJECT

    Q_PROPERTY(int workload READ value WRITE setValue NOTIFY progress)

public:
    explicit Machine(QObject *parent = nullptr);

    int value();
    void setValue(int data);

signals:
    void progress();
    void started();
    void stopped();
    void paused();
    void resumed();

public slots:
    void start();
    void stop();
    void pause();
    void resume();

private slots:
    void timeout();

private:
    QTimer m_timer;
    int m_value;
};

#endif // MACHINE_H
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
38
39
40
41
42

machine.cpp

#include "machine.h"

Machine::Machine(QObject *parent) : QObject(parent)
{
    m_timer.setInterval(1000);
    connect(&m_timer, &QTimer::timeout, this, &Machine::timeout);
}

int Machine::value()
{
    qInfo() << "Returning value";
    return m_value;
}

void Machine::setValue(int data)
{
    qInfo() << "Setting value";
    m_value = data;
    emit progress();
}

void Machine::start()
{
    setValue(0);
    m_timer.start();
    emit started();
}

void Machine::stop()
{
    m_timer.stop();
    emit stopped();
}

void Machine::pause()
{
    m_timer.stop();
    emit paused();
}

void Machine::resume()
{
    m_timer.start();
    emit resumed();
}

void Machine::timeout()
{
    m_value++;
    emit progress();
    if(m_value > 99) stop();
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3

import com.skinnyrobot.machine 1.0

Window {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("C++ Example")

    Component.onCompleted: {

        progressBar.value = machine.workload

        btnStart.enable = true
        btnStop.enable = false
        btnPause.enable = false
        btnResume.enable = false
    }

    Machine {
        id: machine
        onStarted: {
            btnStart.enabled = false
            btnStop.enable = true
            btnPause.enabled = true
            btnResume.enabled = false
            lblStatus.text = "Started"
        }

        onStopped: {
            btnStart.enabled = true
            btnStop.enable = false
            btnPause.enabled = false
            btnResume.enabled = false
            lblStatus.text = "Stopped"
            progressBar.value = 0.0
        }

        onPaused: {
            btnStart.enabled = false
            btnStop.enable = true
            btnPause.enabled = false
            btnResume.enabled = true
            lblStatus.text = "Paused"
        }

        onResumed: {
            btnStart.enabled = false
            btnStop.enable = true
            btnPause.enabled = true
            btnResume.enabled = false
            lblStatus.text = "Resumed"
        }

        onProgress: {
            lblStatus.text = "Progress: " + machine.workload + "%"
            progressBar.value = (machine.workload * 0.01)
        }
    }

    Column {
        id:column
        width: 434
        height: 62
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter
        spacing: 10

        Label {
            id: lblStatus
            text: "Status"
        }

        ProgressBar {
            id: progressBar
            width: parent.width
            anchors.horizontalCenter: parent.horizontalCenter
            value: 0.5
        }

        Row {
            id: row
            width: 200
            height: 400
            spacing: 10

            Button {
                id: btnStart
                text: qsTr("Start")
                onClicked: machine.start()
            }

            Button {
                id: btnPause
                text: qsTr("Pause")
                onClicked: machine.pause()
            }

            Button {
                id: btnResume
                text: qsTr("Resume")
                onClicked: machine.resume()
            }

            Button {
                id: btnStop
                text: qsTr("Stop")
                onClicked: machine.stop()
            }
        }
    }
}

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

C++ Integration Example

# Examples

# Square footage calculator

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("Square Footage Calculator")

    function calc() {
        var num = sbWidth.value * sbHeight.value
        label.text = num + " sqft"
    }


    Column {
        id: column
        x: 0
        y: 0
        width: 200
        height: 164
        anchors.verticalCenter: parent.verticalCenter
        anchors.horizontalCenter: parent.horizontalCenter
        spacing: 10

        Label {
            id: label
            text: qsTr("0 sqft")
            anchors.horizontalCenter: parent.horizontalCenter
            font.pointSize: 25
        }

        Grid {
            id: grid
            width: 400
            height: 400
            rows: 2
            columns: 2
            spacing: 10

            Label {
                id: label1
                text: qsTr("Width")
            }

            SpinBox {
                id: sbWidth
            }

            Label {
                id: label2
                text: qsTr("Height")
            }

            SpinBox {
                id: sbHeight
            }
        }
    }

    Connections {
        target: sbWidth
        onValueModified: calc()
    }

    Connections {
        target: sbHeight
        onValueModified: calc()
    }
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

Examples - Square Footage Calculator