Интеграция QML и C++
В простом случае интеграция может осуществляться с помощью виджета QDeclarativeView. Он является наследником QGraphicsView и уже включает в себя необходимые компоненты для встраивания QML-приложения. Другой подход состоит в построении новых типов C++, доступных в среде выполнения QML, с помощью плагинов. При использовании в QML такие типы обладают теми же возможностями, как и в C++-приложении.
В более сложных ситуациях способ интеграции QML в C++ зависит от структуры кода на C++. Если имеется приложение, использующее виджеты C++, то можно использовать все доступные графические компоненты и переписать все классы, основанные на QWidget, на QML без изменения функциональности приложения. Поскольку в этом случае уже есть готовая архитектура приложения, дальнейшие шаги в разработке на QML будут относительно простыми. Если же приложение основано на QGraphicsView, то процесс преобразования еще проще и может осуществляться в несколько этапов. Можно поместить целое QML-приложение в существующее графическое представление путём создания QML-движка (QML engine). Кроме того, можно использовать тот факт, что интерфейс QML может сосуществовать с пользовательским интерфейсом на C++. Это позволяет производить процесс преобразования пошагово. Три шага, необходимые для создания QML-движка и контекста с существующим QGraphicsView: 1) создаётся объект класса QDeclarativeEngine, представляющий собой окружение для порождения QML-компонентов 2) QML-код компонента помещается в специальный компонент — объект класса QDeclarativeComponent 3) На базе компонента создаётся объект, являющийся наследником класса QGraphicsObject, который может быть добавлен к существующей сцене. Таким образом, он будет сосуществовать с остальной частью пользовательского интерфейса. QGraphicsScene *scene = ...;
QDeclarativeEngine *engine = new QDeclarativeEngine; QDeclarativeComponent component(engine, QUrl::fromLocalFile(...)); QGraphicsObject *object = qobject cast<QGraphicsObject*>(component.create());
scene->addItem(object); Если компонент не может загрузить QML-файл, то значение свойства error будет равно true. Чтобы выводить сообщения об ошибках, надо поместить следующую строку кода после вызова метода create (): qWarning() << component.errors(); Описанный выше код можно использовать только, если в проект подключен <QtDebug>. См. документацию по qWarning () для более детального изучения. Для выравнивания элементов пользовательского интерфейса можно изменить значение параметра z QGraphicsObject, чтобы поместить элемент в соответствующей глубине сцены. Для достижения оптимальной производительности пользовательского интерфейса QML, рекомендуется установить следующие опции: QGraphicsView *view = ...;
view->setOptimizationFlags(QGraphicsView::DontSavePainterState); view->setViewportUpdateMode( QGraphicsView::BoundingRectViewportUpdate); view->setItemIndexMethod(QGraphicsScene::NoIndex);
Обмен данными между C++ и QML Qt Quick предоставляет множество способов обмена данными между C++ и QML, как используя реализацию стандартного шаблона проектирования "model-view", так и не применяя ее. Кроме того, можно вызывать QML-функции из C++ и наоборот. В общем, поскольку все элементы унаследованы от QObject, все их сигналы, слоты и свойства доступны в QML. Весь код на QML выполняется через контекст. Контекст отслеживает, какие данные доступны для различных листьев и узлов в дереве (иерархии) объектов QML. Данные передаются как свойства контекста или объекты контекста. Свойство контекста - это простой способ определить объект, задавая имя этого объекта. Например, чтобы определить объект класса класса QColor как свойство с именем frameColor в QML, можно просто использовать следующий код: QDeclarativeContext *context = ...; context->setContextProperty("frameColor", QColor(Qt::red)); Это свойство может быть доступно через QML-контекст как глобальное свойство, как показано ниже. Следует помнить, что в QML значения свойств привязываются, а не присваиваются. Это означает, что можно изменить свойство frameColor в C++-коде, и эти изменения будут отображены в QML. Rectangle { border.color: frameColor } Можно добавить несколько контекстных свойств в объект класса QDeclarativeContext, однако с увеличением списка свойств ухудшается понятность кода. Поэтому вместо того чтобы задавать каждое свойство отдельно, лучше собрать все контекстные свойства в одном объекте QObject и установить его в качестве контекстного объекта. Следующий код показывает, как определить объект интерфейса MyInterface, используя метод setContextProperty (). Макрос Q_PROPERTY определяет свойства, доступные через объект класса MyInterface из системы свойств библиотеки Qt и задает сигналы, которые могут быть использованы далее в работе. Отметим, что все свойства, добавленные явно при помощи QDeclarativeContext:: setContextProperty (), имеют более высокий приоритет, чем свойства контекстного объекта по умолчанию. class MyInterface : ... { ... Q PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged) ... };
MyInterface *myInterface = new MyInterface; QDeclarativeEngine engine; QDeclarativeContext *context = new QDeclarativeContext(engine.rootContext()); context->setContextObject(myDataSet); QDeclarativeComponent component(&engine); component.setData("import Qt 4.7\nListView { model: myModel }", QUrl()); component.create(context); QML-представления в моделях на C++ Свойства объекта хорошо работают, когда набор их значений в QML ограничен, но ими становится трудно управлять, когда появляются большие объемы данных. В этих случаях формальные модели визуализируются при помощи формальных представлений. Этот шаблон проектирования "model/view" позволяет разработчикам отделять реализацию пользовательского интерфейса от бизнес-логики. Модель может быть реализована на языке С++, в то время как представление написано на QML. QML может создавать представления для моделей на C++, которые являются наследниками класса QAbstractItemModel.
Чтобы работать с QAbstractItemModel в QML, надо использовать контекстное свойство: QAbstractItemModel *model = ...; context->setContextProperty("dataModel", model); Класс QAbstractItemModel в библиотеке Qt предоставляет абстрактный интерфейс для классов модели. Этот класс определяет стандартный интерфейс для моделей, которые могут взаимодействовать с другими компонентами в архитектуре "модель-представление". Нельзя создать объект класса QAbstractItemModel. Необходимо определить наследника этого класса, чтобы создавать новые модели. Класс QAbstractItemModel входит в Model/View Classes и является частью Qt model/view framework.
Программирование на QML / C++
Qt Quick позволяет QML вызывать методы, написанные на C++, и дает возможность обрабатывать C++-сигналы при помощи выражений на JavaScript через контекст QML. Вызов C++-методов в QML Для передачи данных от пользователя к бизнес-логике, QML должен иметь возможность вызывать C++-методы. Это достигается с помощью слотов или методов Q_INVOKABLE. Обеспечив QML доступ к QObject как к контекстному свойству, слоты и методы Q_INVOKABLE соответствующего класса могут быть вызваны из QML. Например, приведенный ниже класс-наследник QObject добавляется в QML как контекстное свойство. class CallableClass : public QObject { Q_OBJECT ... public slots: void cppMethod() { qDebug(”C++ method called!”); } };
...
context->setContextProperty(”cppObject”, new CallableClass); Теперь в QML-коде можно обратиться к методу cppMethod при помощи глобального объекта cppObject. В этом примере метод не возвращает никакого значения и не принимает каких-либо аргументов, но это не запрещено в QML. QML поддерживает все возможные в нем типы возвращаемых значений и аргументов. MouseArea { ... onClicked: { cppObject.cppMethod(); } }
Обработка Qt-сигналов в QML Qt C++ сигналы могут быть обработаны с помощью выражений на языке JavaScript в QML-контексте. Пусть класс CallableClass из предыдущего примера также содержит свой сигнал: cppSignal (). class CallableClass : public QObject { Q_OBJECT ... signals: void cppSignal(); }; Сигнал может быть обработан в QML при помощи элемента Connections. Этот элемент может быть использован для обработки сигналов любого целевого объекта, в том числе и других элементов QML. Обработчик сигнала будет называться onSignalName. Здесь первая буква в имени сигнала становится заглавной. Connections { target: cppObject
onCppSignal: { console.log("QML function called!"); } } Расширение QML с помощью C++
В QML есть встроенная поддержка для обширного набора типов элементов, но, когда конкретное приложение нуждается в поддержке собственных классов, можно дополнить возможности QML с помощью пользовательских типов элементов, написанных на C++. Например, необходимо создать QML-элемент, который называется Person и имеет свойства name и shoeSize. Все QML-элементы имеют однозначное соответствие с типами C++. В листинге 1 объявляется класс Person на С++ с двумя свойствами: name и shoeSize, которые должны быть доступны в типе QML. Хотя в этом примере для C++-класса используется то же имя, что и для элемента QML, C++-класс может иметь и другое название, или находиться в другом пространстве имен. class Person : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
public: Person(QObject *parent = 0);
QString name() const; void setName(const QString &);
int shoeSize() const; void setShoeSize(int);
private: QString m_name; int m_shoeSize; }; Person::Person(QObject *parent): QObject(parent), m_shoeSize(0) { }
QString Person::name() const { return m_name; }
void Person::setName(const QString &n) { m_name = n; }
int Person::shoeSize() const { return m_shoeSize; }
void Person::setShoeSize(int s) { m_shoeSize = s; } Реализация класса Person вполне стандартная. Его методы просто возвращают значения полей. В файле main.cpp также вызывается функция qmlRegisterType(), чтобы зарегистрировать тип Person в QML как тип в библиотеке People версии 1.0, и определяется соответствие между названиями класса в C++ и в QML. Теперь тип Person может быть использован в QML: import People 1.0
Person { name: "Bob Jones" shoeSize: 12 }
Источник: http://qt.nokia.com/files/pdf/qt-quick-for-c-developers |