A simple understanding of qt, QML, cmake
The Example qt_qml_cmake_pipe github link, Create a simple working for qt qml with cmake to show the working of QProperty.
* This is a simple example to set up communication between qt6 and qml.
* The example has simple Qml Rectangle, Text, TextField and Button.
* The example also show how the QProperty READ, WRITE and NOTIFY works.
C++ is a versatile and powerful programming language widely used for system software, game development, real-time simulations, and large-scale applications. Its object-oriented features make it ideal for writing efficient and high-performance applications.
CMake is a cross-platform build system generator that helps manage and control the build process of software projects written in C++, and other languages. It allows developers to define build processes in a platform-independent manner, generating native build configurations for compilers like Make, Ninja, or MSVC.
QML (Qt Modeling Language) is a declarative language used primarily for designing user interfaces in Qt applications. It enables the creation of dynamic, fluid, and highly customizable UIs with ease. QML, together with C++, is commonly used in modern GUI-based applications to combine robust backend logic with sleek, responsive front-end designs.
In Qt, the Q_PROPERTY macro is used to declare properties of a class that can be accessed by Qt’s meta-object system, making them available to QML or other parts of the Qt framework for binding, signaling, and other features. This macro allows properties to be readable, writable, and notifyable for changes.
Components:
READ: Specifies the function to read the property’s value (getter).
WRITE: Specifies the function to set or modify the property’s value (setter).
NOTIFY: Specifies a signal that is emitted when the property value changes.
// pipe.h
class Pipe : public QObject
{
Q_OBJECT
QML_ELEMENT
// the NOTIFY property calls the READ function when triggered and the
// WRITE function send the data from qml to cpp code.
Q_PROPERTY(int data_1 READ getdata_1 WRITE setData_1 NOTIFY data_1Changed FINAL)
Q_PROPERTY(int data_2 READ getdata_2 WRITE setData_2 NOTIFY data_2Changed FINAL)
public:
explicit Pipe(QObject *parent = nullptr);
int getdata_1();
void setData_1(int value);
int getdata_2();
void setData_2(int value);
signals:
void data_1Changed();
void data_2Changed();
private:
int data_1, data_2;
};
Explanation:
READ: The getter getdata_1() is called to retrieve the value of data_1.
WRITE: The setter setData_1() is used to change the value of data_1. It ensures that changes are tracked, and the signal is emitted only when the value actually changes.
NOTIFY: The signal data_1Changed() is emitted when the property is updated, allowing other parts of the application (such as UI bindings in QML) to react to the change.
This mechanism is essential for data binding and interaction between the backend (C++) and the front-end (QML) in Qt applications.
// pipe.cpp
Pipe::Pipe(QObject *parent)
: QObject{parent}
{
data_1 = 0;
data_2 = 0;
}
int Pipe::getdata_1()
{
return data_1;
}
void Pipe::setData_1(int value)
{
std::cout << "Data: " << value << std::endl;
data_1 = value;
data_1++;
// emit data_1Changed(); // Block the NOTIFY property calls the READ function when triggered
}
int Pipe::getdata_2()
{
return data_2;
}
void Pipe::setData_2(int value)
{
std::cout << "Data: " << value << std::endl;
data_2 = value;
data_2++;
emit data_2Changed(); // the NOTIFY property calls the READ function when triggered
}
QML part below:
//Main.qml
import QtQuick
import QtQuick.Controls
import qml_pipe
Window {
width: 640
height: 480
visible: true
title: qsTr("QML Pipe Example")
// Fixed size
maximumHeight: height
maximumWidth: width
minimumHeight: height
minimumWidth: width
Pipe{
id : process
}
Rectangle{
id: left_side
anchors.left: parent.left
width: parent.width / 2
height: parent.height
color: "orange"
TextField{
id: left_field
x: 0
y: 0
width: parent.width
height: 40
}
Button{
id: left_click
x : 0
y : 50
text: "Button_1"
width: parent.width
height: 40
onClicked: process.data_1 = left_field.text
}
Text {
id: left_text
x : 0
y : 100
width: parent.width
height: 40
text: process.data_1
font.family: "Helvetica"
font.pointSize: 12
color: "black"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
Rectangle{
id: right_side
anchors.right: parent.right
width: parent.width / 2
height: parent.height
color: "green"
TextField{
id: right_field
x: 0
y: 0
width: parent.width
height: 40
}
Button{
id: right_click
x : 0
y : 50
text: "Button_2"
width: parent.width
height: 40
onClicked: process.data_2 = right_field.text
}
Text {
id: right_text
x : 0
y : 100
width: parent.width
height: 40
text: process.data_2
font.family: "Helvetica"
font.pointSize: 12
color: "black"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
Explanation:
From QML, when the Button (right_click) is clicked the value in the TextField(right_field) is read and send to the cpp data_2. Here, there is a simple incremental part and emit is triggered to send the data back to Text(right_text). Also since the emit is commented in data_1, there is no change in lift side.
Leave a Reply