大家新年好!這是豬年的第一篇。這裡我想給大家講講qt qml裡非常重要的乙個概念:屬性繫結(property binding)。現代化的開發語言、框架都講究自動化、智慧型化,在筆者看來,屬性繫結則是qml中這方面的代表。用好屬性繫結可以極大提高我們的開發效率。本文首先介紹何為qml屬性繫結,然後用通俗易懂的說法來闡釋其底層原理,最後和大家討論開發時幾個常見的問題。
在qml中實現屬性繫結有三種方法,每種方法都有其各自的優缺點。
這是最常見的繫結方法,在定義屬性時使用qml的冒號語法,所以筆者稱之為「冒號繫結」。
例如下面的qml**就實現了乙個屬性繫結:
textfield
button
上面第4行**將textfield
的text
屬性繫結到了button
的text
上。只要前者發生變化(例如使用者輸入、修改),按鈕上的文字就會跟著變動。
這種方法的優點是簡單方便,是三種方法中**量最少的。
缺點是缺乏彈性,控制能力小,主要有兩方面:
qml中專門提供了乙個型別binding
來實現屬性繫結。上面的例子如果改用biding來寫則**如下:
textfield
button
binding
這種方法的優點主要有兩個:
這兩個可以說完美解決了上面第一種屬性繫結的問題。
該方法有兩個缺點:
所以只在需要的時候用該方法。
這是最後一種屬性繫結方法。上面的例子改用該方法的話**如下:
textfield
button
component.oncompleted:);
}
該方法的好處是可以寫在任何js執行**裡。
缺點是只能執行時繫結。由於前兩種方法都是qml語法申明,qml執行引擎在初始化的時候有機會使用jit技術和cache技術進行優化,而動態執行的js語句是沒法進行這種優化的,因此這種方法的執行效率是三種方法中最低的。
在實際開發中,筆者發現很多人會用屬性繫結,但經常出錯,發生意外的解綁、迴圈繫結等問題。究其原因,往往是因為對qml屬性繫結的底層原理不甚清楚,一旦程式變複雜很容易糊塗。
所謂屬性繫結的原理,用直白點的話來說,就是:為什麼乙個屬性變化了,和它繫結的屬效能跟著變化?
我們還是用上節中冒號繫結的例子。當我們寫下text: textfield.text
這行**的時候,qml引擎實際上做了下面這些事情:
1.構造乙個槽函式。 該槽函式執行時會幹兩件事情:
計算冒號右邊的表示式的值。
將該值通過呼叫左邊屬性的write
方法賦值。
假設button
的text
屬性的write
方法為settext
,用c++的形式描述這個槽函式大概是下面這樣子:
public slots:
void textbindingslot()();");
settext(newtext);
}
2.掃瞄冒號右邊的表示式,找到所有具有notify
訊號的屬性。 所謂notify
訊號是指qt定義屬性時候的notify
字段,具體可以參看文件qt property system。在這個例子中,textfield.text
是乙個具有notify
訊號的屬性,也就是說它被修改後發射notify
訊號。這裡假設該訊號為textchanged()
。
3.將這些屬性的notify
訊號和上面的槽函式連線。 使用connect
連線:connect(textfield, signal(textchanged()), button, slot(textbindingslot));
所以當任何冒號左邊表示式裡的具有notify
訊號屬性值改變時,相關訊號發射,然後構造的槽函式得到執行,計算出新的值,最後將該值賦給被繫結的屬性。
上述過程對於第二和第三種繫結方法也是大體一樣的。
根據上述過程描述,我們也得出另乙個結論:qml的屬性繫結是單向的。所謂單向,是指textfield.text
的改變會引起button.text
的改變;但反過來則不會,因為並沒有經過上述的構造過程。
下面就筆者在實際開發中遇到的幾個問題展開討論。
根據上節闡述的繫結原理,導致繫結不起作用的原因可能有兩個:
仔細檢視屬性定義,是否新增了notify
字段?所有屬性繫結執行、推演的原動力都是這個notify
訊號,如果你的自定義屬性沒有定義該訊號,那屬性繫結很多不會起作用。
如果你的屬性值是在c++中被修改,那是否發**notify
指定的訊號?有很多人定義了notify
訊號,但是在修改屬性值的時候,卻忘記發射這個訊號。這個是非常常見的錯誤。
注意:屬性繫結的原動力是那個notify
訊號。其實不管屬性值是否真的改變,只要你發**該訊號,屬性繫結都會被重新計算一次(只不過如果值確實沒變,每次計算結果也不變)。
這也是很常見的錯誤,它往往是在js**中直接對屬性賦值導致的,例如下面的**:
textfield
button
function func()
正如前面冒號繫結裡說的,button
的text
屬性已經成功繫結到了textfield
的text
屬性上。但是一旦func
函式執行,button
的text
屬性被直接賦值,實際上就和之前構造出來的槽函式(上節中的textbindingslot
)解了綁。
如果直接賦值不可避免,又想在賦值之後重新繫結,那麼可以用第三種繫結方法進行再繫結。或者一開始就用binding
來繫結,用它的when
屬性來控制繫結是否起作用。
前面我們提到了qml的屬性繫結是單向的,但如果我們確實需要做雙向繫結該怎麼辦?
對於都是qt quick自帶型別,可以很簡單,例如:
textfield
button
也就是說,上面各自做一次屬性繫結即可。
但如果是自定義屬性,要特別注意write
部分要檢查屬性值是否真的被修改;只有真的被修改才往外發射notify
訊號,否則很可能進入死迴圈。例如下面的write
函式:
void settext(qstring newtext)
VUE關於物件動態新增屬性無法雙向繫結問題
在專案中遇到的問題,因為物件屬性不固定,需要到資料庫中讀取,然後動態的給物件新增屬性,在新增屬性的過程中發現新增的屬性在雙向繫結時不能生效,房頂方法有三種,如下圖 上邊有三種給物件新增屬性並賦值的方法,只有第一種可以實現值的雙向繫結,但是跟需求不符合,需要新增的屬性不是固定的,後來採用的方法是建立乙...
1120 MVVM框架是如何實現雙向資料繫結剖析。
思路 1.model影響檢視 編譯時註冊watcher,在註冊watcher,呼叫get,通過observer資料劫持get方法,將多個觀察者統一管理起來。當改變資料時,呼叫set方法,將收攏的對應觀察者的upadte方法更新。2.檢視影響model 編譯時註冊wather,node節點繫結對應的事...
Vue3 0為什麼使用Proxy實現雙向繫結
object.defineproperty只能劫持物件的屬性,而proxy是直接 物件 由於object.defineproperty只能對屬性進行劫持,需要遍歷物件的每個屬性。而proxy可以直接 物件。object.defineproperty對新增屬性需要手動進行observe,由於objec...