javascript脚本支持QT本身类的构造

 Qt 支持的脚本基于ECMAScript 脚本语言, 这个东西又是javascript, jscript 的基础. 所以, 一般只要学过javascript 就基本会写Qt 脚本了. 自此开始, Qt 脚本现在就叫javascript. 
    不过作为土人, javascript 中有一个prototype的概念, 现在才知道. javascript 本没有类的概念, 跟不用说是继承之类的了. 但是凭借prototype的特性, 我们可以实现类似C++中类, 以及类继承等一些特性. 
    prototype是个什么概念? 因为这个单词实在表意不清, 导致我花了很多时间来理解这个. 每个javascript对象都有一个指向另一个对象的引用, 这就是它的prototype. 一个对象的prototype定义了这个对象可以进行的操作集. 用C++来类比的话, 这些操作集是一定是成员函数. 看下面的javascript代码: 
function Shape(x, y) {
    this.x = x;
    this.y = y;
}
Shape.prototype.area = function() { return 0; }

function Circle(x, y, radius) {
    Shape.call(this, x, y);
    this.radius = radius;
}
Circle.prototype = new Shape;
Circle.prototype.area = function() {
    return Math.PI * this.radius * this.radius;
}
     我们把Circle对象的prototype设置成Shape对象, 实际上就是把Shape对象的prototype赋给了Circle对象, 让Circle对象的初始操作集跟Circle对象是一样的. 之后我们又重载了area()函数, 当然我们还可以加入新的函数. 它对应的C++代码如下: 
class Shape
{
public:
    Shape(double x, double y) {
        this->x = x;
        this->y = y;
    }
    virtual double area() const { return 0; }
    double x;
    double y;
};

class Circle : public Shape
{
public:
    Circle(double x, double y, double radius) : Shape(x, y)
    {
        this->radius = radius;
    }
    double area() const { return M_PI * radius * radius; }
    double radius;
};
     所以, 我们看到了, 对于一个javascript对象来说, 它还包括了一个内部的prototype对象. 对于Qt要用C++来实现类似prototype的功能的话, 除了要写一个javascript中的对应类, 还要写这个类对应的prototype类. 这个东西很高级, 也很麻烦, 所以建议看官方文档: http://doc.trolltech.com/4.3/qtscript.html#making-use-of-prototype-based-inheritance
     下面我们来说一下一般怎样从Qt的C++代码中调用Qt的script代码. 假设我们要写一个dialog, 上面有一个QPushButton, 一个QLineEdit. 点击QPushButton的时候, 会弹出一个QMessageBox来显示消息. 
    a) 直接写Qt 的C++代码的话, 只要用signal/slot 就行了:
void HelloDialog::helloClicked()
{
    QString text = textEdit->text();
    text = "Hello " + text;
    QMessageBox::information(this, "info", text); 
}
     b) 现在我们要加入javascript 的支持. 要解决的大概有这么一些问题: javascript 中怎么拿到QLineEdit 里的字符串? javascript 中怎么调用QMessage 这个Qt 的类? 我们还是先来看代码: 
QScriptValue constructQMessageBox(QScriptContext *, QScriptEngine *engine) {
    return engine->newQObject(new QMessageBox());
}

void HelloDialog::helloClicked()
{
    QString helloScript = scriptsDir.filePath("xxx.js");
    QFile file(helloScript);
    if (!file.open(QIODevice::ReadOnly)) {
        return;
    }
    QTextStream in(&file);
    in.setCodec("UTF-8");
    QString script = in.readAll();
    file.close();
    // section 1
    QScriptEngine engine;
    QScriptValue helloDialog = engine.newQObject(this);
    engine.globalObject().setProperty("helloDialog", helloDialog);
    // section 2
    QScriptValue constructor = engine.newFunction(constructQMessageBox);
    QScriptValue qsMetaObject = engine.newQMetaObject(&(QMessageBox::staticMetaObject), constructor);
    engine.globalObject().setProperty("QMessageBox", qsMetaObject);

    QScriptValue result = engine.evaluate(script);
}
     我们先把整个javascript 文件读进来, 加入一堆设置, 最后调用QScriptEngine::evaluate()函数来执行这段javascript. QScriptEngine这个类就相当于javascript 的解释器. 
    javascript 里没有类这个概念, 所有的变量都是var 类型. 如果要让Qt 的C++类在javascript 里运行, 那么先要将它包装(wrap)成一个javascript 的类型. 代码的section 1部分把this(即当前的dialog)先做了包装, 然后把包装后的对象加入到javascript 的运行环境的全局变量中. 
    接着来解决QMessageBox的问题. 由于javascript 中没有类, 继而也就是没构造函数这个概念, 但是当我们在javascript 中new 一个Qt C++对象的时候, 还是需要调用它的构造函数. 代码的section 2部分先把一个C++回调函数(只所以成为回调函数, 是因为要作为QScriptEngine::newFunction的参数, signature是固定的)包装成一个QScriptValue, 然后把它和QMessageBox 的meta-object 信息一起包装成一个QScriptValue, 最后依样画葫芦地加入到javascript 的运行环境的全局变量中. 这样我们就能在javascript 中new 出一个QMessageBox 了. 
   有一个很重要问题. 就是Qt 的meta-object 系统和javascript 的调用系统是有对应关系的. 在javascript 中, 一个var 如果是QObject 包装而来, 那么这个QObject 的所有property(Q_PROPERTY声明), signal/slot 都是可以在javascript 中调用的. 还有就是这个QObject 的所有child (指的是包含而不是继承关系), 也是可以直接访问的. 
    看一下javascript 代码. 其中greeting 和 text 都是属性:
function showMessage(parent, title, text)
{
    var messageBox = new QMessageBox;
    messageBox.windowTitle = title;
    messageBox.text = text;
    messageBox.icon = QMessageBox.Information;
    return messageBox.exec();
}

return showMessage(helloDialog, "info", helloDialog.greeting + " " + helloDialog.text);
     c) 我们实现了用javascript 来控制逻辑. GUI的话, Qt 也提供了一种可以直接读取.ui 的方法: QUiLoader::load()函数. 于是我们连GUI 也可以不用直接编译到binary 里去了. 我们要做的就是用Qt 的C++代码搭一个大概的框架, 加载需要的*.ui, *.js文件, 在适当的时候调用适当的javascript 函数就行了. 而且.ui 文件对于每个控件都会有一个objectName的属性, 用uic生成代码的话, 这个值就是变量名, 如果用QUiLoader::load()的话, 这个就被赋给了QObject 的objectName这个property. 当我们要在一个QWidget 的javascript 对象里引用它的子控件的时候, 便能直接用这个objectName 来引用. 于是*.ui 和*.js 文件可以说简直配合的天衣无缝那. 
    还是来看代码, Qt 的C++代码没什么好说的, 就看javascript 代码: 
function showMessage(parent, title, text)
{
    var messageBox = new QMessageBox;
    messageBox.windowTitle = title;
    messageBox.text = text;
    messageBox.icon = QMessageBox.Information;
    return messageBox.exec();
}

function doClick()
{
    var text = dialog.textEdit.text;
    showMessage(dialog, "info", "Hello " + text);
}

dialog.show();
dialog.helloButton.clicked.connect(doClick);
return 0;
     直接访问子控件是不是清爽多了? 呵呵. 代码见这里. 其它请参考官方文档:
        *) ECMAScript Reference
        *) QtScript Module
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值