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

 

posted @ 2020-12-07 09:06  无脑仔的小明  阅读(4232)  评论(0编辑  收藏  举报