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

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

Разработчику
О чём эта статья

Qt Quick — интерфейс на QML (как разметка), логика на C++.

Соберём счётчик: кнопка "+1", подпись обновляется сама при изменении поля в C++.

Виджетный путь — Qt — первая программа.

CMake — CMake — первая программа.


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

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

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

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

В Qt — первая программа кнопки создавали в 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

Код ITЗагрузка примера кода…

Разбор:

  • CMake-конфиг настраивает сборку Qt Quick-приложения и подключает QML-модуль.
  • CMAKE_AUTOMOC ON обязателен для классов с Q_OBJECT и метаинформацией свойств.
  • find_package(Qt6 ... Quick Qml) подключает runtime для QML-интерфейса и нужные библиотеки.
  • qt_add_qml_module регистрирует Main.qml в модуле HelloApp, чтобы загружать его по URI.
  • target_link_libraries(... Qt6::Quick) связывает исполняемый файл с графическим стеком Qt 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

Код ITЗагрузка примера кода…

Разбор:

  • Класс Counter наследует QObject, чтобы участвовать в метаобъектной системе Qt и быть видимым из QML.
  • Q_PROPERTY объявляет свойство value с чтением, записью и сигналом изменения, на который подписывается интерфейс.
  • setValue проверяет, изменилось ли значение, и только потом вызывает emit valueChanged(), чтобы избежать лишних перерисовок.
  • Q_INVOKABLE increment() открывает метод для вызова из QML (counter.increment()).
  • Поле value_{0} хранит состояние в C++-модели, а QML-слой только отображает и триггерит действия.

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

Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)

Разбор:

  • Q_PROPERTY связывает поле модели с QML и делает его частью рефлексии Qt.
  • READ value и WRITE setValue определяют функции доступа к свойству с контролем инвариантов.
  • NOTIFY valueChanged указывает сигнал, по которому QML понимает, что нужно обновить привязанные выражения.
  • Без корректного NOTIFY интерфейс не будет автоматически отражать изменения данных.
  • Это основной механизм реактивности Qt Quick на стороне C++-кода.
ФрагментСмысл
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 — движок и контекст

Код ITЗагрузка примера кода…

Разбор:

  • main.cpp связывает слой логики C++ и QML-слой интерфейса в единую программу.
  • QGuiApplication инициализирует графическую среду без набора виджетов Qt Widgets.
  • engine.rootContext()->setContextProperty("counter", &counter) публикует объект counter в пространство имён QML.
  • engine.loadFromModule("HelloApp", "Main") загружает корневой QML-компонент из модуля, объявленного в CMake.
  • Проверка rootObjects().isEmpty() быстро ловит ошибки загрузки QML до старта event loop.
СтрокаСмысл
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 — разбор

Код ITЗагрузка примера кода…

Разбор:

  • QML-файл декларативно описывает окно, вертикальный контейнер и два элемента управления.
  • Label.text использует привязку к counter.value, поэтому текст пересчитывается автоматически при изменениях модели.
  • Button.onClicked вызывает метод C++-объекта, опубликованного через контекст движка.
  • anchors.centerIn и spacing отвечают за компоновку элементов без ручного расчёта координат.
  • Такой шаблон чётко разделяет роли: QML отвечает за отображение, C++ — за состояние и бизнес-логику.

Привязка в 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
СтартQt — первая программаэта статья

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


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

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

Почему это масштабируется в реальных проектах

В небольшом примере есть та же архитектурная идея, что и в большом продукте:

  • состояние живёт в модели (Counter);
  • QML только отображает и отправляет команды;
  • синхронизация между UI и данными идёт через сигналы изменений.

На этой основе строят формы, панели телеметрии, мультимедийные интерфейсы и HMI.


Связанные статьи


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

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

Тесты Counter без GUI — Google Test и Catch2 в C++. Практика — задание 4.2 в Практические задания по C++.


Дальше


В подборках

Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:

Первые шаги (маршрут подборки) — Qt — первая программа, Первая программа на Laravel, CMake — первая программа, Первая программа на Symfony, Razor Pages — первая программа, Ktor — первая программа.