Перейти к основному содержимому

Qt Quick — первая программа на QML

Разработчику

:::tip О чём эта статья Qt Quick — интерфейс на QML (как разметка), логика на C++. Соберём счётчик: кнопка «+1», подпись обновляется сама при изменении поля в C++. Виджетный путь — 2731. CMake — 1006. :::

Где применяют Qt Quick

Qt Quick — UI для анимаций, touch-экранов, встраиваемых панелей и единого интерфейса на desktop и устройстве. Классические формы Windows чаще на Qt Widgets (2731).

ТерминПростыми словами
QMLязык описания интерфейса (объекты, привязки, анимации)
Qt Quickмодуль рантайма, который рисует QML-сцену
Q_PROPERTYполе C++-объекта, видимое в QML
Привязкавыражение в QML пересчитывается при изменении данных
mocгенерирует метаданные для Q_OBJECT и свойств

Два слоя — QML и C++

В 2731 кнопки создавали в C++. Здесь:

  • QML — «как выглядит» (Button, Label, отступы);
  • C++ — «что хранится и как считается» (Counter, increment()).

Число живёт только в C++. QML читает counter.value и вызывает counter.increment() — без дублирования состояния.


Что мы строим

Окно: подпись «Значение: N», кнопка «+1». При клике N растёт, подпись обновляется без setText из C++.


Установка

В инсталляторе Qt 6 отметьте Qt Quick / Qt Declarative. Без него find_package(Qt6 COMPONENTS Quick) завершится ошибкой.


Структура проекта

qt-quick-hello/
├── CMakeLists.txt
├── main.cpp
├── counter.hpp
├── counter.cpp
└── qml/
└── Main.qml
ЧастьНазначение
Counterхранит value, сигнал valueChanged, метод increment()
Main.qmlразметка, привязки к counter
main.cppQGuiApplication, движок QML, setContextProperty
qt_add_qml_moduleупаковка QML в модуль HelloApp

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)
project(qt_quick_hello LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)

find_package(Qt6 REQUIRED COMPONENTS Quick Qml)

qt_add_executable(quick_hello
main.cpp
counter.cpp
counter.hpp
)

qt_add_qml_module(quick_hello
URI HelloApp
VERSION 1.0
QML_FILES qml/Main.qml
)

target_link_libraries(quick_hello PRIVATE Qt6::Quick)

qt_add_qml_module — Qt 6: CMake знает URI HelloApp для engine.loadFromModule("HelloApp", "Main").

cmake -S . -B build -DCMAKE_PREFIX_PATH="/path/to/Qt/6.x/gcc_64"
cmake --build build

Модель на C++ — Q_PROPERTY

#pragma once
#include <QObject>

class Counter : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit Counter(QObject* parent = nullptr) : QObject(parent) {}

int value() const { return value_; }
void setValue(int v) {
if (value_ == v) return;
value_ = v;
emit valueChanged();
}

Q_INVOKABLE void increment() { setValue(value_ + 1); }

signals:
void valueChanged();

private:
int value_{0};
};

Разбор строки Q_PROPERTY

Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
ФрагментСмысл
int valueв QML свойство называется value, тип int
READ valueчитать через метод value()
WRITE setValueписать через setValue(int) (для двусторонних привязок)
NOTIFY valueChangedпри изменении обязательно emit valueChanged() — QML обновит экран

Q_INVOKABLE void increment() — метод, вызываемый из QML как counter.increment().

if (value_ == v) return — лишние сигналы не уходят в UI, меньше перерисовок.

counter.cpp может быть пустым, если вся логика в заголовке — для учебного примера нормально.


main.cpp — движок и контекст

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "counter.hpp"

int main(int argc, char* argv[]) {
QGuiApplication app(argc, argv);

Counter counter;

QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("counter", &counter);
engine.loadFromModule("HelloApp", "Main");

if (engine.rootObjects().isEmpty())
return -1;

return app.exec();
}
СтрокаСмысл
QGuiApplicationGUI без набора Widgets — достаточно для Quick
Counter counterобъект на стеке, живёт всё время main
setContextProperty("counter", &counter)в любом QML файле этого движка имя counter → наш объект
loadFromModule("HelloApp", "Main")загрузить корневой Main.qml из модуля CMake
rootObjects().isEmpty()проверка: QML загрузился (иначе смотреть вывод ошибок в консоль)

В больших приложениях тип регистрируют через qmlRegisterType<Counter>() и создают Counter { } в QML. Для первого шага хватает контекста.


Main.qml — разбор

import QtQuick
import QtQuick.Controls

ApplicationWindow {
width: 360
height: 200
visible: true
title: "Qt Quick Hello"

Column {
anchors.centerIn: parent
spacing: 12

Label {
text: "Значение: " + counter.value
}

Button {
text: "+1"
onClicked: counter.increment()
}
}
}

Привязка в text

text: "Значение: " + counter.value

Это привязка, а не разовое присваивание:

  1. QML подписывается на valueChanged (через метаданные NOTIFY).
  2. При increment()setValueemit valueChanged() строка пересчитывается.
  3. Label перерисовывается без вызова C++ API для текста.

Обработчик onClicked

onClicked: counter.increment()

onClicked — обработчик сигнала кнопки. Внутри — вызов C++-метода.


Как связаны C++ и QML

МеханизмКогда использовать
Q_PROPERTY + NOTIFYполя на экране
Q_INVOKABLEдействия по кнопке
setContextPropertyодин глобальный объект в прототипе
qmlRegisterTypeнесколько экземпляров в QML

Qt Widgets или Qt Quick?

Qt WidgetsQt Quick
Описание UIC++ (+ .ui)QML
Типичные задачиофис, IDE, нативные диалогианимации, touch, HMI
Старт2731эта статья

Гибрид: QQuickWidget внутри окна на виджетах.


Частые ошибки

СимптомВероятная причина
Белое окноQML не загрузился — синтаксис, URI, qt_add_qml_module
counter is undefinedнет setContextProperty или опечатка в имени
Счётчик не обновляетсянет NOTIFY или забыли emit valueChanged()
Сборка без QMLне подключён qt_add_qml_module

Что попробовать

  1. Кнопка «Сброс» и Q_INVOKABLE void reset() { setValue(0); }.
  2. qmlRegisterType<Counter> и Counter { id: c } вместо контекста.
  3. NumberAnimation на свойство в QML (чисто декларативно).

Тесты Counter без GUI — 1007. Практика — задание 4.2 в 1008.


Дальше

  • windeployqt для QML-зависимостей — 27.md;
  • Model/View для списков — там же;
  • виджетный путь — 2731.

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).