我正在开发一种测试工具,用于从PC并行端口生成波形.此工具旨在生成任何波形图案,定时精度为ms,因此我使用Lua脚本定义波形图案,当用户单击[开始]按钮时,GUI启动新的QThread以运行脚本.
> pwrite:将数据写入并行端口.
> msleep:等待某个ms(使用nanosleep()实现)
> print:覆盖Lua默认打印功能,此功能会将消息附加到一个QTextEdit小部件.
当调用pwrite时,写入的数据存储在全局变量中,然后以20ms的间隔更新GUI以更新GUI上的并行端口数据. (这20ms间隔刷新不是一个好的设计,但我还没弄清楚如何在数据改变时使用信号进行GUI更新).
该工具现在基本上是功能性的.波形输出没有问题,但并行端口数据更新有一些问题:
当Lua调用msleep时,GUI线程停止,并行端口数据仅在msleep结束后更新.
所以我的问题是:
>如何实现sleep方法,以便它不会阻止GUI线程更新?
>如何实现pwrite,以便GUI在写入数据发生变化时可以接收信号来更新并行端口数据?
程序GUI如下链接:
相关代码:
/* common.cpp file */ int L_MSleep(lua_State* l) { int milisec=0; struct timespec req={0,0}; time_t sec; milisec=luaL_optint(l,1,0); // obtain parameter if (milisec==0) return 0; sec=(int)(milisec/1000); milisec=milisec-(sec*1000); req.tv_sec=sec; req.tv_nsec=milisec*1000000L; while(nanosleep(&req,&req)==-1) continue; return 1; } /* LuaRunner.cpp file */ LuaRunner::LuaRunner(QObject *parent) : QThread(parent) { runlua = false; } void LuaRunner::run() { QString err = ""; runlua = true; LUA_RunScript(this->ff,err); runlua = false; if(err != "") { emit errorMessage(err); } } int LuaRunner::LUA_RunScript(QString ff,QString &err) { L = lua_open(); luaL_openlibs(L); if (luaL_loadfile(L,ff.toAscii()) || lua_pcall(L,0)) { err = QString(lua_tostring(L,-1)); return -1; } lua_register(L,"ssleep",L_SSleep); lua_register(L,"msleep",L_MSleep); lua_register(L,"pwrite",L_PortWrite); lua_register(L,"print",L_Log); lua_getglobal(L,"dotest"); if (!lua_isfunction(L,-1)) { err = QString("Test function(dotest) should be a function"); return -1; } if(lua_pcall(L,-1)); return -1; } lua_close(L); return 0; }
解决方法
您正确地在专用线程中运行Lua脚本.这是正确的方法 – 差不多.每次要运行脚本时都要重新启动线程.那是错的.您还从LUA线程访问GUI线程中的数据,而不进行任何同步.这不好. Qt以信号和插槽之间的排队连接的形式提供了一种出色的机制.当signal-slot调用通过线程边界时,参数将被包装在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); if (data != -1) { // access the parallel port HERE,NOT in the GUI thread! emit hasParallelData(luaL_optint(l,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); 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); 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,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)) { emit hasError(lua_tostring(L,-1)); return; } lua_register(L,L_SSleep); lua_register(L,L_MSleep); lua_register(L,L_PWrite); lua_register(L,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)) { 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; }
我将UI的实现留给您的想象力.请注意,它是未经测试的代码.它可能会让我知道的所有东西都爆炸.