【问题标题】:How does the binding of the property `text` of the TextEdit work in QtQuick?QtQuick 中 TextEdit 的属性 `text` 的绑定如何工作?
【发布时间】:2018-05-08 17:00:12
【问题描述】:

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

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

假设 1:

TextEdit 的text 是只写的,并且始终显示其text 属性分配/绑定到的内容(如果存在)。

实验一

操作

运行 QML

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

在 TextEdit 中输入任何内容

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

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

结论假设1是错误的。

假设 2:

text 属性设置为初始化时绑定的对象。 一旦用户对其进行编辑,text 就会被赋予一个新值,这会破坏绑定。

实验 2

操作

运行 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 可以编辑,点击按钮后显示重置为“栏”。

实际在第一次点击时显示设置为“栏”,之后点击按钮完全没有效果。

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

还有……

在阅读了QML Reference several times 几次之后,尤其是Property Binding 部分,我不知道这样可以读写的属性如何(也不像Angular 的2 路绑定)。我很困惑。

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

【问题讨论】:

    标签: qt qml qtquick2 qt-quick


    【解决方案1】:

    绑定更像是元属性而不是实际值。一旦你想象它是如何从 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)));
    

    如您所见,绑定基本上是:“为我分配当前值,并且每当您的值更改时,将我的值更新为您的值”。但是由于“this”仍然有它自己的文本副本,您当然可以通过调用this->setText("something") 来修改它,而不会破坏绑定(或用 c++ 术语 - 信号连接)

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

    现在进行第三个实验,它有点棘手。 c++ 代码被大大简化,只解释发生了什么,而不是它实际上是如何工作的。对于第三种情况,我们必须看看 qml 的 javascript 端。更具体:如何从 javascript 创建绑定:

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

    所以你并没有真正为t1.text 赋值,而是一个“绑定对象”。 (Qt.binding 函数做了一些魔术并返回这样一个对象)这在内部与上面的 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 生成一个新值。

    我希望我能解释一下你可以理解的行为。

    【讨论】:

    • 我认为这仍然无法解释实验 3:为什么要分配给 foo 打破 t1.text 的绑定?应该支持t1.text,而不是foo
    • 我编辑了回复以详细解释它。希望能为你解释它
    • 好的,这可以解释这种行为。但我很难过 QML 被称为是一种数据绑定语言,而其标准库中的组件却没有遵循单一来源的真实习惯。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-24
    • 2012-09-20
    • 2014-01-28
    • 1970-01-01
    • 1970-01-01
    • 2017-02-21
    相关资源
    最近更新 更多