防止执行“继承”信号处理程序

时间:2016-01-22 14:19:21

标签: qt signals qml handler qtquick2

在许多派生组件经常使用该功能时,在“基础”组件中定义信号处理程序非常有用。

但是,在QML中,在派生组件中安装新处理程序不会替换原始处理程序,它只是堆叠在它之上。由于处理程序并非每个信号都是唯一的,它们只是连接,每个信号可以有多个连接。

一种解决方案是简单地不在基本组件中提供默认处理程序,但是每次使用组件时都必须复制并粘贴处理程序。那么有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

正如Peppe所提到的,一种解决方案是直接安装处理程序,而只是让处理程序调用可以覆盖的函数。但是,当有意在派生组件中重用基本实现时,函数重写本身就是一个混合包,不一定在具有组件继承的顺序处理程序堆栈中。

我实际上提出了一个灵活的,虽然有点笨重的解决方案。它是手动断开先前安装的处理程序并手动连接新处理程序。这有两个含义:

  1. 处理程序不能是匿名表达式,它们必须作为函数实现,因此可以引用它们以断开连接。

  2. 不能使用声明性语法(onSignal: handler())绑定处理程序,因为它不连接到处理程序函数,而是连接到调用处理函数的匿名表达式。所以你不能断开连接。

  3. 所以它看起来像这样:

    //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提供了一种非常好的方式来制作一个“独特的”绑定,那将是很好的和有用的 - 在制作新的连接之前清除所有先前的连接。然后,不需要所有变通方法代码。

答案 1 :(得分:0)

鉴于在QML中覆盖函数时,the base implementation is no more available使得每个实现必须有一个不同的名称。

首先为插槽处理程序函数定义命名方案,让我们说 onSomethingHappened 执行 句柄 OnSomethingHappened ComponentA的实施是 句柄 OnSomethingHappened _ComponentA 。在handleOnSomethingHappened中有 superHandle OnSomethingHappened ,它执行基类'实施

通过这些命名约定,我们可以实现一系列不错的属性设置:

  1. 可能的多重继承
  2. 基类'可以选择在任何特定点调用实现
  3. 一个类必须只知道它的直接基类
  4. 来电者代码比实施更好
  5. 这很复杂但并不复杂(和Complex is better than complicated.
  6. 我猜它可以轻松编码代码
  7. 在第一个例子中,我们有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中看到的是

    1. 它在BaseMouseArea(光标形状)上添加了一些属性
    2. 它会覆盖super*方法的具体实现
    3. 实现其具体实现并调用父实现
    4. 此处提供了foll示例项目:https://github.com/webmaster128/derived-qml-slots