Как QML интегрируется с C++ (выставление C++-классов в QML)?
C++-класс регистрируется в QML через qmlRegisterType() или макрос QML_ELEMENT. После этого Q_PROPERTY, Q_INVOKABLE-методы и сигналы класса становятся доступны в QML напрямую.
Интеграция QML с C++
Qt предоставляет несколько механизмов для того, чтобы C++-классы стали «первоклассными» объектами в QML. Выбор метода зависит от того, нужно ли создавать экземпляры типа прямо в QML или только обращаться к одному заранее созданному объекту.
Способ 1: qmlRegisterType (Qt 5 / совместимо с Qt 6)
Регистрирует класс как инстанцируемый тип QML. После регистрации можно писать MyClass { } прямо в .qml-файле.
// counter.h
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
Q_PROPERTY(int value READ value NOTIFY valueChanged)
public:
explicit Counter(QObject *parent = nullptr) : QObject(parent), m_value(0) {}
int value() const { return m_value; }
public slots:
void increment() { ++m_value; emit valueChanged(); }
signals:
void valueChanged();
private:
int m_value;
};// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "counter.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// uri="com.example", major=1, minor=0, qml-имя="Counter"
qmlRegisterType<Counter>("com.example", 1, 0, "Counter");
QQmlApplicationEngine engine;
engine.load(QUrl("qrc:/main.qml"));
return app.exec();
}// main.qml
import QtQuick 2.15
import com.example 1.0
Item {
Counter {
id: counter
}
Text { text: counter.value }
MouseArea {
anchors.fill: parent
onClicked: counter.increment()
}
}Способ 2: QML_ELEMENT / QML_NAMED_ELEMENT (Qt 6, предпочтительный)
Макрос QML_ELEMENT в теле класса и настройка qt_add_qml_module в CMake автоматически регистрируют тип без ручного вызова qmlRegisterType:
// counter.h
#include <QObject>
#include <QtQml/qqmlregistration.h>
class Counter : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int value READ value NOTIFY valueChanged)
// ... (как выше)
};# CMakeLists.txt
qt_add_qml_module(app
URI com.example
VERSION 1.0
SOURCES counter.h counter.cpp
QML_FILES main.qml
)Способ 3: Singleton через qmlRegisterSingletonType
Когда нужен ровно один экземпляр (например, сервис настроек):
qmlRegisterSingletonType<AppSettings>(
"com.example", 1, 0, "AppSettings",
[](QQmlEngine *, QJSEngine *) -> QObject * {
return new AppSettings();
});import com.example 1.0
Text { text: AppSettings.theme } // нет id, обращение по имени типаСпособ 4: setContextProperty
Передать уже существующий объект в корневой контекст (см. вопрос о QML-контексте). Подходит для «глобальных» объектов уровня приложения.
Что доступно из QML автоматически
Q_PROPERTY— читается и пишется как свойство QML, изменения черезNOTIFY-сигнал запускают биндинги.- Публичные
slotsи методы с макросомQ_INVOKABLE— вызываются как обычные JS-функции. Q_ENUM/Q_FLAG— становятся перечислениями видаMyClass.ValueA.
Подводные камни
- Отсутствие Q_OBJECT: без макроса
Q_OBJECTmoc не обработает класс, сигналы и Q_PROPERTY просто не будут работать — и ошибку компилятора при этом не получить. - Потоки: QML-движок работает в GUI-потоке; вызов слотов из фонового потока требует
Qt::QueuedConnection. - Владение памятью: объекты, созданные в C++ и переданные QML, по умолчанию остаются под управлением C++; объекты, созданные внутри QML, управляются движком — смешивать без осторожности опасно (двойное удаление).
- Устаревший QQmlEngine::setObjectOwnership: явно задавайте
QQmlEngine::CppOwnershipдля объектов, которые QML не должен удалять. - Версия URI: в Qt 6 с
QML_ELEMENTверсии мажорного/минорного модуля контролируются CMake-конфигурацией, ручные вызовыqmlRegisterTypeс другой версией конфликтуют. - Типы возврата: методы, возвращающие
QObject*, работают; возвращающие пользовательские C++ типы без регистрации в мета-системе — нет. - Отладка: включайте
QML_IMPORT_PATHи запускайте с-qmljsdebuggerдля диагностики ошибок регистрации типов.
Common mistakes
- Объяснять QML и C++ integration только по синтаксису, без жизненного цикла и стоимости.
- Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
- Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
- Смешивать signals/slots с generic callbacks и забывать про thread affinity.
What the interviewer is testing
- Кандидат формулирует точную модель для QML и C++ integration, а не только определение.
- Пример компилируем, безопасен по lifetime и соответствует версии технологии.
- Названы trade-off, ограничения и диагностируемые симптомы ошибки.
- Понимает границу между C++ кодом, runtime/framework metadata и editor/UI слоем.