2011-10-19 51 views
7

我正在开发一个测试工具以从PC并行端口生成波形。这个工具被设计用于生成任意波形模式,其定时精度为ms,所以我使用Lua脚本来定义波形模式,当用户单击[Start]按钮时,GUI启动新的QThread来运行脚本。来自Lua脚本的nanosleep()调用暂停QT GUI线程

为lua的以下三个功能被实现为C++全局函数:

  • PWRITE:将数据写入到并行端口。
  • msleep:等待某个ms(使用nanosleep()实现)
  • print:覆盖Lua的默认打印函数,这个函数会将消息附加到一个QTextEdit控件。

当调用pwrite时,写入的数据存储在全局变量中,然后以20ms的间隔更新GUI以更新GUI上的并行端口数据。 (这种20ms的间隔刷新并不是一个好的设计,但是当数据改变时,我还没有弄清楚如何使用信号来更新GUI)。

该工具现在基本可以使用。波形输出没有问题,但并行端口数据更新有一些问题:

当Lua调用msleep时,GUI线程停止,并行端口数据仅在msleep结束后更新。

所以我的问题是:

  1. 如何实现睡眠方法,以便它不会停止更新的GUI线程?

  2. 如何实现pwrite,以便在写入数据更改时,GUI可以接收信号以更新并行端口数据?

程序GUI下面的链接: Program 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, 0, 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, 0, 0, 0)) 
    { 
     err = QString(lua_tostring(L, -1)); 
     return -1; 
    } 

    lua_close(L); 

    return 0; 
} 

回答

0

也许你应该使用Qt的msleep因为它是由QThread

4

您提供使用在专用线程中正确运行您的Lua脚本。这是正确的做法 - 差不多。每次要运行脚本时都要重新启动线程。这是错误的。您还可以从LUA线程访问GUI线程中的数据,而无需进行任何类型的同步。这不好。 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; 
} 

我离开了UI的实施发挥你的想象力。请注意,它是未经测试的代码。它可能会炸毁我所知的所有电脑。