Привязка свойств в QML

Добавлено 10 мая 2022 в 15:44

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

Привязки свойств – это основная функция QML, которая позволяет разработчикам задавать отношения между свойствами различных объектов. Когда значение зависимостей свойств изменяется, свойство автоматически обновляется в соответствии с указанным отношением.

За кулисами механизм QML отслеживает зависимости свойств (то есть переменные в выражении привязки). При обнаружении изменения механизм QML повторно вычисляет выражение привязки и применяет к свойству новый результат.

Обзор

Чтобы создать привязку свойства, свойству присваивается выражение JavaScript, результатом которого является необходимое значение. В самом простом случае привязка может быть ссылкой на другое свойство. Возьмем следующий пример, где высота синего прямоугольника привязана к высоте его родителя:

Rectangle {
    width: 200; height: 200

    Rectangle {
        width: 100
        height: parent.height
        color: "blue"
    }
}

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

Привязка может содержать любое допустимое выражение или инструкцию JavaScript, поскольку QML использует механизм JavaScript, соответствующий стандартам. Привязки могут получать доступ к свойствам объекта, вызывать методы и использовать встроенные объекты JavaScript, такие как Date и Math. Ниже приведены другие возможные привязки для предыдущего примера:

height: parent.height / 2

height: Math.min(parent.width, parent.height)

height: parent.height > 100 ? parent.height : parent.height/2

height: {
    if (parent.height > 100)
        return parent.height
    else
        return parent.height / 2
}

height: someMethodThatReturnsHeight()

Ниже приведен более сложный пример, включающий больше объектов и типов:

Column {
    id: column
    width: 200
    height: 200

    Rectangle {
        id: topRect
        width: Math.max(bottomRect.width, parent.width/2)
        height: (parent.height / 3) + 10
        color: "yellow"

        TextInput {
            id: myTextInput
            text: "Hello QML!"
        }
    }

    Rectangle {
        id: bottomRect
        width: 100
        height: 50
        color: myTextInput.text.length <= 10 ? "red" : "blue"
    }
}

В предыдущем примере

  • topRect.width зависит от bottomRect.width и column.width
  • topRect.height зависит от column.height
  • bottomRect.color зависит от myTextInput.text.length

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

Создание привязок свойств из JavaScript

Свойство с привязкой автоматически обновляется по мере необходимости. Однако если свойству позже будет присвоено статическое значение из инструкции JavaScript, привязка будет удалена.

Например, приведенный ниже прямоугольник изначально гарантирует, что его высота всегда будет в два раза больше его ширины. Однако при нажатии клавиши пробела текущее значение width*3 будет присвоено свойству height как статическое значение. После этого высота останется фиксированной на этом значении, даже если ширина изменится. Присвоение статического значения удаляет привязку.

import QtQuick 2.0

Rectangle {
    width: 100
    height: width * 2

    focus: true
    Keys.onSpacePressed: {
        height = width * 3
    }
}

Если намерение состоит в том, чтобы придать прямоугольнику фиксированную высоту и остановить автоматические обновления, то это не проблема. Однако если намерение состоит в том, чтобы установить новую связь между шириной и высотой, то вместо этого новое выражение привязки должно быть заключено в функцию Qt.binding():

import QtQuick 2.0

Rectangle {
    width: 100
    height: width * 2

    focus: true
    Keys.onSpacePressed: {
        height = Qt.binding(function() { return width * 3 })
    }
}

Теперь, после нажатия клавиши пробела, высота прямоугольника будет продолжать автоматически обновляться, чтобы всегда быть в три раза больше его ширины.

Отладка перезаписи привязок

Распространенной причиной ошибок в приложениях QML является случайная перезапись привязок статическими значениями из инструкций JavaScript. Чтобы помочь разработчикам отслеживать подобные проблемы, механизм QML может выдавать сообщения всякий раз, когда привязка теряется из-за императивных присваиваний.

Чтобы генерировать такие сообщения, вам необходимо включить информационный вывод для категории логирования qt.qml.binding.removal, например, вызвав:

QLoggingCategory::setFilterRules(QStringLiteral("qt.qml.binding.removal.info=true"));

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

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

Использование this с привязкой свойств

При создании привязки свойства из JavaScript ключевое слово this может использоваться для ссылки на объект, который получает привязку. Это полезно для разрешения неоднозначностей с именами свойств.

Например, обработчик Component.onCompleted ниже определен в пределах области видимости Item. В этой области width относится к ширине Item, а не к ширине Rectangle. Чтобы привязать высоту прямоугольника к его собственной ширине, выражение привязки должно явно ссылаться на this.width (или, в качестве альтернативы, rect.width):

Item {
    width: 500
    height: 500

    Rectangle {
        id: rect
        width: 100
        color: "yellow"
    }

    Component.onCompleted: {
        rect.height = Qt.binding(function() { return this.width * 2 })
        console.log("rect.height = " + rect.height) // prints 200, not 1000
    }
}

Примечание. Значение this не определяется вне привязок свойств. Подробности смотрите в разделе «Ограничения среды JavaScript».

Теги

GUI / Графический интерфейс пользователяQMLQtQtQuickПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.