qt scritable object

来源:互联网 发布:淘宝外卖和饿了么合并 编辑:程序博客网 时间:2024/06/05 23:07
 qt scritable object 2011-04-27 17:10:02

分类: WINDOWS

qt script 管理qobject的模式。
http://doc.trolltech.com/4.7/scripting.html

在qt的脚本环境下,gc将自动回收哪些没有ref的对象。

但是qt如何回收那些底下的c++ qobject对象,当它们的js wrapper 对象被回收时候。

qt提供的模型是你在创建一个scriptble对象时候,你可以指定3种模型。

QScriptEngine::newQObject(). 第二个参数...

了解了这个细节,有助于在开发时候,避免出现应该销毁c++ 对象时候没有;或者在访问时候发现c++对象已经销毁了。

联想到去年做v8的api wrapper layer时候,在mapping模式下当时只是支持全局型c++对象mapping过去。 就是为了避免引入ref counting 等对象生命期维护问题。


1. qt 控制型
 
    script enviroment 不管理, 完全由qt 的object tree mode管理。

   这种情况特别适合c++端将一些全局型或者可以确认对象生命周期肯定长于script evn周期的,比如一个联系人管理对象的生命周期要长于一个聊天对话框.html page的js 执行环境。

2. script env 控制型

     QScriptEngine::ScriptOwnership

  script evn 将完全掌控c++ 对象的生命周期, 比如gc认为没有任何ref 2 this 对象时候; 这个模式特别适合那些本来就创建于或者为script evn创建的c++对象,比如:

1
2
3
4
QScriptValue myQObjectConstructor(QScriptContext *context, QScriptEngine *engine)
 {
   // let the engine manage the new object's lifetime.
   returnengine->newQObject(newMyQObject(), QScriptEngine::ScriptOwnership); 
} 


3. auto 模式

    这个模式的含义是如果c++对象没有parent时候那么相当于script env ownership模式;否则就是qt ownership模式。



异常情况说明

假如有代码不管这个模式设置状况,在某个地点删除了c++对象,将引起?

1. script 端访问这个wrapper对象时候将触发exception
2. script 检查包装对象QScriptValue::isQObject() 返回true,但是 QScriptValue::toQObject() 返回null 




控制对象访问

QScriptEngine::newQObject() 的第三个参数可以设定那些访问对脚本过滤。


script 端直接创建c++对象

目前qt 的meta object还不支持动态绑定构造函数。

QScriptEngine::scriptValueFromQMetaObject().
You can also use QScriptEngine::newFunction() to wrap your own factory function, and add it to the script environment; 

可以达到类似的目的。



类型专函问题,我记得去年我和刚刚搞v8 api layer的时候,写了很多模板,大致干的也是这个事情。

Default Conversion from Qt Script to C++

The following table describes the default conversion from a QScriptValue to a C++ type.

C++ TypeDefault Conversion

bool

QScriptValue::toBool()

int

QScriptValue::toInt32()

uint

QScriptValue::toUInt32()

float

float(QScriptValue::toNumber())

double

QScriptValue::toNumber()

short

short(QScriptValue::toInt32())

ushort

QScriptValue::toUInt16()

char

char(QScriptValue::toInt32())

uchar

unsigned char(QScriptValue::toInt32())

qlonglong

qlonglong(QScriptValue::toInteger())

qulonglong

qulonglong(QScriptValue::toInteger())

QString

An empty string if the QScriptValue is null or undefined; QScriptValue::toString() otherwise.

QDateTime

QScriptValue::toDateTime()

QDate

QScriptValue::toDateTime().date()

QRegExp

QScriptValue::toRegExp()

QObject*

QScriptValue::toQObject()

QWidget*

QScriptValue::toQObject()

QVariant

QScriptValue::toVariant()

QChar

If the QScriptValue is a string, the result is the first character of the string, or a nullQChar if the string is empty; otherwise, the result is a QChar constructed from the unicode obtained by converting the QScriptValue to a ushort.

QStringList

If the QScriptValue is an array, the result is a QStringList constructed from the result of QScriptValue::toString() for each array element; otherwise, the result is an emptyQStringList.

QVariantList

If the QScriptValue is an array, the result is a QVariantList constructed from the result of QScriptValue::toVariant() for each array element; otherwise, the result is an emptyQVariantList.

QVariantMap

If the QScriptValue is an object, the result is a QVariantMap with a (key, value) pair of the form (propertyName, propertyValue.toVariant()) for each property, usingQScriptValueIterator to iterate over the object's properties.

QObjectList

If the QScriptValue is an array, the result is a QObjectList constructed from the result of QScriptValue::toQObject() for each array element; otherwise, the result is an emptyQObjectList.

QList<int>

If the QScriptValue is an array, the result is a QList<int> constructed from the result of QScriptValue::toInt32() for each array element; otherwise, the result is an emptyQList<int>.



    

Default Conversion from C++ to Qt Script

The following table describes the default behavior when a QScriptValue is constructed from a C++ type:

C++ TypeDefault Construction

void

QScriptEngine::undefinedValue()

bool

QScriptValue(engine, value)

int

QScriptValue(engine, value)

uint

QScriptValue(engine, value)

float

QScriptValue(engine, value)

double

QScriptValue(engine, value)

short

QScriptValue(engine, value)

ushort

QScriptValue(engine, value)

char

QScriptValue(engine, value)

uchar

QScriptValue(engine, value)

QString

QScriptValue(engine, value)

qlonglong

QScriptValue(engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit integers can be represented using the qsreal type.

qulonglong

QScriptValue(engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit unsigned integers can be represented using the qsreal type.

QChar

QScriptValue(this, value.unicode())

QDateTime

QScriptEngine::newDate(value)

QDate

QScriptEngine::newDate(value)

QRegExp

QScriptEngine::newRegExp(value)

QObject*

QScriptEngine::newQObject(value)

QWidget*

QScriptEngine::newQObject(value)

QVariant

QScriptEngine::newVariant(value)

QStringList

A new script array (created with QScriptEngine::newArray()), whose elements are created using the QScriptValue(QScriptEngine *, QString) constructor for each element of the list.

QVariantList

A new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newVariant() for each element of the list.

QVariantMap

A new script object (created with QScriptEngine::newObject()), whose properties are initialized according to the (key, value) pairs of the map.

QObjectList

A new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newQObject() for each element of the list.

QList<int>

A new script array (created with QScriptEngine::newArray()), whose elements are created using the QScriptValue(QScriptEngine *, int) constructor for each element of the list.

Other types (including custom types) will be wrapped using QScriptEngine::newVariant(). For null pointers of any type, the result is QScriptEngine::nullValue().

下面看几种特别的类型

1. 返回对象 指针

     记住,qt只能识别qobject* qwidget* 这两个指针, 如果你给他传递其他指针,它是不能识别的。

因此建议在函数的signature 上仅仅使用 qobject* , qwidget* 作为接口指针。  

或者使用自定义类型转换注册qScriptRegisterMetaType() .



2. script function object
   脚本中的函数对象非常不同于c++的函数,它是个一级对象,可以有自己的属性,也可以作为一个对象传递和存储; 而且可以匿名。

 因此了解函数对象的呼叫,定义在c++与js 非常有意义。

The following script defines a Qt Script object that has a toKelvin() function:

1
2
3
({ unitName: "Celsius",
   toKelvin: function(x) { returnx + 273; }
 })

obtained and called from C++:

1
2
3
4
QScriptValue object = engine.evaluate("({ unitName: 'Celsius', toKelvin: function(x) { return x + 273; } })");
QScriptValue toKelvin = object.property("toKelvin");
QScriptValue result = toKelvin.call(object, QScriptValueList() << 100);
qDebug() << result.toNumber(); // 373

global function add():

1
2
3
function add(a, b) {
    returna + b;
}

C++ code might call the add() function as follows:

1
2
QScriptValue add = engine.globalObject().property("add");
qDebug() << add.call(QScriptValue(), QScriptValueList() << 1 << 2).toNumber(); // 3

call 这个方法的第一个参数是this指针,表达wrappered js object; 如果非法或者null表示采用全局空间。


在js环境中理解this。。。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var getProperty = function(name) { returnthis[name]; };
 
name = "Global Object";// creates a global variable
print(getProperty("name"));// "Global Object"
 
var myObject = { name: 'My Object' };
print(getProperty.call(myObject,"name"));// "My Object"
 
myObject.getProperty = getProperty;
print(myObject.getProperty("name"));// "My Object"
 
getProperty.name = "The getProperty() function";
getProperty.getProperty = getProperty;
getProperty.getProperty("name");// "The getProperty() function"
 
在js中,这个this,一直到执行时候才evaluate , 不同于c++。 它的this并不和环境固定绑定,比如:
1
2
var o = { a: 1, b: 2, sum: function() { returna + b; } };
print(o.sum());// reference error, or sum of global variables a and b!!
这基本上是c++ java程序员非常容易犯的错误。

将c++的函数包装为可让js访问。。。

1
2
3
4
5
QScriptValue getProperty(QScriptContext *ctx, QScriptEngine *eng)
 {
     QString name = ctx->argument(0).toString();
     returnctx->thisObject().property(name);
 }

Call QScriptEngine::newFunction() to wrap the function. 


这样就可以创建一个wrapper 内部hold一个c++的函数,而可以被js使用



如何编码实现。。。?

Making a C++ object available to Scripts Written in QtScript


让c++的对象可以被script访问时意见很有趣和重要的事情,因为c++标准并不提供这种能力。

js是一种动态语言,要求能够运行时获取对象的信息,比如函数名,signature,属性等。

而这恰恰是c++标准没有定义的。



qt 扩展了c++的能力,这就是一点。

meta object system。


qtscript 就是使用了这个系统来支持动态访问c++对象。



1. 让class scritble

必须derived from qobject 这样script系统就可以动态获取class name,functionname,

 signature,而有了这些信息,就可以支持动态访问。 当然q_OBJECT 需要定义。

2. 让成员函数scritble

      默认的只要是qobject的subclas,它的public的slot就是脚本可见的。注意是public


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classMyObject : publicQObject
{
    Q_OBJECT
 
public:
    MyObject( ... );
 
    voidaNonScriptableFunction();
 
publicslots: // these functions (slots) will be available in QtScript
    voidcalculate( ... );
    voidsetEnabled( boolenabled );
    boolisEnabled() const;
 
private:
   ....
 
};


其实普通成员函数也可以很简单的scritble。。

比如上面例子中的anonscritablefunction 是脚本不可见的,但是只要 。。。

1
2
3
4
5
6
7
8
9
10
classMyObject : publicQObject
 {
     Q_OBJECT
 
     public:
     Q_INVOKABLEvoidthisMethodIsInvokableInQtScript();
     voidthisMethodIsNotInvokableInQtScript();
 
     ...
 };


就可以脚本可见的。。。




让属性scritable。。。

比如:

1
2
3
var obj = newMyObject;
obj.enabled = true;
print("obj is enabled: " + obj.enabled );


1
2
3
4
5
classMyObject : publicQObject
 {
     Q_OBJECT
     // define the enabled property
     Q_PROPERTY(boolenabled WRITE setEnabled READ isEnabled )



如果你不原意让你的属性可悲脚本访问,则可以

1
Q_PROPERTY(intnonScriptableProperty READ foo WRITE bar SCRIPTABLE false)


signal scriptable..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
classMyObject : publicQObject
 {
     Q_OBJECT
     // define the enabled property
     Q_PROPERTY(boolenabled WRITE setEnabled READ isEnabled )
 
 public:
     MyObject( ... );
 
     voidaNonScriptableFunction();
 
 publicslots: // these functions (slots) will be available in QtScript
     voidcalculate( ... );
     voidsetEnabled( boolenabled );
     boolisEnabled() const;
 
 signals:// the signals
     voidenabledChanged( boolnewState );
 
 private:
    ....
 
 };
1
2
3
4
5
6
7
8
9
10
11
12
13
function enabledChangedHandler( b )
{
    print("state changed to: " + b );
}
 
function init()
{
    var obj = newMyObject();
    // connect a script function to the signal
    obj["enabledChanged(bool)"].connect(enabledChangedHandler);
    obj.enabled = true;
    print("obj is enabled: " + obj.enabled );
}



这样如何在c++端实现class,memfunc,slot,property,signal sctibable就ok了。





记得去年做demo时候,很多时候会接触到v8::context 这样一个东西, 比如注入的函数等都需要

通过这个对象,这个对象代表了一个js执行上下文,所以的变量等都出在这个上下文中。。


而qt port中存在一个

QScriptContext 




  • Get the arguments that were passed to the function.
  • Get the this object.
  • Find out whether the function was called with the new operator (the significance of this will be explained later).
  • Throw a script error.
  • Get the function object that's being invoked.
  • Get the activation object (the object used to hold local variables).



参数需要注意两件事情

1. 任何脚本可call函数,它的参数数量可以是任意的。也就是说检查函数参数数量是函数的事情。

2. 参数可以被任何一个类型的输入apply, 也就是说类型检查是函数的事情。

qtscript 不对上述两点进行强制。



我们看个例子,就知道为何js 和 c++对参数都无所谓了。。

1
2
3
function add(a, b) {
     returna + b;
 }

1
2
3
4
5
6
7
8
9
function add() {
    if(arguments.length != 2)
        throwError("add() takes exactly two arguments");
    if(typeof arguments[0] != "number")
        throwTypeError("add(): first argument is not a number");
    if(typeof arguments[1] != "number")
        throwTypeError("add(): second argument is not a number");
    returnarguments[0] + arguments[1];
}
1
2
3
function add() {
    returnarguments[0] + arguments[1];
}
1
2
3
4
5
function add() {
     if(arguments.length != 2)
         throwError("add() takes exactly two arguments");
     returnarguments[0] + arguments[1];
 }
1
2
3
4
5
6
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
    doublea = ctx->argument(0).toNumber();
    doubleb = ctx->argument(1).toNumber();
    returna + b;
}

1
2
3
4
5
6
7
8
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
 {
     if(ctx->argumentCount() != 2)
         returnctx->throwError("add() takes exactly two arguments");
     doublea = ctx->argument(0).toNumber();
     doubleb = ctx->argument(1).toNumber();
     returna + b;
 }

1
2
3
4
5
6
7
8
9
10
11
12
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
    if(ctx->argumentCount() != 2)
        returnctx->throwError("add() takes exactly two arguments");
    if(!ctx->argument(0).isNumber())
        returnctx->throwError(QScriptContext::TypeError, "add(): first argument is not a number");
    if(!ctx->argument(1).isNumber())
        returnctx->throwError(QScriptContext::TypeError, "add(): second argument is not a number");
    doublea = ctx->argument(0).toNumber();
    doubleb = ctx->argument(1).toNumber();
    returna + b;
}