将信号连接到 JavaScript 中的内部处理程序

Connect signal to intrinsic handler in JavaScript

MenuItem为例,通常在QML中,为triggered信号指定handler很简单:

MenuItem {
    onTriggered: {
        console.log("Hey");
    }
}

现在如果我想做同样的事情,但不是动态创建的 MenuItem,例如通过 Menu.addItem(),那么连接和指定信号处理程序的语法是什么?

我没想到这会奏效,但这是一个可行的解决方案:

function onTriggered() {
    console.log("Hey");
}

var newItem = myMenu.addItem("Item 1");
newItem.triggered.connect(onTriggered);

但是有没有更好的方法呢?上面我定义了一个自定义函数,刚好名字是onTriggered,但是它可以随便命名,对吧?所以这段代码没有使用内置处理程序,这就是为什么我想知道是否有更简洁的解决方案?

更重要的是,后来我注意到这种方法的更多问题:在 for 循环中,如果处理程序使用了一个临时变量,事情就不再起作用了:

for (var i = 0; i < myArray.length; i ++) {
    var info = myArray[i];
    var newItem = myMenu.addItem("Item " + i);
    newItem.triggered.connect(function() {
        console.log(info);
    });
}

在这里您会看到控制台在触发时为所有添加的菜单项打印 last info in myArray。如何为每个单独的菜单项正确设置独立的处理程序?

除了评论,还可以轻松搞定"easier":

Menu {
    id: myMenu

    function add(text, handler) {
        var newItem = addItem(text)
        newItem.triggered.connect(handler)
    }
}

到此为止,问题已解决,现在您只需 myMeny.add("Item 1", onTriggered)

至于你在循环和仿函数中得到的结果,那是因为JS的作用域规则。查看链接的答案以了解如何解决该问题的详细信息。

So this code piece doesn't make use of the built-in handler

不要把onSignal当成一个处理程序,它只是一个挂接处理程序的钩子。将其视为声明性连接语法。当然,您也可以在声明中使用 Connection 元素,但只有在实际情况需要时才有意义。

我认为这种混淆源于其他一些确实为您生成处理程序方法的语言/框架。 onSignal 不同于 function onSignal() { expression } - 后者是处理函数,前者是处理程序挂钩,它只是将信号连接到绑定 expression.eval()。 Qt 文档也将 onSignal 称为处理程序,IMO 在技术上和概念上都是错误的,因为处理程序是要执行的代码,处理程序是您绑定到 onSignal.[=27= 的任何内容]

所以您可以高枕无忧,您担心的代码不会导致任何形式的冗余或低效,也不会留下任何未使用的东西,实际上是在 QML 中做事的正确方法。

综上所述,您可以 "built in handlers",但这是完全不同的事情:

// SomeItem.qml
Item {
    signal someSignal
    onSomeSignal: console.log("I am a built in handler")
}

// main.qml
SomeItem {
    onSomeSignal: console.log("I am another handler")
    Component.onCompleted: {
        someSignal.connect(function(){console.log("Yet another handler")})
        someSignal()
    }
}

并且控制台中的输出将显示:

qml: I am a built in handler
qml: I am another handler
qml: Yet another handler

如您所见,它实际上不是一个处理程序,而是一个连接挂钩。没有阴影,没有 "replacing / not using the built in handler",只有一个带有 3 个连接的信号来评估三个表达式。

signal.connect() 与命名函数一起使用确实有一个优势,如果您需要删除内置或其他处理程序,您可以稍后 signal.disconnect(namedFunction) 。我不确定如果您使用 onSignal: expr 是否可以这样做,因为您没有办法引用该匿名表达式。请注意,如果您使用 onSignal: namedFunction() 这将不起作用,您将无法 signal.disconnect(namedFunction) 因为信号没有直接连接到该函数,而是连接到调用它的匿名表达式。