话接上文。笔者为了方便使用Lua,自己编写了一个Lua的类。主要代码如下:
QLua.h
1 #ifndef QLUA_H
2 #define QLUA_H
3
4 // own
5 #include "include/lua.hpp"
6
7 // qt
8 #include <QObject>
9 #include <QFile>
10 #include <QDebug>
11
12 #include <QWidget>
13 #include <QLineEdit>
14 #include <QPushButton>
15 #include <QMessageBox>
16
17 class QLua : public QObject
18 {
19 Q_OBJECT
20 public:
21 QLua(QObject *parent = 0);
22 ~QLua();
23
24 // lua
25 void init();
26 void close();
27
28 void pushFunction(QString funcName, lua_CFunction func);
29
30 void beginModule(QString);
31 void addType(QString, lua_CFunction deleteFunc);
32 void moduleTypeFunction(QString, lua_CFunction);
33 void endModule();
34
35 void run(QString);
36 signals:
37
38 public slots:
39
40 private:
41 lua_State *luaState;
42
43 bool isStartModule;
44 };
45
46 #endif // QLUA_H
QLua.cpp
1 #include "qlua.h"
2
3 QLua::QLua(QObject *parent) :
4 QObject(parent)
5 , luaState(NULL)
6 , isStartModule(false)
7 {
8 init();
9 }
10
11 QLua::~QLua()
12 {
13 close();
14 }
15
16 void QLua::init()
17 {
18 luaState = luaL_newstate();
19 luaL_openlibs(luaState);
20 }
21
22 void QLua::close()
23 {
24 if(luaState != NULL)
25 {
26 lua_close(luaState);
27 luaState = NULL;
28 }
29 }
30
31 void QLua::pushFunction(QString funcName, lua_CFunction func)
32 {
33 if (funcName.isEmpty()) return;
34 if (func == NULL) return;
35
36 lua_pushcfunction(luaState, func);
37 lua_setglobal(luaState, funcName.toLocal8Bit().data());
38 }
39
40 void QLua::beginModule(QString name)
41 {
42 if(luaState == NULL) return;
43
44 if (isStartModule == false)
45 {
46 tolua_open(luaState);
47 tolua_module(luaState, NULL, 0);
48 isStartModule = true;
49 }
50
51 const char *str = name.isEmpty()? NULL : name.toLocal8Bit().data();
52 tolua_beginmodule(luaState, str);
53 }
54
55 void QLua::addType(QString name, lua_CFunction deleteFunc)
56 {
57 if (luaState == NULL) return;
58 if (name.isEmpty()) return;
59 if (deleteFunc == NULL) return;
60
61 tolua_usertype(luaState, name.toLocal8Bit().data());
62 const char *str = name.toLocal8Bit().data();
63 tolua_cclass(luaState, str, str, "", deleteFunc);
64 beginModule(name);
65 }
66
67 void QLua::moduleTypeFunction(QString name, lua_CFunction func)
68 {
69 if(luaState == NULL) return ;
70 if (name.isEmpty()) return;
71
72 const char *str = name.toLocal8Bit().data();
73 tolua_function(luaState, str, func);
74 }
75
76 void QLua::endModule()
77 {
78 tolua_endmodule(luaState);
79 }
80
81 void QLua::run(QString str)
82 {
83 luaL_loadbuffer(luaState, str.toLocal8Bit().data(), str.length(), "line");
84 lua_pcall(luaState, 0, 0, 0);
85 }
QLua类可以方便的实现一些简单的Lua操作,如初始化,关闭,运行Lua代码,绑定函数等。
笔者目前想做到的是能在Lua代码中自有的生成Qt对象,然后能连接Qt原生对象的信号与槽。那么如何实现呢?
Qt中连接信号与槽的函数是QObject::connect(QObject * a, SIGNAL(), QObject * b, SLOT())。首先,我们要弄清楚SIGNAL和SLOT到底是什么。
从connect的参数列表中,我们可以很清晰的看到SIGNAL和SLOT的结果都是char*类型的字符串。我们可以直接在SIGNAL上点右键,转到SIGNAL的定义。或者做个简单的实验。测试如下Qt代码:
qDebug() << QString::fromLocal8Bit(SIGNAL(clicked()));
qDebug() << QString::fromLocal8Bit(SLOT(close()));
得到的结果很有意思:
"2clicked()" "1close()"
简单总结就是我们只要传入函数名的字符串,加上前缀即可。SIGNAL的前缀是2,SLOT的前缀是1。
这样我们就可以实现在Lua中的connect函数了。仍在使用C++编写,然后绑定到Lua里。如下:
1 static int connect(lua_State* state)
2 {
3 QObject * a = (QObject*)tolua_tousertype(state, 1, 0);
4 const char * signal = tolua_tostring(state, 2, 0);
5 QObject * b = (QObject*)tolua_tousertype(state, 3, 0);
6 const char * slot = tolua_tostring(state, 4, 0);
7
8 QObject::connect(a, QString("2%0").arg(signal).toLocal8Bit().data(),
9 b, QString("1%0").arg(slot).toLocal8Bit().data());
10 }
绑定时可以使用上面的QLua类:
lua.pushFunction("connect", connect);
完整main.cpp代码如下:
#include "include/lua.hpp"
#include "qlua.h"
#include <QWidget>
#include <QApplication>
#include <QFile>
#include <QDebug>
static int test(lua_State* state)
{
QPushButton* a = (QPushButton*)tolua_tousertype(state, 1, 0);
if(a)
a->show();
}
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());
}
static int tolua_new_QWidget(lua_State* state)
{
QWidget* widget = new QWidget();
tolua_pushusertype(state, widget, "QWidget");
return 1;
}
static int tolua_delete_QWidget(lua_State* state)
{
qDebug() << "delete Start";
QWidget* widget = (QWidget* )tolua_tousertype(state, 1, 0);
if(NULL != widget)
{
qDebug() << "delete~";
widget->close();
delete widget;
}
return 1;
}
static int tolua_Show_QWidget(lua_State* state)
{
QWidget* widget = (QWidget* )tolua_tousertype(state, 1, 0);
if(widget != NULL)
{
widget->show();
}
return 1;
}
static int tolua_new_QPushButton(lua_State* state)
{
QPushButton* button = new QPushButton();
tolua_pushusertype(state, button, "QPushButton");
return 1;
}
static int tolua_delete_QPushButton(lua_State* state)
{
QPushButton* button = (QPushButton* )tolua_tousertype(state, 1, 0);
if(NULL != button)
{
button->close();
delete button;
}
return 1;
}
static int tolua_Show_QPushButton(lua_State* state)
{
QPushButton* button = (QPushButton* )tolua_tousertype(state, 1, 0);
if(button != NULL)
{
button->show();
}
return 1;
}
static int tolua_setText_QPushButton(lua_State* state)
{
QPushButton* button = (QPushButton* )tolua_tousertype(state, 1, 0);
const char * text = tolua_tostring(state, 2, 0);
if(button != NULL)
{
button->setText(QString::fromLocal8Bit(text));
}
return 1;
}
static int tolua_Resize_QWidget(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;
}
static int QApplication_instance(lua_State* state)
{
tolua_pushusertype(state, QApplication::instance(), "QApplication");
return 1;
}
static int QApplication_quit(lua_State* state)
{
QApplication * app = (QApplication *)tolua_tousertype(state, 1, 0);
if(app)
app->quit();
return 1;
}
static int QApplication_delete(lua_State*)
{
return 1;
}
int main(int argc, char * argv[])
{
Q_INIT_RESOURCE(resources);
QApplication a(argc, argv);
QLua lua;
lua.beginModule("");
lua.addType("QWidget", tolua_delete_QWidget);
lua.moduleTypeFunction("new", tolua_new_QWidget);
lua.moduleTypeFunction("show", tolua_Show_QWidget);
lua.moduleTypeFunction("resize", tolua_Resize_QWidget);
lua.endModule();
lua.addType("QPushButton", tolua_delete_QPushButton);
lua.moduleTypeFunction("new", tolua_new_QPushButton);
lua.moduleTypeFunction("show", tolua_Show_QPushButton);
lua.moduleTypeFunction("setText", tolua_setText_QPushButton);
lua.endModule();
lua.addType("QApplication", QApplication_delete);
lua.moduleTypeFunction("instance", QApplication_instance);
lua.moduleTypeFunction("quit", QApplication_quit);
lua.endModule();
lua.endModule();
lua.pushFunction("test", test);
lua.pushFunction("connect", connect);
// 读取资源文件
QFile file("://test.lua");
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream in(&file);
in.setCodec("UTF-8");
// 执行
lua.run(in.readAll());
return a.exec();
}
test.lua代码如下:
widget = QWidget:new()
widget:show()
button = QPushButton:new()
button:setText("China")
button:show()
connect(button, "clicked()", widget, "close()")
connect(button, "clicked()", button, "close()")
print("run over")
程序运行结果如下:

点击China按钮,button和widget的窗口都会关闭。
至此,已经实现了连接 Qt 对象的信号与槽。
如果继续做下去,完全可以实现在Lua脚本中使用Qt的类和对象,写出Lua的Gui程序。
附完整代码工程文件:http://pan.baidu.com/s/1ntmFdjj
附个偶的博客地址:http://www.cnblogs.com/IT-BOY/
