QT绑定Lua脚本,相互调用
0. 前言
最近有个需求,就是需要在QT(C++)中移植lua脚本。达到可以动态更新软件功能。lua是一门脚本语言。常用于各类编程语言,作为脚本。特别是游戏行业,据说很多用lua脚本来写业务逻辑。本次分为两种调用,一种是QT调用Lua,这种比较简单。利用Lua源码编译后,直接就可以使用。另外一种是Lua调用QT里面的函数,这种就比较麻烦,这里采用第三方库LuaBridge。
1. 安装,编译
到lua官网下载lua源代码,我这里使用5.3版本。然后在GitHub上,下载LuaBridge源码(下载路径在本文最后面的参考资料里面)。然后把源码加到QT工程目录下。目录结构如下:
配置QT工程文件 LuaDemo.pro 两个注意点,注意lua版本间的差异。还有就是SOURCES += 加入C文件时, lua.c luac.c 这两个文件不要加入到编译文件中。下面这个pro工程文件,仅供参考
1 QT += core gui 2 3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 5 CONFIG += c++11 6 7 # The following define makes your compiler emit warnings if you use 8 # any Qt feature that has been marked deprecated (the exact warnings 9 # depend on your compiler). Please consult the documentation of the 10 # deprecated API in order to know how to port your code away from it. 11 DEFINES += QT_DEPRECATED_WARNINGS 12 13 # You can also make your code fail to compile if it uses deprecated APIs. 14 # In order to do so, uncomment the following line. 15 # You can also select to disable deprecated APIs only up to a certain version of Qt. 16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 17 18 SOURCES += \ 19 lua-5.3.5/src/lapi.c \ 20 lua-5.3.5/src/lauxlib.c \ 21 lua-5.3.5/src/lbaselib.c\ 22 lua-5.3.5/src/lbitlib.c \ 23 lua-5.3.5/src/lcode.c \ 24 lua-5.3.5/src/lcorolib.c\ 25 lua-5.3.5/src/lctype.c \ 26 lua-5.3.5/src/ldblib.c \ 27 lua-5.3.5/src/ldebug.c \ 28 lua-5.3.5/src/ldo.c \ 29 lua-5.3.5/src/ldump.c \ 30 lua-5.3.5/src/lfunc.c \ 31 lua-5.3.5/src/lgc.c \ 32 lua-5.3.5/src/linit.c \ 33 lua-5.3.5/src/liolib.c \ 34 lua-5.3.5/src/llex.c \ 35 lua-5.3.5/src/lmathlib.c\ 36 lua-5.3.5/src/lmem.c \ 37 lua-5.3.5/src/loadlib.c \ 38 lua-5.3.5/src/lobject.c \ 39 lua-5.3.5/src/lopcodes.c\ 40 lua-5.3.5/src/loslib.c \ 41 lua-5.3.5/src/lparser.c \ 42 lua-5.3.5/src/lstate.c \ 43 lua-5.3.5/src/lstring.c \ 44 lua-5.3.5/src/lstrlib.c \ 45 lua-5.3.5/src/ltable.c \ 46 lua-5.3.5/src/ltablib.c \ 47 lua-5.3.5/src/ltm.c \ 48 lua-5.3.5/src/lundump.c \ 49 lua-5.3.5/src/lutf8lib.c\ 50 lua-5.3.5/src/lvm.c \ 51 lua-5.3.5/src/lzio.c \ 52 main.cpp \ 53 mainwindow.cpp \ 54 qtwidgetbridgelua.cpp 55 56 57 58 HEADERS += \ 59 common.h \ 60 mainwindow.h \ 61 qtwidgetbridgelua.h 62 63 #INCLUDEPATH += lua-5.2.0/src 64 INCLUDEPATH += lua-5.3.5/src 65 66 67 FORMS += \ 68 mainwindow.ui 69 70 # Default rules for deployment. 71 qnx: target.path = /tmp/$${TARGET}/bin 72 else: unix:!android: target.path = /opt/$${TARGET}/bin 73 !isEmpty(target.path): INSTALLS += target
头文件引用 common.h 这里引用lua.hpp, 还有LuaBridge.h 即可。
1 #ifndef COMMON_H 2 #define COMMON_H 3 4 #include "lua.hpp" 5 #include "LuaBridge/LuaBridge.h" 6 7 #include <QString> 8 #include <QRandomGenerator> 9 #include <QDebug> 10 11 12 #endif // COMMON_H
这样就把lua和LuaBridge依赖关系搞定了。接下来就是写逻辑代码了。
2. QT调用Lua
QT调用Lua,相对比较简单。
1 lua_State * LUA; 2 int luaAdd(int x, int y) 3 { 4 int sum; 5 lua_getglobal(LUA, "add"); 6 lua_pushnumber(LUA, x); 7 lua_pushnumber(LUA, y); 8 lua_call(LUA, 2, 1); //2个参数, 1个返回值 9 sum = (int)lua_tonumber(LUA, -1); //获取结果 10 lua_pop(LUA, 1); //清楚返回值,弹出栈 11 return sum; 12 } 13 14 int main(int argc, char *argv[]) 15 { 16 LUA = luaL_newstate(); //新建lua解释器 17 luaL_openlibs(LUA); //载入lua基础库 18 //执行lua脚本 19 //luaL_loadfile(LUA, "add.lua"); //载入lua脚本 20 luaL_dofile(LUA, "add.lua"); 21 int sum = luaAdd(10, 20); 22 qDebug() << "sum: " << sum; 23 lua_close(LUA); 24 return 0; 25 }
3. Lua调用QT
定义几个Class
1 class A 2 { 3 public: 4 A() { } 5 virtual void foo( int a ) { 6 qDebug() << "foo base: " << a << ":" << Member.c_str(); 7 } 8 std::string Member; 9 }; 10 11 class B : public A 12 { 13 public: 14 virtual void foo( int a ) { 15 qDebug() << "foo inherited: " << a << ":" << Member.c_str(); 16 } 17 }; 18 void foo(int a) { 19 qDebug() << "foo: " << a; 20 }
1 class A; class B; 2 lua_State * LUA; 3 int main(int argc, char *argv[]) 4 { 5 qDebug() << "aab"; 6 lua_State* L = luaL_newstate(); 7 //加载Lua基本库 8 luaL_openlibs(L); 9 luabridge::getGlobalNamespace(L) 10 .beginClass<A>("Sobj") 11 .addConstructor<void (*) (void)> () 12 .addFunction("foo", &A::foo) 13 .addData("Member",&A::Member) 14 .endClass() 15 .deriveClass<B, A>("SSec") 16 .addFunction("foo",&B::foo ) 17 .endClass(); 18 luabridge::getGlobalNamespace(L).addFunction("foo", foo ); 19 20 B ins; 21 ins.Member = "data"; 22 luabridge::setGlobal(L, ins, "ins"); 23 ins.foo(3); 24 25 luaL_dofile(L, "qt.lua"); 26 return 0; 27 }
Lua脚本
1 print('start') 2 local a = Sobj() 3 a.Member='World' 4 a:foo(2) 5 ins.Member='Hello' 6 ins:foo(3) 7 foo(1) 8 print('end')
4. 代码预览
common.h
1 #ifndef COMMON_H 2 #define COMMON_H 3 4 #include "lua.hpp" 5 #include "LuaBridge/LuaBridge.h" 6 7 #include <QString> 8 #include <QRandomGenerator> 9 #include <QDebug> 10 11 12 #endif // COMMON_H
mainwindow.h
1 #ifndef MAINWINDOW_H 2 #define MAINWINDOW_H 3 4 #include "qtwidgetbridgelua.h" 5 #include <QMainWindow> 6 7 QT_BEGIN_NAMESPACE 8 namespace Ui { class MainWindow; } 9 QT_END_NAMESPACE 10 11 class MainWindow : public QMainWindow 12 { 13 Q_OBJECT 14 15 public: 16 MainWindow(QWidget *parent = nullptr); 17 ~MainWindow(); 18 19 private slots: 20 void on_btnQtCallLua_clicked(); 21 void on_btnLuaCallQt_clicked(); 22 23 private: 24 Ui::MainWindow *ui; 25 QtWidgetBridgeLua lua; 26 }; 27 #endif // MAINWINDOW_H
qtwidgetbridgelua.h
1 #ifndef QTWIDGETBRIDGELUA_H 2 #define QTWIDGETBRIDGELUA_H 3 4 #include "common.h" 5 6 class QtWidgetBridgeLua 7 { 8 public: 9 QtWidgetBridgeLua(); 10 ~QtWidgetBridgeLua(); 11 12 //加载Lua调Qt的脚本 13 void luaCallQtLoadCode(QString code); 14 //注册所有函数 15 void registerFunction(); 16 //加法 17 int luaCallQtAdd(int x, int y); 18 //计算字符长度 19 int luaCallQtStrlen(std::string str); 20 //产生随机数 21 std::string luaCallQtRandomStr(int cnt); 22 //打印变量 23 void luaCallQtPrint(); 24 25 //加载Qt调用Lua的脚本 26 void qtCallLuaLoadCode(QString code); 27 //加法 28 int qtCallLuaAdd(int x, int y); 29 //计算字符长度 30 int qtCallLuaStrlen(std::string str); 31 //产生随机数 32 std::string qtCallLuaRandomStr(int cnt); 33 //打印变量 34 void qtCallLuaPrint(); 35 36 public: 37 std::string member; 38 int value; 39 40 private: 41 lua_State * L; 42 }; 43 44 #endif // QTWIDGETBRIDGELUA_H
main.cpp
1 #include "mainwindow.h" 2 #include "qtwidgetbridgelua.h" 3 4 #include <QApplication> 5 #include <QDebug> 6 7 //#include "lua.hpp" 8 //#include "LuaBridge/LuaBridge.h" 9 10 int main(int argc, char *argv[]) 11 { 12 QApplication a(argc, argv); 13 MainWindow w; 14 w.show(); 15 return a.exec(); 16 }
mainwindow.cpp
1 #include "mainwindow.h" 2 #include "ui_mainwindow.h" 3 4 MainWindow::MainWindow(QWidget *parent) 5 : QMainWindow(parent) 6 , ui(new Ui::MainWindow) 7 { 8 ui->setupUi(this); 9 10 } 11 12 MainWindow::~MainWindow() 13 { 14 delete ui; 15 } 16 17 void MainWindow::on_btnQtCallLua_clicked() 18 { 19 QString txt = ui->txtQtCallLua->toPlainText(); 20 lua.qtCallLuaLoadCode(txt); 21 qDebug() << "qt call lua:"; 22 //调用加法计算 23 int sum = lua.qtCallLuaAdd(20, 30); 24 qDebug() << "add :" << sum; 25 //调用计算字符串长度 26 int len = lua.qtCallLuaStrlen("Hello World!!"); 27 qDebug() << "strlen: " << len; 28 //调用随机数 29 std::string randstr = lua.qtCallLuaRandomStr(12); 30 qDebug() << "randstr: " << randstr.c_str(); 31 //调用打印 32 lua.qtCallLuaPrint(); 33 } 34 35 void MainWindow::on_btnLuaCallQt_clicked() 36 { 37 QString txt = ui->txtLuaCallQt->toPlainText(); 38 lua.luaCallQtLoadCode(txt); 39 }
qtwidgetbridgelua.cpp
1 #include "qtwidgetbridgelua.h" 2 3 QtWidgetBridgeLua::QtWidgetBridgeLua() 4 { 5 L = luaL_newstate(); //新建lua解析器 6 luaL_openlibs(L); //载入lua基础库 7 } 8 QtWidgetBridgeLua::~QtWidgetBridgeLua() 9 { 10 lua_close(L); //释放lua解析器 11 } 12 13 /****************Lua调用Qt函数*******************/ 14 void QtWidgetBridgeLua::luaCallQtLoadCode(QString code) 15 { 16 //每次都重新创建lua解析器 17 lua_close(L); 18 L = luaL_newstate(); 19 luaL_openlibs(L); 20 //注册所有函数 21 registerFunction(); 22 luaL_dostring(L, code.toStdString().c_str()); //加载脚本 23 //luaL_dofile(L, "sample.lua"); //文件方式加载脚本 24 } 25 void QtWidgetBridgeLua::registerFunction() 26 { 27 luabridge::getGlobalNamespace(L) 28 .beginClass<QtWidgetBridgeLua>("QtUtils") 29 .addConstructor<void (*) (void)>() 30 .addFunction("luaCallQtAdd", &QtWidgetBridgeLua::luaCallQtAdd) 31 .addFunction("luaCallQtStrlen", &QtWidgetBridgeLua::luaCallQtStrlen) 32 .addFunction("luaCallQtRandomStr", &QtWidgetBridgeLua::luaCallQtRandomStr) 33 .addFunction("luaCallQtPrint", &QtWidgetBridgeLua::luaCallQtPrint) 34 .addData("member", &QtWidgetBridgeLua::member) 35 .addData("value", &QtWidgetBridgeLua::value) 36 .endClass(); 37 } 38 int QtWidgetBridgeLua::luaCallQtAdd(int x, int y) 39 { 40 return x + y; 41 } 42 int QtWidgetBridgeLua::luaCallQtStrlen(std::string str) 43 { 44 return str.length(); 45 } 46 std::string QtWidgetBridgeLua::luaCallQtRandomStr(int cnt) 47 { 48 const char ch[] = "0123456789abcdefABCDEF"; 49 int start = 0; 50 int end = sizeof(ch) - 1; 51 char *str = new char[cnt + 1]; 52 for(int i=0; i<cnt; i++) 53 { 54 int val = QRandomGenerator::global()->bounded(start, end); 55 str[i] = ch[val]; 56 } 57 str[cnt] = '\0'; 58 return std::string(str); 59 } 60 void QtWidgetBridgeLua::luaCallQtPrint() 61 { 62 qDebug() << this->member.c_str() << " : " << this->value; 63 } 64 65 /****************Qt调用Lua函数*******************/ 66 void QtWidgetBridgeLua::qtCallLuaLoadCode(QString code) 67 { 68 //每次都重新创建lua解析器 69 lua_close(L); 70 L = luaL_newstate(); 71 luaL_openlibs(L); 72 73 luaL_dostring(L, code.toStdString().c_str()); //加载脚本 74 //luaL_dofile(L, "sample.lua"); //文件方式加载脚本 75 } 76 int QtWidgetBridgeLua::qtCallLuaAdd(int x, int y) 77 { 78 int sum; 79 lua_getglobal(L, "add"); 80 lua_pushnumber(L, x); 81 lua_pushnumber(L, y); 82 83 lua_call(L, 2, 1); //2个参数, 1个返回值 84 sum = (int)lua_tonumber(L, -1); //获取结果 85 lua_pop(L, 1); //清除返回值,弹出栈 86 return sum; 87 } 88 int QtWidgetBridgeLua::qtCallLuaStrlen(std::string str) 89 { 90 int len; 91 lua_getglobal(L, "strlen"); 92 lua_pushstring(L, str.c_str()); 93 lua_call(L, 1, 1); 94 len = (int)lua_tonumber(L, -1); 95 lua_pop(L, 1); 96 return len; 97 } 98 std::string QtWidgetBridgeLua::qtCallLuaRandomStr(int cnt) 99 { 100 lua_getglobal(L, "randomStr"); 101 lua_pushnumber(L, cnt); 102 lua_call(L, 1, 1); 103 const char* str = lua_tostring(L, -1); 104 lua_pop(L, 1); 105 return std::string(str); 106 } 107 void QtWidgetBridgeLua::qtCallLuaPrint() 108 { 109 lua_getglobal(L, "printInfo"); 110 lua_call(L, 0, 0); 111 return ; 112 }
mainwindow.ui
(略)
A.lua
1 function add(x,y) 2 return x - y 3 end 4 function strlen(str) 5 return #str 6 end 7 function printInfo(str) 8 print("printInfo") 9 end 10 function randomStr(len) 11 local rankStr = "" 12 local randNum = 0 13 math.randomseed(os.time()) 14 for i=1,len do 15 if math.random(1,3)==1 then 16 randNum=string.char(math.random(0,25)+65) --生成大写字母 random(0,25)生成0=< <=25的整数 17 elseif math.random(1,3)==2 then 18 randNum=string.char(math.random(0,25)+97) --生成小写字母 19 else 20 randNum=math.random(0,9) --生成0=< and <=9的随机数字 21 end 22 rankStr=rankStr..randNum 23 end 24 return rankStr 25 end
B.lua
1 print("lua call调用 Qt Class") 2 local utils = QtUtils(); 3 utils.member="aaaa" 4 print(utils.member) 5 local val = utils:luaCallQtAdd(10, 20) 6 print("lua call qt add: " .. val) 7 local len = utils:luaCallQtStrlen("Hello World ~~~~~~~") 8 print("lua call qt strlen: " .. len) 9 local str = utils:luaCallQtRandomStr(16); 10 print("lua call qt random: " .. str) 11 utils.member = "Member ****==> " 12 utils.value = 1024 13 utils:luaCallQtPrint()
5. 运行效果
参考资料:
https://www.runoob.com/lua/lua-tables.html
https://www.cnblogs.com/Hichy/p/7060226.html
http://www.cppblog.com/sunicdavy/archive/2013/12/07/204648.html
https://blog.csdn.net/qq_31073871/article/details/81285791
https://blog.csdn.net/FL63Zv9Zou86950w/article/details/78402879
http://www.lua.org/download.html
https://github.com/vinniefalco/LuaBridge
http://vinniefalco.github.io/LuaBridge/Manual.html#s2.1
本文地址: https://www.cnblogs.com/wunaozai/p/14087370.html
作者:无脑仔的小明 出处:http://www.cnblogs.com/wunaozai/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 如果文中有什么错误,欢迎指出。以免更多的人被误导。有需要沟通的,可以站内私信,文章留言,或者关注“无脑仔的小明”公众号私信我。一定尽力回答。 |