在「基本」組件中定義信號處理程序時,如果許多派生組件會頻繁使用該功能,那麼該處理程序非常漂亮。防止「繼承」信號處理程序執行
但是,在QML中,在派生組件中安裝新處理程序並不是替換原始處理程序,它僅僅堆疊在它上面。由於處理程序對於每個信號並不是唯一的,它們僅僅是連接,每個信號可以有多個連接。
一種解決方案是簡單地不在基本組件中提供默認處理程序,但是每次使用組件時都必須複製並粘貼處理程序。那麼有沒有更好的方法?
在「基本」組件中定義信號處理程序時,如果許多派生組件會頻繁使用該功能,那麼該處理程序非常漂亮。防止「繼承」信號處理程序執行
但是,在QML中,在派生組件中安裝新處理程序並不是替換原始處理程序,它僅僅堆疊在它上面。由於處理程序對於每個信號並不是唯一的,它們僅僅是連接,每個信號可以有多個連接。
一種解決方案是簡單地不在基本組件中提供默認處理程序,但是每次使用組件時都必須複製並粘貼處理程序。那麼有沒有更好的方法?
正如peppe所提到的,一種解決方案是不是直接安裝處理程序,而是讓處理程序調用一個可以被重寫的函數。然而,如果有意在派生組件中重用基本實現,而不一定在組件繼承的順序處理程序堆棧中,則覆蓋自身的函數是一個混雜的包。
我實際上想出了一個靈活的,雖然有點笨重的解決方案。它是手動斷開先前安裝的處理程序並手動連接一個新處理程序。這有兩個含義:
處理程序不能是匿名錶達式,它們必須作爲函數實現,以便它們可以被引用用於斷開連接。
無法使用聲明性語法()綁定處理程序,因爲它不會連接到處理程序函數,而是連接到調用處理程序函數的匿名錶達式。所以你不能斷開連接。
因此,它看起來是這樣的:
//BaseComp.qml
QtObject {
signal sig(int i)
function baseHandler(i) {...}
Component.onCompleted: sig.connect(baseHandler)
}
//DerivedComp.qml
BaseComp {
function derivedHandler(i) {...}
Component.onCompleted: {
sig.disconnect(baseHandler)
sig.connect(derivedHandler)
}
}
的基本模式是斷開其覆蓋它的每一個派生的組件前面的基本處理程序。這樣,如果需要這樣做,就可以從派生組件訪問基本處理程序,但如果只有一個重寫的處理函數,那麼基類實現將無法從派生類訪問,因爲如何實現重寫QML(將有兩個同名的函數作爲對象的成員,但它們都會引用派生的組件覆蓋)。
如果QML提供了一種非常有用的方法來創建一個「獨特」的綁定 - 在創建新的連接之前清除所有以前的連接,這將是很好的和有用的。然後,所有的解決方法代碼將不再需要。
鑑於在QML中重寫函數時,the base implementation is no more available使得每個實現必須有不同的名稱。
首先定義了時隙處理器功能的命名方案,讓我們說onSomethingHappened執行手柄 OnSomethingHappened。並且ComponentA
的實現是句柄發生了什麼事_ComponentA。在handleOnSomethingHappened
有superHandle發生了什麼事,它執行基類的實現。
有了這些命名約定就可以實現與一羣漂亮的屬性的設置:
在第一個例子,我們有3個按鈕,處理點擊,1.使用默認實現,2.使用自定義實現和3.通過自定義實現加基實現(在任何點):
BaseButton {
text: "Button 1"
}
BaseButton {
text: "Button 2"
handleOnClicked: function() {
console.log("Custom click handler")
}
}
BaseButton {
text: "Button 3"
handleOnClicked: function() {
console.log("Custom click handler")
superHandleOnClicked()
}
}
這可以通過定義BaseButton
這樣
Button {
property var handleOnClicked: superHandleOnClicked
// "super" from the instance's perspective. Use this in implementations of handleOnDoubleClicked
property var superHandleOnClicked: handleOnClicked_BaseButton
function handleOnClicked_BaseButton() {
console.log("BaseButton clicked.")
}
onClicked: handleOnClicked()
}
的做基本實現在功能superHandleOnClicked
中可用。
插槽參數
當使用參數,沒有什麼變化:
Rectangle {
width: 100
height: 40
color: "green"
BaseMouseArea {
}
}
Rectangle {
width: 100
height: 40
color: "green"
BaseMouseArea {
handleOnDoubleClicked: function(mouse) {
console.log("Custom click handler", mouse.x, mouse.y)
}
}
}
Rectangle {
width: 100
height: 40
color: "green"
BaseMouseArea {
handleOnDoubleClicked: function(mouse) {
console.log("Custom click handler", mouse.x, mouse.y)
superHandleOnDoubleClicked(mouse)
}
}
}
與BaseMouseArea定義爲
MouseArea {
anchors.fill: parent
property var handleOnDoubleClicked: superHandleOnDoubleClicked
// "super" from the instance's perspective. Use this in implementations of handleOnDoubleClicked
property var superHandleOnDoubleClicked: handleOnDoubleClicked_BaseMouseArea
function handleOnDoubleClicked_BaseMouseArea(mouse) {
console.log("BaseMouseArea clicked", mouse.x, mouse.y, ".")
}
onDoubleClicked: handleOnDoubleClicked(mouse)
}
多重繼承
現在我們instance
是一個PointerMouseArea
是一個BaseMouseArea
,用實例被定義爲
Rectangle {
width: 100
height: 40
color: "blue"
PointerMouseArea {
}
}
Rectangle {
width: 100
height: 40
color: "blue"
PointerMouseArea {
handleOnDoubleClicked: function(mouse) {
console.log("Don't tell father and grandfather", mouse.x, mouse.y)
}
}
}
Rectangle {
width: 100
height: 40
color: "blue"
PointerMouseArea {
handleOnDoubleClicked: function(mouse) {
console.log("Tell father and grandfather", mouse.x, mouse.y)
superHandleOnDoubleClicked(mouse)
}
}
}
這需要PointerMouseArea
定義如下:
BaseMouseArea {
cursorShape: Qt.PointingHandCursor
superHandleOnDoubleClicked: handleOnDoubleClicked_PointerMouseArea
function handleOnDoubleClicked_PointerMouseArea(mouse, superImplementation) {
console.log("Pointed at something") // I just add my comment and then call super
handleOnDoubleClicked_BaseMouseArea(mouse)
}
}
你PointerMouseArea看到的是
super*
方法,它的具體實施的FOLL示例項目,請訪問: https://github.com/webmaster128/derived-qml-slots
我們可以重寫函數,它在被覆蓋的函數中只是一個相當具有破壞性的過程。這種方法似乎幾乎是這個簡短的解決方案的擴展版本覆蓋:http://stackoverflow.com/questions/33875085/resolving-property-and-function-overrides-in-qml在一個側面說明,它會有如果QML能夠很好地支持這些簡單且廣泛使用的編程概念,而不必訴諸於更適合C日子的笨重手動解決方案,那麼我實際上使用手動生命週期管理,因爲cuz refcounting被竊聽了 – dtech
@ddriver謝謝爲提示。我修正了關於覆蓋函數的陳述。但後果不變。你的觀點正確無誤,但我們無法輕易改變。 –
那麼,你仍然可以在一個函數中定義一段代碼,並讓子類簡單地調用該函數。或者提供一些布爾屬性來防止超類處理程序做任何事情...... – peppe
不知何故,「漂亮」的想法聽起來不對。如果基類實現需要由基類發出的信號來保持一致的狀態,那麼應該不可能在派生的組件中重載它,因爲這將破壞基類的設計。但是,如果插槽不是基類所需要的,但偶爾會在派生組件中保存一些文本行,那麼在基類中進行連接聽起來像是一種糟糕的設計。我認爲,你應該真正考慮你的課程的封裝/設計。 – Jens
@Jens - 問題不在於組件設計,而在於QML的侷限性。覆蓋並不像預期的那樣工作,信號接口僅限於連接/斷開連接,後者需要表達參考才能工作。我不知道你認爲覆蓋基類實現「打破」的東西 - 它總是被大量使用。基本類型定義了接口和一些功能,派生類型基於此。在這種情況下,基本組件信號是接口的一部分,它提供了默認實現。 – dtech