Вы правильно запускаете скрипт Lua в специальной ветке. Это правильный способ сделать это - почти. Вы перезапускаете поток каждый раз, когда хотите запустить скрипт. Это неверно. Вы также получаете доступ к данным в потоке GUI из потока LUA без какой-либо синхронизации. Это не хорошо. Qt предоставляет отличный механизм для этого в виде соединений в очереди между сигналами и слотами. Когда вызов сигнального слота проходит границы потоков, параметры заключаются в QEvent
и асинхронно доставляются к цели QObject
. В каждом потоке доставка событий сериализуется, поэтому вам не нужно беспокоиться о повреждении данных и т. Д.
Вот как это должно быть сделано:
// LUAObject.h
#include <QObject>
class LUAObject : public QObject
{
Q_OBJECT
public:
LUAObject(QObject * parent = 0);
public slots:
void setScript(const QString &);
void runScript();
void stop();
signals:
void hasError(const QString &);
void finished();
void hasParallelData(int);
void hasMessage(const QString &);
private:
QString script;
bool stop;
}
// LUAObject.cpp
// whatever Lua includes you need etc
LUAObject::LUAObject(QObject* p) : QObject(p)
{}
void LUAObject::stop() { stopped = true; }
void LUAObject::setScript(const QString & scr)
{ script = scr; }
int L_PWrite(lua_State* l)
{
int data = luaL_optint(l, 1, -1);
if (data != -1) {
// access the parallel port HERE, NOT in the GUI thread!
emit hasParallelData(luaL_optint(l, 1, 0));
}
return 0;
}
// returns a bool - true means we are stopped and should exit
int L_MSleep(lua_State* l)
{
int ms = luaL_optint(l, 1, -1);
if (ms == -1) return 0;
QApplication::processEvents(QEventLoop::WaitForMoreEvents, ms);
lua_pushBoolean(l, stopped); // event processing would run the stop() slot call
return 1;
}
int L_SSleep(lua_State* l)
{
int secs = luaL_optint(l, 1, -1);
if (secs == -1) return 0;
QApplication::processEvents(QEventLoop::WaitForMoreEvents, secs*1000);
lua_pushBoolean(l, stopped); // event processing would run the stop() slot call
return 1;
}
int L_Log(lua_State* l)
{
const char * msg = luaL_optstring(l, 1, 0);
if (!msg) return 0;
emit hasMessage(msg);
return 0;
}
class Lua // RAII
{
public:
explicit Lua(lua_state * l) : L(l) {}
~Lua() { lua_close(L); }
operator lua_state*() const { return L; }
private:
lua_state * L;
Q_DISABLE_COPY(LUA)
};
LUAObject::runScript()
{
stopped = false;
Lua L(lua_open());
luaL_openlibs(L);
if (luaL_loadbuffer(L, script.toAscii().constData(), script.length(), "script") || lua_pcall(L, 0, 0, 0))
{
emit hasError(lua_tostring(L, -1));
return;
}
lua_register(L, "ssleep", L_SSleep);
lua_register(L, "msleep", L_MSleep);
lua_register(L, "pwrite", L_PWrite);
lua_register(L, "print", L_Log);
lua_getglobal(L, "dotest");
if (!lua_isfunction(L, -1))
{
emit hasError("Test function(dotest) should be a function");
return;
}
if(lua_pcall(L, 0, 0, 0))
{
emit hasError(lua_tostring(L, -1));
return;
}
emit finished();
}
// main.cpp
#include <QApplication>
#include <QMetaMethod>
#include "LUAObject.h"
...
int main(int argc, char** argv)
{
QApplication(argc, argv);
MainWindow window;
...
QThread thread;
LUAObject lua;
thread.start(QThread::TimeCriticalPriority);
lua.moveToThread(&thread);
...
// NOTE: you can ONLY connect to LUAObject slots, you CANNOT call them
// directly since it runs in a separate thread!
connect(&window, SIGNAL(startClicked()), &lua, SLOT(runScript());
connect(&lua, SIGNAL(hasError(QString)), &window, SLOT(luaError(QString)));
...
window.show();
int rc = qApp->exec();
QMetaObject::invokeMethod(&lua, SLOT(stop())); // cross-thread slot invocation
thread.exit();
thread.wait();
return rc;
}
Я оставляю реализацию интерфейса на ваше воображение. Обратите внимание, что это непроверенный код. Это может взорвать ваш компьютер, насколько я знаю.