TextEdit 的 属性 `text` 的绑定在 QtQuick 中如何工作?

How does the binding of the property `text` of the TextEdit work in QtQuick?

我是 QML 的初学者,我正在尝试遵循 QtQuick 的标准组件行为作为指导。

我想以 TextEdit 的工作方式实现用户输入组件。但是现在我真的很困惑 text 属性 是如何实现的。

假设一:

TextEdit的text是只写的,总是显示什么,如果存在,它的text 属性是assigned/bind到.

实验 1

操作

运行QML

Window {
    visible: true
    property string foo: "foo"
    TextEdit { text: foo }
}

在 TextEdit 中输入任何内容

预期文本始终显示"foo"且无法编辑。

实际文本随您键入的内容而变化。

结论假设1不成立

假设二:

text 属性 设置为它在初始化时绑定的内容。 一旦用户对其进行编辑,text 就会被分配给一个新值,这会破坏绑定。

实验二

操作

运行QML

Window {
    visible: true
    property string foo: "foo"
    TextEdit { id: t1; text: foo; x: 0; }
    TextEdit { id: t2; text: t1.text; x: 100 }
}

在 t2 中输入内容,然后在 t1 中输入内容

预期编辑t2后,编辑t1将不再改变t2的显示。

实际 t2 可以单独编辑。但是一旦再次编辑 t1,t2 的显示将再次绑定到 t1。

结论假设2不成立

假设 3:

TextEdit 有类似 internalText 的内容,代表用户输入的内容。并且 TextEdit 显示 internalTexttext,以最新更改的为准。

实验 3

运行

运行QML

Window {
    visible: true
    property string foo: "foo"
    TextEdit { id: t1; text: foo; x: 0; }
    Button { x: 100; onClick: foo = "bar" } // A custom button
}

在 t1

中输入 "blablabla"

点击按钮

在 t1

中输入 "blablabla"

点击按钮,点击按钮,点击按钮一千次

预期 t1 可以编辑,单击按钮时显示重置为 "bar"。

Actual 第一次点击显示设置为"bar",之后点击按钮完全没有效果。

结论假设3是错误的...什么...??

还有...

看完QML Reference several times several times especially the Property Binding部分后,我不知道这样的属性是怎么写和读的(也不像Angular的双向绑定)。我很纳闷。

做了实验 3 之后我真的很困惑,有人能解释一下它是如何工作的吗?

绑定更像是元属性 而不是实际值。一旦您从 C++ 端想象它是如何工作的,它就很容易理解。

绑定是使用信号和槽实现的。简单的 QML 行

text: foo.text

作为示例等同于以下 C++ 代码(简化):

// first: call the getter of text on foo, and the setter on this
this->setText(foo->text());
// second: connect the change signal of foo to this
connect(foo, SIGNAL(textChanged(QString)),
        this, SLOT(setText(QString)));

如您所见,绑定基本上是:"Assign me the current value, and whenever your value changes, update my value to yours"。但是由于 "this" 仍然有它自己的文本副本,您可以通过调用 this->setText("something") 来修改它而不破坏绑定(或者用 c++ 术语 - 信号连接)

这也解释了第二个(和第一个)实验:每当您在 GUI 中更改 t2 的文本时,t2 的内部文本都会更新。但是,由于绑定仍然存在,每次更改 t1 时,t1 都会发出 textChanged 信号,从而也会更新 t2。

第三个实验有点棘手。 C++代码被大大简化了,只解释了发生了什么,而不是它是如何工作的。对于第3种情况,我们要看qml的javascript这边。更具体:如何从 javascript:

创建绑定
// the qml line
text: foo
// is eqivalent to the js
t1.text = Qt.binding(function(){return foo;})

所以你实际上并没有给t1.text赋值,而是给"binding object"赋值。 (Qt.binding 函数做了一些魔法,returns 这样一个对象)这在内部与上面的 c++ 代码相同,但有一个区别。一旦您将新值分配给 t1.text,就像您在按钮中所做的那样,当您用新值替换绑定对象时,旧绑定将与信号连接一起被删除。

因此在按钮的 JS 代码中,您将绑定对象替换为值 "bar",从而销毁它。从 GUI 编辑文本不会破坏绑定,因为只有内部值发生变化,实际上没有任何内容分配给 QML 中的 属性。

编辑: 现在,后续按钮点击不执行任何操作的原因是:按下按钮一次后,foo 设置为 "bar",这会触发textChanged 信号并因此改变 t1。然而,下一次按下按钮时 foo 再次设置为 bar,它什么也不做,因为它已经是 "bar"。您不会通过将其设置为与现在相同的值来更改任何内容。想象一下这样的 setter 实现:

void setText(QString text) {
    if(this->text == text)
        return;
    this->text = text;
    emit textChanged(text);
}

如果您修改代码,例如像这样:

Button { x: 100; onClick: foo = t1.text + "bar" }

它将始终有效并相应地更新 foo,因为此表达式始终会为 foo 生成一个新值。

我希望我能解释你能理解的行为。