在“基本”组件中定义信号处理程序时,如果许多派生组件会频繁使用该功能,那么该处理程序非常漂亮。防止“继承”信号处理程序执行
但是,在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