Как преобразовать свойство x или y QQuickItem на основе его текущего значения без использования функции QML

Я использую коммерческую версию Qt 5.9.3.

Сценарий
У меня есть логика, которая должна быть выполнена в части Qt QML моего кода. При нажатии какой-то случайной кнопки. Я хочу переместить прямоугольник QML в другую позицию. Другая позиция вычисляется (x, y) position на основе текущей позиции прямоугольника.

Вот что я хочу сделать:

Код:

import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    visible: true
    width: 800
    height: 600
    title: qsTr("Hello World")

    property bool some_boolean_flag: false

    Rectangle {
        id: my_rect_1
        color: "blue"
        width: parent.width / 4
        height: parent.height / 4
        z:1

        property real current_x
        property real current_y

        x: {
            if (some_boolean_flag) {
                current_x = current_x + current_x/10
                return current_x
            }
            else {
                var default_x = parent.width/6
                current_x = default_x
                return current_x
            }
        }
        y: {

            if (some_boolean_flag) {
                current_y = current_y + current_y/10
                return current_y
            }
            else {
                var default_y = parent.height/6
                current_y = default_y
                return current_y
            }
        }
    }

    Rectangle {
        id: my_rect_2
        color: "yellow"
        width: parent.width/5
        height: parent.height/5
        x: parent.width - parent.width/4
        y: parent.height - parent.height/4
        z:2

        MouseArea {
            anchors.fill: parent
            onClicked: {
                // swapping the boolean flag between true and false
                some_boolean_flag = !some_boolean_flag
            }
        }
    }
}

Цель этого вопроса:
Обратите внимание, что я хочу выполнить расчет декларативно. Я могу легко создать QML-функцию и сделать это, объявив некоторые глобальные свойства, что, конечно, несложно, но суть этого вычисления не в этом. Моя цель - научиться делать это декларативно. Как применить расчет к QQuickItem::x на основе самого текущего значения QQuickItem::x..

Если вы запустите пример кода в этом вопросе, вы увидите, что my_rect_1 просто продолжает переключаться между двумя позициями вперед и назад, что на самом деле не соответствует логике. Цель логики состоит в том, чтобы продолжать увеличивать my_rect_1::x и my_rect_1::y на определенный коэффициент, который заставит my_rect_1 перемещаться вправо и вниз, когда вы продолжаете нажимать на другой прямоугольник, который является my_rect_2.

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

qrc:/main.qml:12:5: QML Rectangle: Binding loop detected for property "x"

Насколько я понимаю, QML жалуется на циклическую зависимость свойства x в my_rect_1. Как этого избежать и добиться декларативного преобразования x и y?

Потенциальное решение, которое я нашел в ходе поиска
Похоже на Перевести тип QML может быть полезен в логике, которую я хочу реализовать, но как?

Как я могу добиться такого поведения без написания функции QML, которая выполняет это преобразование отдельно вне my_rect_1?


person TheWaterProgrammer    schedule 24.02.2018    source источник


Ответы (1)


Что ж, «декларативно» будет просто включать привязки, которые по своей сути в значительной степени являются функциями, с дополнительным преимуществом автоматической переоценки при изменении значений, участвующих в выражениях.

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

ApplicationWindow {
  id: main
  width: 640
  height: 480
  visible: true

  property real xpos: 10
  property real ypos: 10

  Rectangle {
    id: rect
    x: xpos
    y: ypos
    width: 10
    height: 10
    radius: 5
    color: "red"
  }

  MouseArea {
    anchors.fill: parent
    onClicked: {
      xpos = (xpos + mouseX) * .5
      ypos = (ypos + mouseY) * .5
      console.log(ypos)
    }
  }
}

Обратите внимание, что свойства xpos и ypos практически избыточны, и то же самое можно сделать и без них, но это потребует ручного манипулирования координатами точки, поэтому ее положение не будет объявлено чисто «декларативным способом». Таким образом, он привязан к значениям свойств и будет продолжать следовать им независимо от того, какой процесс контролирует значения.

Что касается этой части вашего кода:

  x: {
      if (some_boolean_flag) {
          current_x = current_x + current_x/10
          return current_x
      }
      else {
          var default_x = parent.width/6
          current_x = default_x
          return current_x
      }
  }

Это выражение привязки, которое определяет значение свойства. Если вы присваиваете значение свойству вручную, оно разрывает существующую привязку. Что вы делаете, так это присваиваете значение свойству в выражении, которое должно делать это автоматически — чистая ерунда, которая также объясняет, почему это не работает должным образом.

Подходящим форматом будет:

  x: {
      if (some_boolean_flag) {
          return current_x + current_x/10
      }
      else {
          var default_x = parent.width/6
          return default_x
      }
  }

Что на самом деле может быть переработано до:

  x: some_boolean_flag ? current_x + current_x/10 : parent.width/6      

и т. д. И, как вы видите, использование этого формата удаляет предупреждения цикла привязки, и теперь у вас есть поведение, соответствующее логике кода.

person dtech    schedule 24.02.2018
comment
не мог ожидать лучшего ответа, чем этот. так что ошибка, которую я делаю, это current_x = current_x + current_x/10; return current_x? выполнение логики в двух строках кода, где первая строка присваивает его временному свойству разрывает привязку? Интересно. большое спасибо за это подробное объяснение! - person TheWaterProgrammer; 24.02.2018
comment
Если вы делаете prop = value, это разрывает любую привязку, которую он мог прикрепить, независимо от того, где это происходит. Обратите внимание, что вы можете создать привязку императивно, используя prop = Qt.binding() =, которая прикрепит новую привязку, в отличие от простого присваивания. - person dtech; 24.02.2018
comment
Я прочитал об этом Qt.binding() здесь. Похоже, я все еще могу сделать эти бесплатные 2 строки (current_x = current_x + current_x/10; return current_x) из function, используя Qt.binding()? Таким образом, есть еще один способ добиться этой привязки и сделать current_x = current_x + current_x/10; вернуть current_x`? - person TheWaterProgrammer; 24.02.2018
comment
Я принял ответ, но у меня есть один дополнительный вопрос, который нужно уточнить в комментарии выше. Пожалуйста, ответьте, если можно - person TheWaterProgrammer; 24.02.2018
comment
Опять же, выражение привязки будет автоматически присваивать возвращаемое значение свойству. Самостоятельно присваивать его бессмысленно и бессмысленно, это неправильно, плохо оформлено и не служит никакой цели. Почему ты продолжаешь настаивать на этом? - person dtech; 24.02.2018
comment
У меня довольно сложный расчет, который нужно связать/выполнить на лету с помощью свойства x и y. поэтому и спрашивал об этом. но в любом случае, у меня есть другой способ сделать это, используя способ, который вы предлагаете, с техникой xpos и ypos. - person TheWaterProgrammer; 24.02.2018
comment
Какая часть этого расчета требует присвоения значения свойства более одного раза? В любом случае, вы можете сделать var tempvalue = propValue и использовать tempvalue для назначения и еще много чего, и, наконец, return tempvalue. Вся цель выражения привязки состоит в том, чтобы присвоить возвращаемое значение свойству, поэтому нет смысла вручную присваивать значение этому свойству в его теле. - person dtech; 24.02.2018
comment
Давайте продолжим это обсуждение в чате. - person TheWaterProgrammer; 24.02.2018