作者:Qt开发技术 大家新年好!这是猪年的第一篇。这里我想给大家讲讲Qt QML里非常重要的一个概念:属性绑定(Property binding)。现代化的开发语言、框架都讲究自动化、智能化,在笔者看来,属性绑定则是QML中这方面的代表。用好属性绑定可以极大提高我们的开发效率。本文首先介绍何为QML属性绑定,然后用通俗易懂的说法来阐释其底层原理,最后和大家讨论开发时几个常见的问题。 如何实现QML属性绑定在QML中实现属性绑定有三种方法,每种方法都有其各自的优缺点。 1. 冒号绑定这是最常见的绑定方法,在定义属性时使用QML的冒号语法,所以笔者称之为“冒号绑定”。 例如下面的QML代码就实现了一个属性绑定: TextField{id: textField} Button{ id: button text: textField.text } 上面第4行代码将 textField 的text 属性绑定到了button 的text 上。只要前者发生变化(例如用户输入、修改),按钮上的文字就会跟着变动。这种方法的优点是简单方便,是三种方法中代码量最少的。 缺点是缺乏弹性,控制能力小,主要有两方面:
2. 使用BindingQML中专门提供了一个类型 Binding 来实现属性绑定。上面的例子如果改用Biding来写则代码如下:TextField{id: textField} Button{id: button} Binding{ target: button property: "text" value: textField.text } 这种方法的优点主要有两个:
这两个可以说完美解决了上面第一种属性绑定的问题。 该方法有两个缺点:
所以只在需要的时候用该方法。 3. 使用Qt.binding()函数这是最后一种属性绑定方法。上面的例子改用该方法的话代码如下: TextField{id: textField} Button{id: button} Component.onCompleted:{ button.text=Qt.binding(function(){returntextField.text;}); } 该方法的好处是可以写在任何js执行代码里。 缺点是只能运行时绑定。由于前两种方法都是QML语法申明,QML执行引擎在初始化的时候有机会使用JIT技术和Cache技术进行优化,而动态执行的js语句是没法进行这种优化的,因此这种方法的执行效率是三种方法中最低的。 QML属性绑定原理在实际开发中,笔者发现很多人会用属性绑定,但经常出错,发生意外的解绑、循环绑定等问题。究其原因,往往是因为对QML属性绑定的底层原理不甚清楚,一旦程序变复杂很容易糊涂。 所谓属性绑定的原理,用直白点的话来说,就是:为什么一个属性变化了,和它绑定的属性能跟着变化? 我们还是用上节中冒号绑定的例子。当我们写下 text: textField.text 这行代码的时候,QML引擎实际上做了下面这些事情:
所以当任何冒号左边表达式里的具有 NOTIFY 信号属性值改变时,相关信号发射,然后构造的槽函数得到执行,计算出新的值,最后将该值赋给被绑定的属性。上述过程对于第二和第三种绑定方法也是大体一样的。 根据上述过程描述,我们也得出另一个结论:QML的属性绑定是单向的。所谓单向,是指 textField.text 的改变会引起button.text 的改变;但反过来则不会,因为并没有经过上述的构造过程。常见问题讨论下面就笔者在实际开发中遇到的几个问题展开讨论。 1. 我在QObject派生类中自定义了一个属性,然后在QML中绑定到了其他属性,为什么改变该属性,绑定不起作用?根据上节阐述的绑定原理,导致绑定不起作用的原因可能有两个:
注意:属性绑定的原动力是那个 NOTIFY 信号。其实不管属性值是否真的改变,只要你发射了该信号,属性绑定都会被重新计算一次(只不过如果值确实没变,每次计算结果也不变)。2. 我的属性绑定之前好好的,为什么忽然不起作用了?这也是很常见的错误,它往往是在js代码中直接对属性赋值导致的,例如下面的代码: TextField{id: textField} Button{ id: button text: textField.text } functionfunc(){ button.text="Hello"; } 正如前面冒号绑定里说的, button 的text 属性已经成功绑定到了textField 的text 属性上。但是一旦func 函数执行,button 的text 属性被直接赋值,实际上就和之前构造出来的槽函数(上节中的textBindingSlot )解了绑。如果直接赋值不可避免,又想在赋值之后重新绑定,那么可以用第三种绑定方法进行再绑定。或者一开始就用 Binding 来绑定,用它的when 属性来控制绑定是否起作用。3. 如何做双向绑定?前面我们提到了QML的属性绑定是单向的,但如果我们确实需要做双向绑定该怎么办? 对于都是Qt Quick自带类型,可以很简单,例如: TextField{ id: textField text: button.text } Button{ id: button text: textField.text } 也就是说,上面各自做一次属性绑定即可。 但如果是自定义属性,要特别注意 WRITE 部分要检查属性值是否真的被修改;只有真的被修改才往外发射NOTIFY 信号,否则很可能进入死循环。例如下面的WRITE 函数:voidsetText(QStringnewText){ // 如果没修改,则直接返回 if(text==newText) return; text=newText; emittextChanged(); } ------------------------------------------------------------------------- 我们尊重原创,也注重分享,如若侵权请联系qter@qter.org。 ------------------------------------------------------------------------- |