Spec-Zone.ru › Qt 5.9

Предоставление атрибутов типов C++ в QML

QML легко расширяется с помощью функциональности, определенной в коде C++. Благодаря тесной интеграции движка QML с системой метаобъектов Qt, любая функциональность, надлежащим образом предоставленная производным классом QObject, доступна из кода QML. Это позволяет напрямую обращаться к данным и функциям C++ из QML, часто с небольшими или без изменений.

Движок QML может инспектировать экземпляры QObject с помощью системы метаобъектов. Это означает, что любой код QML может получить доступ к следующим членам экземпляра производного класса QObject:

  • Свойства
  • Методы (при условии, что они являются открытыми слотами или помечены Q_INVOKABLE)
  • Сигналы

(Кроме того, перечисления доступны, если они были объявлены с Q_ENUMS. Подробнее см. Преобразование типов данных между QML и C++).

Как правило, к ним можно получить доступ из QML независимо от того, был ли производный класс QObject зарегистрирован в системе типов QML. Однако, если класс должен использоваться таким образом, что движку требуется доступ к дополнительной информации о типе (например, если сам класс используется в качестве параметра метода или свойства, или если один из его типов перечислений используется таким образом), то класс может потребоваться зарегистрировать.

Также обратите внимание, что ряд важных концепций, рассмотренных в этом документе, продемонстрированы в учебнике «Создание расширений QML с помощью C++».

Обработка типов данных и владение

Любые данные, передаваемые из C++ в QML, будь то в качестве значения свойства, параметра метода или возвращаемого значения, или значения параметра сигнала, должны быть типа, поддерживаемого движком QML.

По умолчанию движок поддерживает ряд типов Qt C++ и может автоматически преобразовывать их при использовании из QML. Кроме того, классы C++, зарегистрированные в системе типов QML, могут использоваться как типы данных, так и их перечисления, если они надлежащим образом зарегистрированы. Дополнительную информацию см. в Преобразование типов данных между QML и C++.

Кроме того, при передаче данных из C++ в QML учитываются правила владения данными. Подробную информацию см. в Владение данными.

Предоставление свойств

Свойство может быть указано для любого производного класса QObject с помощью макроса Q_PROPERTY(). Свойство — это член данных класса с ассоциированной функцией чтения и необязательной функцией записи.

Все свойства производного класса QObject доступны из QML.

Например, ниже представлен класс Message со свойством author. Как указано в вызове макроса Q_PROPERTY, это свойство читается через метод author(), и записывается через метод setAuthor().

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
    void setAuthor(const QString &a) {
        if (a != m_author) {
            m_author = a;
            emit authorChanged();
        }
    }
    QString author() const {
        return m_author;
    }
signals:
    void authorChanged();
private:
    QString m_author;
};

Если экземпляр этого класса был установлен как контекстное свойство при загрузке файла с именем MyItem.qml из C++:

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

    QQuickView view;
    Message msg;
    view.engine()->rootContext()->setContextProperty("msg", &msg);
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}

Тогда свойство author можно было бы считать из MyItem.qml:

// MyItem.qml
import QtQuick 2.0

Text {
    width: 100; height: 100
    text: msg.author    // invokes Message::author() to get this value

    Component.onCompleted: {
        msg.author = "Jonah"  // invokes Message::setAuthor()
    }
}

Для максимальной совместимости с QML, любое свойство, которое можно изменить, должно иметь связанный сигнал NOTIFY, который генерируется всякий раз, когда значение свойства изменилось. Это позволяет использовать свойство с связыванием свойств, что является важной функцией QML, которая обеспечивает взаимосвязи между свойствами, автоматически обновляя свойство всякий раз, когда значение любой из его зависимостей изменяется.

В приведенном выше примере связанный сигнал NOTIFY для свойства author — authorChanged, как указано в вызове макроса Q_PROPERTY(). Это означает, что всякий раз, когда генерируется сигнал (как при изменении автора в Message::setAuthor()), это уведомляет движок QML о необходимости обновления любых связанных свойств, и, в свою очередь, движок обновит свойство author, снова вызвав Message::author().

Если свойство author было изменяемым, но не имело связанного сигнала NOTIFY, значение text было бы инициализировано начальным значением, возвращенным Message::author(), но не обновлялось бы при последующих изменениях этого свойства. Кроме того, любые попытки привязаться к свойству из QML приведут к предупреждению во время выполнения от движка.

Примечание: Рекомендуется, чтобы сигнал NOTIFY назывался <свойство>Changed, где <property> — имя свойства. Обработчик сигнала изменения связанного свойства, генерируемый движком QML, всегда будет иметь вид on<Property>Changed, независимо от имени соответствующего сигнала C++, поэтому рекомендуется, чтобы имя сигнала соответствовало этой конвенции, чтобы избежать путаницы.

Примечания по использованию сигналов Notify

Для предотвращения циклов или чрезмерной оценки разработчики должны убедиться, что сигнал изменения свойства генерируется только тогда, когда значение свойства фактически изменилось. Кроме того, если свойство или группа свойств используется редко, разрешается использовать один и тот же сигнал NOTIFY для нескольких свойств. Это следует делать с осторожностью, чтобы не пострадала производительность.

Наличие сигнала NOTIFY влечет за собой небольшую нагрузку. Существуют случаи, когда значение свойства устанавливается во время создания объекта и впоследствии не изменяется. Наиболее распространенный случай — когда тип использует группированные свойства, а объект сгруппированного свойства выделяется один раз и освобождается только при удалении объекта. В этих случаях атрибут CONSTANT можно добавить к объявлению свойства вместо сигнала NOTIFY.

Атрибут CONSTANT следует использовать только для свойств, значение которых устанавливается и завершается только в конструкторе класса. Все остальные свойства, которые должны использоваться в связях, должны иметь сигнал NOTIFY вместо этого.

Свойства с типами объектов

Свойства типа объекта доступны из QML, при условии, что тип объекта был надлежащим образом зарегистрирован в системе типов QML.

Например, тип Message может иметь свойство body типа MessageBody*:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged)
public:
    MessageBody* body() const;
    void setBody(MessageBody* body);
};

class MessageBody : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged)
// ...
}

Предположим, что тип Message был зарегистрирован в системе типов QML, позволяя использовать его как тип объекта из кода QML:

Message {
    // ...
}

Если тип MessageBody также был зарегистрирован в системе типов, то можно было бы назначить MessageBody свойству body типа Message, все это в коде QML:

Message {
    body: MessageBody {
        text: "Hello, world!"
    }
}

Свойства с типами списков объектов

Свойства, содержащие списки производных типов QObject, также могут быть доступны QML. Однако для этой цели следует использовать QQmlListProperty вместо QList<T> в качестве типа свойства. Это связано с тем, что QList не является производным типом QObject и, следовательно, не может предоставить необходимые характеристики свойства QML, такие как уведомления о сигналах при изменении списка.

QQmlListProperty — это шаблонный класс, который удобно создается из значения QList.

Например, класс MessageBoard ниже имеет свойство messages типа QQmlListProperty, хранящий список экземпляров Message:

class MessageBoard : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
public:
    QQmlListProperty<Message> messages();

private:
    static void append_message(QQmlListProperty<Message> *list, Message *msg);

    QList<Message *> m_messages;
};

Функция MessageBoard::messages() просто создает и возвращает QQmlListProperty из своего члена QList<T> m_messages, передавая соответствующие функции изменения списка, как требуется конструктором QQmlListProperty:

QQmlListProperty<Message> MessageBoard::messages()
{
    return QQmlListProperty<Message>(this, 0, &MessageBoard::append_message);
}

void MessageBoard::append_message(QQmlListProperty<Message> *list, Message *msg)
{
    MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object);
    if (msg)
        msgBoard->m_messages.append(msg);
}

Обратите внимание, что тип шаблона класса для QQmlListProperty (в данном случае Message ) должен быть зарегистрирован в системе типов QML.

Сгруппированные свойства

Любое доступное для чтения свойство типа объекта доступно из кода QML как группированное свойство. Это может использоваться для предоставления группы связанных свойств, описывающих набор атрибутов для типа.

Например, предположим, что свойство Message::author было типа MessageAuthor вместо просто строки, со свойствами name и email:

class MessageAuthor : public QObject
{
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(QString email READ email WRITE setEmail)
public:
    ...
};

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageAuthor* author READ author)
public:
    Message(QObject *parent)
        : QObject(parent), m_author(new MessageAuthor(this))
    {
    }
    MessageAuthor *author() const {
        return m_author;
    }
private:
    MessageAuthor *m_author;
};

Свойство author можно записать, используя синтаксис группированных свойств в QML, например так:

Message {
    author.name: "Alexandra"
    author.email: "alexandra@mail.com"
}

Тип, который предоставляется как группированное свойство, отличается от свойства типа объекта тем, что группированное свойство является только для чтения и инициализируется допустимым значением родительским объектом при создании. Подсвойства группированного свойства могут быть изменены из QML, но сам объект группированного свойства никогда не изменится, в то время как свойству типа объекта может быть назначен новый объект из QML в любое время. Таким образом, жизненный цикл объекта группированного свойства строго контролируется реализацией родительского объекта C++, в то время как свойство типа объекта может быть свободно создано и уничтожено с помощью кода QML.

Предоставление методов (включая Qt-слоты)

Любой метод производного типа QObject доступен из кода QML, если он:

  • Является открытым методом, помеченным макросом Q_INVOKABLE()
  • Методом, который является открытым Qt-слотом

Например, класс MessageBoard ниже имеет метод postMessage(), помеченный макросом Q_INVOKABLE, а также метод refresh(), который является открытым слотом:

class MessageBoard : public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE bool postMessage(const QString &msg) {
        qDebug() << "Called the C++ method with" << msg;
        return true;
    }

public slots:
    void refresh() {
        qDebug() << "Called the C++ slot";
    }
};

Если экземпляр MessageBoard был установлен в качестве контекстных данных для файла MyItem.qml, тогда MyItem.qml может вызвать два метода, как показано в примерах ниже:

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

    MessageBoard msgBoard;
    QQuickView view;
    view.engine()->rootContext()->setContextProperty("msgBoard", &msgBoard);
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}
QML
// MyItem.qml
import QtQuick 2.0

Item {
    width: 100; height: 100

    MouseArea {
        anchors.fill: parent
        onClicked: {
            var result = msgBoard.postMessage("Hello from QML")
            console.log("Result of postMessage():", result)
            msgBoard.refresh();
        }
    }
}

Если у метода C++ есть параметр типа QObject*, значение параметра можно передать из QML, используя объект id или значение JavaScript переменной, ссылающееся на этот объект.

QML поддерживает вызов перегруженных функций C++. Если существует несколько функций C++ с одинаковым именем, но различными аргументами, правильная функция будет вызвана в соответствии с количеством и типами предоставленных аргументов.

Значения, возвращаемые методами C++, преобразуются в значения JavaScript при обращении к ним из выражений JavaScript в QML.

Отображение сигналов

Любой публичный сигнал объекта, производного от QObject, доступен из кода QML.

Двигатель QML автоматически создаёт обработчик сигнала для любого сигнала объекта, производного от QObject, который используется из QML. Обработчики сигналов всегда имеют имя on<Signal>, где <Signal> — имя сигнала с заглавной первой буквой. Все параметры, передаваемые сигналом, доступны в обработчике сигнала через имена параметров.

Например, предположим, что класс MessageBoard имеет сигнал newMessagePosted() с единственным параметром subject.

class MessageBoard : public QObject
{
    Q_OBJECT
public:
   // ...
signals:
   void newMessagePosted(const QString &subject);
};

Если тип MessageBoard был зарегистрирован в системе типов QML, то объект MessageBoard , объявленный в QML, мог бы получать сигнал newMessagePosted() с помощью обработчика сигналов с именем onNewMessagePosted, и исследовать значение параметра subject.

MessageBoard {
    onNewMessagePosted: console.log("New message received:", subject)
}

Как и в случае с значениями свойств и параметрами методов, параметр сигнала должен иметь тип, поддерживаемый движком QML; см. Преобразование типов данных между QML и C++. (Использование незарегистрированного типа не вызовет ошибки, но значение параметра не будет доступно из обработчика.)

Классы могут иметь несколько сигналов с одинаковым именем, но доступен только последний сигнал как QML-сигнал. Обратите внимание, что сигналы с одинаковым именем, но разными параметрами, не могут быть различимы.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.9/qtqml-cppintegration-exposecppattributes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API