Эта глава раскроет, как соединяют два мира так, чтобы они усиливали друг друга. Здесь обнаружите: как скорость разработки и качество архитектуры растут, когда QML отвечает за визуальное, а C++ — за «мозг» приложения, и всё это связано правильно, без костылей.
Будут показаны 3 рабочих маршрута интеграции — внедрение QML через QQuickWidget, управление свойствами и вызов функций через setProperty()/QMetaObject::invokeMethod(), а также мост «сигналы QML → слоты C++» через QQmlApplicationEngine. Плюс — публикация C++-объектов через QQmlContext, регистрация типов qmlRegisterType<T>(), Q_PROPERTY и Q_INVOKABLE, и даже расширение QML собственным визуальным элементом на QQuickPaintedItem.
Если до сих пор казалось, что «QML и C++ нормально не дружат», — эту главу лучше не пропускать.
В этой главе вы найдёте готовые к использованию примеры кода.
Самопроверка по главе
Почему класс QQuickWidget может использоваться как обычный виджет в Qt/C++ приложениях?Ответ
Правильный ответ: QQuickWidget наследуется от QWidget, поэтому его можно размещать в любом месте Qt/C++ приложения, используя layouts или прямое позиционирование.
Зачем элементу QML присваивается свойство objectName перед взаимодействием с ним из C++?Ответ
Правильный ответ: ObjectName позволяет найти QML-элемент через метод findChild(), так как базовый класс QQuickItem наследуется от QObject и поддерживает это свойство для идентификации объектов.
Почему при вызове QML-функций из C++ используется тип QVariant для всех аргументов?Ответ
Правильный ответ: QVariant обеспечивает универсальную систему типов между C++ и JavaScript-средой QML, позволяя безопасно передавать различные типы данных через метаобъектную систему Qt.
Почему при соединении QML-сигналов со слотами C++ используется старый синтаксис SIGNAL()/SLOT(), а не новый?Ответ
Правильный ответ: QML-объекты создаются динамически во время выполнения, их сигналы известны только в рантайме, а новый синтаксис требует строгой проверки типов на этапе компиляции.
Что произойдет, если установить свойства контекста через setContextProperty() после загрузки QML-файла?Ответ
Правильный ответ: При первом обращении к этим свойствам в QML возникнет ошибка “ReferenceError: property is not defined”, так как QML пытается получить доступ к еще не существующим свойствам контекста.
В чем разница между публикацией объекта через setContextProperty() и регистрацией класса через qmlRegisterType()?Ответ
Правильный ответ: SetContextProperty публикует конкретный экземпляр объекта для использования в QML, а qmlRegisterType регистрирует сам класс, позволяя создавать его экземпляры непосредственно в QML-коде.
Почему свойство result в классе Calculation объявлено только с READ, а не с WRITE?Ответ
Правильный ответ: Это выходное вычисляемое значение, которое не должно изменяться извне; его значение автоматически обновляется при изменении input через метод setInputValue().
Зачем в Q_PROPERTY указывать параметр NOTIFY и когда его обязательно использовать?Ответ
Правильный ответ: NOTIFY связывает свойство с сигналом, уведомляющим об изменении значения, что необходимо для автоматического обновления QML-привязок (bindings) при изменении свойства из C++.
Какую проблему решает использование QQuickPaintedItem вместо прямого наследования от QQuickItem?Ответ
Правильный ответ: QQuickPaintedItem предоставляет готовый метод paint() с объектом QPainter, упрощая реализацию визуальных элементов без необходимости работы с низкоуровневым OpenGL-рендерингом.
Как в QML получить доступ к изображению, создаваемому через QQuickImageProvider?Ответ
Правильный ответ: Через специальную схему URL вида “image://имя_провайдера/параметры”, где имя провайдера задается при регистрации через addImageProvider(), а параметры передаются в метод requestImage().
Почему декларация Q_INVOKABLE необходима для вызова C++-методов из QML?Ответ
Правильный ответ: Q_INVOKABLE включает информацию о методе в метаобъектную систему Qt, делая его доступным через QMetaObject::invokeMethod() и видимым для QML-движка во время выполнения.
Что произойдет, если создать несколько объектов QQmlApplicationEngine в одном приложении?Ответ
Правильный ответ: Каждый engine создаст отдельную среду выполнения QML с собственным контекстом и состоянием, что может привести к дублированию ресурсов и усложнению управления приложением.
Когда следует использовать QQuickWidget, а когда QQmlApplicationEngine?Ответ
Правильный ответ: QQuickWidget используется для встраивания QML внутри виджет-приложений Qt; QQmlApplicationEngine — для полностью QML-приложений с собственным главным окном и циклом событий.
Почему в методе paint() класса Ellipse устанавливается Qt::NoPen?Ответ
Правильный ответ: Это отключает отрисовку контурной линии эллипса, оставляя только заливку цветом, что соответствует требованиям дизайна элемента без границы.
Какое преимущество дает разделение логики на C++ и интерфейса на QML в архитектуре приложения?Ответ
Правильный ответ: Используются сильные стороны обоих инструментов: производительность и типобезопасность C++ для бизнес-логики плюс декларативность и анимационные возможности QML для UI, упрощая разработку и поддержку.
Практические задания
Простой уровень
QML-калькулятор с C++ логикой
Создайте простое Qt-приложение, где интерфейс с двумя полями ввода и кнопкой реализован на QML, а операция сложения двух чисел выполняется в C++ классе. При нажатии кнопки результат должен отображаться в QML-элементе Text. Используйте setContextProperty для публикации C++ объекта.
Подсказки: Создайте класс Calculator с Q_INVOKABLE методом add(int a, int b). Используйте QQuickWidget или QQmlApplicationEngine для загрузки QML. В QML используйте TextField для ввода и Button для вызова метода. Не забудьте подключить модули quickwidgets или quick/qml в .pro файле.
Средний уровень
Двусторонняя синхронизация списка задач
Реализуйте приложение со списком задач (To-Do List), где модель данных QStringListModel создается в C++, а интерфейс с ListView и возможностью добавления/удаления элементов — на QML. Обеспечьте двустороннюю синхронизацию: изменения в QML должны обновлять C++ модель и наоборот. Добавьте кнопку в C++, которая программно добавляет новую задачу в список.
Подсказки: Создайте класс TaskManager с Q_PROPERTY для модели и Q_INVOKABLE методами addTask/removeTask. Используйте сигналы NOTIFY для автоматического обновления ListView. В QML используйте delegate с кнопкой удаления. Для добавления из C++ можете использовать QPushButton в основном виджете.
Сложный уровень
Визуализатор графиков с динамическими данными
Создайте приложение для визуализации математических функций: C++ класс должен вычислять точки графика (например, sin, cos, tan) и передавать их в QML через QQuickImageProvider или QQuickPaintedItem. QML интерфейс должен содержать элементы управления для выбора функции, диапазона и масштаба. Реализуйте плавную анимацию при изменении параметров и возможность экспорта графика в изображение.
Подсказки: Наследуйте QQuickPaintedItem для отрисовки графика через QPainter. Создайте Q_PROPERTY для типа функции, диапазона X/Y и масштаба с NOTIFY сигналами. В методе paint() вычисляйте точки и рисуйте ломаную линию. Используйте QPropertyAnimation в QML для плавных переходов. Для экспорта используйте метод grabToImage() элемента QML или QPixmap::save() в C++.
💬 Присоединяйтесь к обсуждению!
Разобрались, как соединить мощь C++ с элегантностью QML? Возникли вопросы о порядке инициализации или работе с Q_PROPERTY?
Поделитесь своим опытом интеграции, расскажите о встретившихся проблемах или помогите другим читателям освоить взаимодействие между C++ и QML!
Ваш практический опыт может стать бесценной подсказкой для коллег-разработчиков