Временное значение свойства QML без полного нарушения привязки

В QML (по крайней мере, до версии 5.6 1) привязки свойств нарушаются после назначения нового значения в контексте JavaScript:

Item {
    property bool sourceBool: <some changing value e.g. from C++ code>
    property bool boundBool: sourceBool

    function reassignBool() {
        boundBool = true;
    }
}

однажды reassignBool называется, boundBool будет true независимо от состояния sourceBool,

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

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

MyButton {
    id: onceOnlyButton
    // Suppose the QML type `MyButton` provides a boolean property
    // called `enabled` that greys out the button and prevents it from
    // being clicked when `false`.
    enabled: <condition1> && <scondition2>
    onClicked: {
        <schedule some asynchronous work>
    }
}

Предположим, что когда кнопка нажата, <condition1> в конечном итоге станет false, чтобы кнопка в конечном итоге была отключена соответствующим образом - но не сразу.

Поэтому я хотел бы сделать что-то вроде следующего:

....
onClicked: {
    enabled = false
    <schedule some asynchronous work>
    enabled = Qt.binding(function() {
        return <condition1> && <condition2>
    }
}

... но, конечно, как только я восстановлю привязку, так как condition1 еще не стал false, enabled станет true снова.

Я полагаю, я мог бы создать какой-то Connections объект, который соединит NOTIFY сигнал (ы), связанные с <condition1> к обработчику, который будет вызывать Qt.binding, затем.... удалить Connections объект, я полагаю. Но это кажется довольно сложным и не элегантным.

Есть ли способ обеспечить enabled установлен в false немедленно, затем снова привязать к NOTIFY сигналы для <condition1> а также <condition2> как только какой-либо из этих сигналов испускается?

1 Я не совсем уверен, как назначения влияют на привязки в 5.7, но я знаю, что присвоение значения свойству не нарушает привязку автоматически. Так что это может просто сработать автоматически из 5.7.

1 ответ

Это должно быть довольно простым для достижения с помощью QML Binding type, который отлично подходит для временного переопределения привязки, не разрушая ее (и это было еще с версии 5.6!)

Вам понадобится переменная или свойство, которое вы можете использовать в when свойство типа Binding. В приведенном ниже примере я использовал timer.running потому что запланированная асинхронная работа - это просто таймер.

import QtQuick 2.7
import QtQuick.Controls 2.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    property bool condition1: true
    property bool condition2: true

    Button {
        id: onceOnlyButton

        enabled: condition1 && condition2

        Binding on enabled {
            when: timer.running
            value: false
        }

        onClicked: {
            timer.start()
        }
        text: "onceOnlyButton"
    }

    // stuff below is just for demonstration
    Button {
        anchors.left: onceOnlyButton.right
        text: "toggle Condition1\n(to prove binding still intact)"
        onClicked: condition1 = !condition1
    }

    Timer {
        id: timer
        interval: 2000
        running: false
        repeat: false
        onTriggered: condition1 = false
    }
}

Если у вас нет переменной, вы можете использовать ее, чтобы определить, продолжается ли асинхронная работа (например, моя timer.running) тогда вам нужно будет создать его, как показано ниже forceButtonDisable, Это делает его более сложным, поэтому давайте поместим его в новый компонент многократного использования:

OnceOnlyButton.qml ##

import QtQuick 2.7
import QtQuick.Controls 2.0

Item {
    id: control
    property alias text: button.text
    property bool enabled
    property bool forceButtonDisable: false

    signal clicked()

    onEnabledChanged: forceButtonDisable = false

    width: button.implicitWidth
    height: button.implicitHeight

    Button {
        id: button

        width: control.width
        height: control.height
        enabled: control.enabled

        Binding on enabled {
            when: control.forceButtonDisable
            value: false
        }

        onClicked: {
            control.forceButtonDisable = true
            control.clicked()
        }
    }
}

main.qml

import QtQuick 2.7
import QtQuick.Controls 2.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    property bool condition1: true
    property bool condition2: true

    // once clicked, the button is temporarily disabled until original binding expression results in a different value
    OnceOnlyButton {
        id: onceOnlyButton

        text: "onceOnlyButton"
        enabled: condition1 && condition2
        onClicked: timer.start()
    }

    // stuff below is just for demonstration
    Button {
        anchors.left: onceOnlyButton.right
        text: "toggle Condition1\n(to prove binding still intact)"
        onClicked: condition1 = !condition1
    }

    Timer {
        id: timer
        interval: 2000
        running: false
        repeat: false
        onTriggered: condition1 = false
    }
}
Другие вопросы по тегам