众所周知,在JavaScript中,所有函数都可以采用任意数量的参数。 在Qt中,您可以使QObject(其方法可以从QML访问),并用Q_INVOKABLE进行标记。例如:
class myObj: public QObject
{
Q_OBJECT
//...
public slots:
Q_INVOKABLE QJSValue myFunction(QJSValue value);
//...
};
然后您可以从JS调用它:
(function(){
//like this:
var result = myObj.myFunction("test");
//but also like this:
var result2 = myObj.myFunction(1,2,3,4,5); //,6,7,8, ..., 9998, 9999
})();
那么,如何在C ++端处理可变数量的参数?在JS方面,我们有“ arguments”对象。有Q_INVOKABLE方法的模拟物吗?
答案 0 :(得分:1)
用正常的方式似乎是不可能的。现在,我已经解决了。 QJSValue可以包含任何JS类型。包括数组。所以方法看起来像这样:
QJSValue myObject::test(QJSValue arg1, QJSValue arg2, QJSValue rest)
{
qDebug() << "arg1 = " << arg1.toString();
qDebug() << "arg2 = " << arg2.toString();
auto args = toJSValueList(rest);
qDebug() << "args = [";
for(auto arg : args) {
qDebug() << " " << arg.toString() << ",";
}
qDebug() << "]";
return (arg1.isUndefined() ? 0 : 1) + (arg2.isUndefined() ? 0 : 1) + args.length();
}
参数arg1和arg2是必需的。其余参数可以作为第三个参数(数组)传递给方法。 toJSValueList方法只是将包含数组的QJSValue转换为QJSValueList的助手。
QJSValueList myObject::toJSValueList(QJSValue arg)
{
QJSValueList list;
auto length = arg.property("length");
if(length.isNumber()){
for(int i = 0, intLength = length.toInt(); i < intLength; ++i){
list << arg.property(static_cast<quint32>(i));
}
} else if(!arg.isUndefined()){
list << arg;
}
return list;
}
这足以使事情正常进行。但是,如果实际上有人需要将任意数量的参数直接传递给函数(而不是通过数组),则可以稍加修改即可。
class myObject: public QObject, public QmlSingletonProvider<QmlTimer>
{
Q_OBJECT
public:
//...
// declare property of type QJSValue which will actually be a function
Q_PROPERTY(QJSValue variadic_test READ variadic_test)
public slots:
Q_INVOKABLE QJSValue test(QJSValue arg1, QJSValue arg2, QJSValue rest);
private:
QJSValueList toJSValueList(QJSValue arg);
QJSValue variadic_test(); // getter for property
QJSValue variadic_test_fn; // stored JS function
};
// variadic wrapper for method test()
QJSValue myObject::variadic_test()
{
if(variadic_test_fn.isCallable())
return variadic_test_fn;
auto engine = qjsEngine(this);
if(!engine) return QJSValue();
variadic_test_fn = engine->evaluate("function(){ return this.test( arguments[0], arguments[1], Array.prototype.slice.call( arguments, 2 ) ); }");
return variadic_test_fn;
}
QJSValue也可以是一个函数。因此,您可以添加具有QJSValue类型的只读Q_PROPERTY,并在首次访问属性时为其分配JS函数。该函数将是一个包装器,该包装器仅将其所有参数收集到数组并将其传递给实际方法。在JS中,您可以看到并调用这两个函数:实际的一个和可变参数的包装器。在该示例中,我按原样传递了两个参数(如果参数数目小于2,则将传递“ undefined”。),因为方法“ test”等待至少2个参数。其余参数通过“ Array.prototype.slice.call(arguments,2)”收集到数组。如果没有必需的参数,则可以使用“ function(){return this.test(Array.prototype.slice.call(arguments));}”。 当然,您的C ++方法应该准备好可能的“未定义”值。并且每个参数的类型需要手动检查。即使它是用C ++编写的,它看起来也更像是“ JS代码”。
答案 1 :(得分:1)
对于每个QObject来说似乎都是不可能的,但是您可以使用自定义JS脚本包装您的单例C ++后端SLOT,该脚本将多个参数结合在一起形成单个数组。这可以通过here方法回调来完成,该回调提供对QJSEngine实例的访问。
QJSValue ApiProvider::initSingletonType(QQmlEngine *qmlEngine, QJSEngine *jsEngine) {
qDebug() << "ApiProvider initSingletonType";
Q_UNUSED(qmlEngine);
QJSValue instance=jsEngine->newQObject(new ApiProvider(jsEngine));
QJSValue restProvider=jsEngine->evaluate(
"(function (instance) {" \
" instance.createElement=function(type,...child){" \
" return instance.createElementInternal(type,child);" \
" }; " \
" return instance;" \
"})"
);
if (restProvider.isError()) {
qCritical() << restProvider.toString();
return QJSValue();
} else {
QJSValue result=restProvider.call({instance});
if (result.isError()) {
qCritical() << result.toString();
return QJSValue();
} else {
return instance;
}
}
}
...
qmlRegisterSingletonType(
"com.tripolskypetr.quitejs",
1, 0,
"Api",
ApiProvider::initSingletonType
);
此qmlRegisterSingletonType实现了条件jsx工厂,其中每个元素的rest参数中可以具有任意数量的后代。源代码已准备就绪。
答案 2 :(得分:0)
您可以将QList<QVariant>
作为C ++ Q_INVOKABLE
函数的参数,并在qml端传递任何数组,例如["string", 1, 2, 3]
。 QVariant
可转换为大多数基本数据类型。