Win32下 Qt与Lua交互使用,四:在Lua脚本中自由执行Qt类中的函数

话接上篇。通过前几篇博客,我们实现在Lua脚本中执行Qt类中函数的方法,以及在Lua脚本中连接Qt对象的信号与槽。

但是,我们也能发现,如果希望在Lua脚本中执行Qt类的函数,就必须绑定一个真正实现功能的函数。如QWidget::show(),需要写一个在栈中取出widget指针,widget调用show()函数的方式。如果希望在Lua中调用大量函数,就需要编写大量的C++实现函数。有没有什么省时省力的好方法呢?

上一篇中我们实现了在Lua脚本中连接信号与槽。我们只是传过去了两个QObject的对象,和两个字符串的函数名。我们并没有具体实现那个函数,但是槽函数顺利执行了。这给了笔者启发。如果我传过去一个函数名,理论上我可以找一个信号连接并激发它,也就达到了执行的目的。

OK,我们写这样一个函数:

static int exec(lua_State *state)
{
    QObject* obj = (QObject* )tolua_tousertype(state, 1, 0);
    const char * slot = tolua_tostring(state, 2, 0);

    if(obj != NULL)
    {
        RunSLOT run;
        QObject::connect(&run, SIGNAL(sendSignal()),
                obj, QString("1%0()").arg(slot).toLocal8Bit().data());
    }

    return 1;
}

取得QObject对象,以及字符串的槽函数名称。然后我们新建了一个RunSLOT对象,连接了run和obj对象。这样就能执行字符串代表的函数?

继续看RunSLOT类:

#ifndef RUNSLOT_H
#define RUNSLOT_H

#include <QObject>

class RunSLOT : public QObject
{
    Q_OBJECT
public:
    RunSLOT(QObject *parent = 0);
    ~RunSLOT();

signals:
    void sendSignal();
};

#endif // RUNSLOT_H
#include "runslot.h"

RunSLOT::RunSLOT(QObject *parent) :
    QObject(parent)
{
}

RunSLOT::~RunSLOT()
{
    emit sendSignal();
}

在析构是发出sendSignal信号,激发槽函数。而RunSLOT类对象run在遇到‘}’自动析构(C++局部变量原则),也就间接的执行了槽函数。

在绑定函数时将exec绑定到类里,然后写个测试脚本看看:

widget = QWidget:new()
widget:exec("show")

button = QPushButton:new()
button:exec("show")

connect(button, "clicked()", widget, "hide()")

成功显示一个窗体,一个按钮。

完整main.cpp如下:

// own
#include "include/lua.hpp"
#include "qlua.h"
#include "runslot.h"

// qt
#include <QWidget>
#include <QApplication>
#include <QFile>
#include <QDebug>

static int connect(lua_State* state)
{
    QObject * a = (QObject*)tolua_tousertype(state, 1, 0);
    const char * signal = tolua_tostring(state, 2, 0);
    QObject * b = (QObject*)tolua_tousertype(state, 3, 0);
    const char * slot = tolua_tostring(state, 4, 0);

    QObject::connect(a, QString("2%0").arg(signal).toLocal8Bit().data(),
                     b, QString("1%0").arg(slot).toLocal8Bit().data());

    return 1;
}

static int exec(lua_State *state)
{
    QObject* obj = (QObject* )tolua_tousertype(state, 1, 0);
    const char * slot = tolua_tostring(state, 2, 0);

    if(obj != NULL)
    {
        RunSLOT run;
        QObject::connect(&run, SIGNAL(sendSignal()),
                obj, QString("1%0()").arg(slot).toLocal8Bit().data());
    }

    return 1;
}

static int QObject_delete(lua_State* state)
{
    qDebug() << "delete Start";
    QObject* obj = (QObject* )tolua_tousertype(state, 1, 0);
    if(NULL != obj)
    {
        qDebug() << "delete~";
        delete obj;
    }
    return 1;
}

static int QWidget_new(lua_State* state)
{
    QWidget* widget = new QWidget();
    tolua_pushusertype(state, widget, "QWidget");
    return 1;
}

static int QPushButton_new(lua_State* state)
{
    QPushButton* button = new QPushButton();
    tolua_pushusertype(state, button, "QPushButton");
    return 1;
}

static int QWidget_resize(lua_State* state)
{
    QWidget* widget = (QWidget* )tolua_tousertype(state, 1, 0);
    double a = tolua_tonumber(state, 2, 0);
    double b = tolua_tonumber(state, 3, 0);

    if(widget)
    {
        widget->resize((int)a, (int)b);
    }
    return 1;
}

int main(int argc, char * argv[])
{
    Q_INIT_RESOURCE(resources);
    QApplication a(argc, argv);

    QLua lua;

    lua.beginModule("");

    lua.addType("QWidget", QObject_delete);
    lua.moduleTypeFunction("new", QWidget_new);
    lua.moduleTypeFunction("resize", QWidget_resize);
    lua.moduleTypeFunction("exec", exec);
    lua.endModule();

    lua.addType("QPushButton", QObject_delete);
    lua.moduleTypeFunction("new", QPushButton_new);
    lua.moduleTypeFunction("exec", exec);
    lua.endModule();

    lua.endModule();

    lua.pushFunction("connect", connect);

    // 读取资源文件
    QFile file("../../LuaTest/test.lua");
    file.open(QIODevice::ReadOnly | QIODevice::Text);

    QTextStream in(&file);
    in.setCodec("UTF-8");

    // 执行
    lua.run(in.readAll());

    return a.exec();
}

我们可以在将绑定exec函数写在QLua类里,添加类时就绑定这个函数。

当然,这个方法的局限性在于执行的函数无参数,而且必须是槽函数。如果想自由一个,可以在调用时写上参数,参数类型,C++这边判断并选择不同的信号连接方式,一劳永逸。

通过这个例子,我们可以发现,Qt本身就具备由字符串执行函数的能力,按照我的见解,这就是一种半动态。笔者会继续做下去,因为这个真的很有趣。

完整例程代码下载:http://pan.baidu.com/s/1sjFDiRb

转载注明出处:http://www.cnblogs.com/IT-BOY/