Commit bc9ac14d authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Finished implementation of Lua output redirection:

* Introduced two new tgt::LogManager log levels LuaInfo and LuaError.
* ScriptingWidget registers itself as logger and prints the Lua output into the console window.
parent f1b3cd9a
......@@ -150,6 +150,7 @@ namespace campvis {
#ifdef CAMPVIS_HAS_SCRIPTING
// create and store Lua VM for this very pipeline
_luaVmState = new LuaVmState();
_luaVmState->redirectLuaPrint();
// Let Lua know where CAMPVis modules are located
if (! _luaVmState->execString("package.cpath = '" CAMPVIS_LUA_MODS_PATH "'"))
......@@ -166,8 +167,6 @@ namespace campvis {
if (! _luaVmState->execString("inspect = require 'inspect'"))
LERROR("Error setting up Lua VM.");
_luaVmState->redirectLuaPrint();
#endif
// parse argument list and create pipelines
......@@ -324,6 +323,7 @@ namespace campvis {
LuaVmState* CampVisApplication::getLuaVmState() {
return _luaVmState;
}
#endif
}
......@@ -276,6 +276,9 @@ namespace campvis {
_dcInspectorWidget->init();
_logViewer->init();
if (_scriptingConsoleWidget)
_scriptingConsoleWidget->init();
}
void MainWindow::deinit() {
......@@ -283,6 +286,9 @@ namespace campvis {
_dcInspectorWidget->deinit();
_logViewer->deinit();
if (_scriptingConsoleWidget)
_scriptingConsoleWidget->deinit();
}
void MainWindow::addVisualizationPipelineWidget(const std::string& name, QWidget* canvas) {
......
......@@ -37,11 +37,20 @@ namespace campvis {
, _currentPosition(-1)
{
setupGUI();
addCat("", true, tgt::LuaInfo);
}
ScriptingWidget::~ScriptingWidget() {
}
void ScriptingWidget::init() {
LogMgr.addLog(this);
}
void ScriptingWidget::deinit() {
LogMgr.removeLog(this);
}
void ScriptingWidget::setupGUI() {
setWindowTitle(tr("Scripting Console"));
......@@ -75,11 +84,10 @@ namespace campvis {
connect(_btnClear, SIGNAL(clicked()), this, SLOT(clearLog()));
connect(_btnExecute, SIGNAL(clicked()), this, SLOT(execute()));
connect(_editCommand, SIGNAL(returnPressed()), this, SLOT(execute()));
connect(this, SIGNAL(s_commandExecuted(QString)), this, SLOT(appendMessage(const QString&)));
}
void ScriptingWidget::appendMessage(const QString& message) {
_consoleDisplay->append(tr("> ") + message);
_consoleDisplay->append(message);
}
void ScriptingWidget::clearLog() {
......@@ -88,7 +96,10 @@ namespace campvis {
void ScriptingWidget::execute() {
QString command = _editCommand->text();
appendMessage(tr("> ") + command);
emit s_commandExecuted(command);
_history.push_front(command);
_currentPosition = -1;
_editCommand->clear();
......@@ -116,6 +127,13 @@ namespace campvis {
return QObject::eventFilter(obj, event);
}
void ScriptingWidget::logFiltered(const std::string &cat, tgt::LogLevel level, const std::string& msg, const std::string& extendedInfo/*=""*/) {
if (level == tgt::LuaInfo || level == tgt::LuaError) {
appendMessage(QString::fromStdString(msg));
}
}
}
......@@ -26,6 +26,7 @@
#define SCRIPTINGWIDGET_H__
#include "sigslot/sigslot.h"
#include "tgt/logmanager.h"
#include "tgt/painter.h"
#include "tbb/mutex.h"
......@@ -45,13 +46,15 @@
#include <QTextEdit>
namespace campvis {
class ScriptingWidget : public QWidget {
/**
* Qt widget providing a console-like interface to the Lua VM of CampvisApplication.
*/
class ScriptingWidget : public QWidget, tgt::Log {
Q_OBJECT;
public:
/**
* Creates a new DataContainerInspectorWidget.
* Creates a new ScriptingWidget.
* \param parent Parent Qt widget, may be 0 (default)
*/
explicit ScriptingWidget(QWidget* parent = 0);
......@@ -61,6 +64,18 @@ namespace campvis {
*/
~ScriptingWidget();
bool isOpen() { return true; }
/**
* Initializes the underlying log and registers it with tgt.
*/
void init();
/**
* Deinitializes the underlying log and deregisters it from tgt.
*/
void deinit();
protected:
/**
* Setup the the log viewer's GUI
......@@ -69,6 +84,8 @@ namespace campvis {
bool eventFilter(QObject* obj, QEvent* event);
void logFiltered(const std::string &cat, tgt::LogLevel level, const std::string& msg, const std::string& extendedInfo="");
public slots:
/**
* Append the given message to the log viewer
......@@ -98,7 +115,7 @@ namespace campvis {
QPushButton* _btnClear; ///< Button to clear the console output
std::deque<QString> _history; ///< History of executed commands
int _currentPosition; ///< Current position in command history
int _currentPosition; ///< Current position in command history
};
}
......
......@@ -41,11 +41,13 @@ namespace tgt {
* Debug messages are not logged in release builds!
*/
enum LogLevel {
Debug,
Info,
Warning,
Error,
Fatal
LuaInfo = 0,
LuaError = 1,
Debug = 10,
Info = 11,
Warning = 12,
Error = 13,
Fatal = 14
};
/**
......
#include "luavmstate.h"
#include <iostream>
#include "globalluatable.h"
#include "tgt/logmanager.h"
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
#include "luavmstate.h"
#include <iostream>
#include "globalluatable.h"
#include "tgt/logmanager.h"
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
static int lua_campvis_print(lua_State* L) {
int nargs = lua_gettop(L);
lua_getglobal(L, "tostring");
std::string str;
for (int i=1; i <= nargs; i++) {
const char *s;
size_t l;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tolstring(L, -1, &l); /* get result */
if (s == NULL)
return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print"));
if (i>1)
str += "\t";
str += s;
const char *s;
size_t l;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tolstring(L, -1, &l); /* get result */
if (s == NULL)
return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print"));
if (i>1)
str += "\t";
str += s;
lua_pop(L, 1); /* pop result */
}
LINFOC("Lua", str);
LogMgr.log("Lua", tgt::LuaInfo, str);
return 0;
}
static int lua_campvis_debug(lua_State* L) {
for (;;) {
char buffer[250];
//luai_writestringerror("%s", "lua_debug> ");
if (fgets(buffer, sizeof(buffer), stdin) == 0 || strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || lua_pcall(L, 0, 0, 0))
LDEBUGC("Lua", lua_tostring(L, -1));
lua_settop(L, 0); /* remove eventual returns */
}
}
namespace campvis {
LuaVmState::LuaVmState(bool loadStdLibs /*= true*/)
: _luaState(0)
, _luaStateMutex()
{
_luaState = luaL_newstate();
// load standard Lua libraries
if (loadStdLibs)
luaL_openlibs(_luaState);
/*
* Store a pointer to the mutex guarding access to _luaState in the state's registry; this
* way code that accesses Lua state directly (e.g. connections between sigslot's signals and
* slots defined in Lua) has access to it and can lock it when necessary.
*/
lua_pushlightuserdata(_luaState, static_cast<void*>(_luaState));
lua_pushlightuserdata(_luaState, static_cast<void*>(&_luaStateMutex));
lua_settable(_luaState, LUA_REGISTRYINDEX);
}
LuaVmState::~LuaVmState() {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
lua_close(_luaState);
}
void LuaVmState::logLuaError() {
const char* errorMsg = lua_tostring(_luaState, -1);
if (errorMsg == nullptr)
std::cerr << "(error object is not a string)" << std::endl;
else
std::cerr << errorMsg << std::endl;
lua_pop(_luaState, 1);
}
bool LuaVmState::execFile(const std::string& scriptPath) {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
// run a Lua script here; true is returned if there were errors
if (luaL_dofile(_luaState, scriptPath.c_str())) {
this->logLuaError();
return false;
}
return true;
}
bool LuaVmState::execString(const std::string& scriptString) {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
if (luaL_dostring(_luaState, scriptString.c_str())) {
this->logLuaError();
return false;
}
return true;
}
std::shared_ptr<GlobalLuaTable> LuaVmState::getGlobalTable() {
return std::shared_ptr<GlobalLuaTable>(new GlobalLuaTable(*this));
}
lua_State* LuaVmState::rawState() const {
return _luaState;
}
LuaStateMutexType& LuaVmState::getMutex() {
return _luaStateMutex;
}
void LuaVmState::callLuaFunc(int nargs, int nresults) {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
if (lua_pcall(_luaState, nargs, nresults, 0) != LUA_OK) {
this->logLuaError();
}
}
void LuaVmState::redirectLuaPrint() {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
}
static int lua_campvis_debug(lua_State* L) {
for (;;) {
char buffer[250];
//luai_writestringerror("%s", "lua_debug> ");
if (fgets(buffer, sizeof(buffer), stdin) == 0 || strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || lua_pcall(L, 0, 0, 0))
LDEBUGC("Lua", lua_tostring(L, -1));
lua_settop(L, 0); /* remove eventual returns */
}
}
namespace campvis {
LuaVmState::LuaVmState(bool loadStdLibs /*= true*/)
: _luaState(0)
, _luaStateMutex()
{
_luaState = luaL_newstate();
// load standard Lua libraries
if (loadStdLibs)
luaL_openlibs(_luaState);
/*
* Store a pointer to the mutex guarding access to _luaState in the state's registry; this
* way code that accesses Lua state directly (e.g. connections between sigslot's signals and
* slots defined in Lua) has access to it and can lock it when necessary.
*/
lua_pushlightuserdata(_luaState, static_cast<void*>(_luaState));
lua_pushlightuserdata(_luaState, static_cast<void*>(&_luaStateMutex));
lua_settable(_luaState, LUA_REGISTRYINDEX);
}
LuaVmState::~LuaVmState() {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
lua_close(_luaState);
}
void LuaVmState::logLuaError() {
const char* errorMsg = lua_tostring(_luaState, -1);
if (errorMsg == nullptr)
LogMgr.log("Lua", tgt::LuaError, "(error object is not a string)");
else
LogMgr.log("Lua", tgt::LuaError, errorMsg);
lua_pop(_luaState, 1);
}
bool LuaVmState::execFile(const std::string& scriptPath) {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
// run a Lua script here; true is returned if there were errors
if (luaL_dofile(_luaState, scriptPath.c_str())) {
this->logLuaError();
return false;
}
return true;
}
bool LuaVmState::execString(const std::string& scriptString) {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
if (luaL_dostring(_luaState, scriptString.c_str())) {
this->logLuaError();
return false;
}
return true;
}
std::shared_ptr<GlobalLuaTable> LuaVmState::getGlobalTable() {
return std::shared_ptr<GlobalLuaTable>(new GlobalLuaTable(*this));
}
lua_State* LuaVmState::rawState() const {
return _luaState;
}
LuaStateMutexType& LuaVmState::getMutex() {
return _luaStateMutex;
}
void LuaVmState::callLuaFunc(int nargs, int nresults) {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
if (lua_pcall(_luaState, nargs, nresults, 0) != LUA_OK) {
this->logLuaError();
}
}
void LuaVmState::redirectLuaPrint() {
LuaStateMutexType::scoped_lock lock(_luaStateMutex);
static const struct luaL_Reg printlib[] = {
{"print", &lua_campvis_print},
{"debug", &lua_campvis_debug},
{"debug", &lua_campvis_debug},
{NULL, NULL} /* end of array */
};
lua_getglobal(_luaState, "_G");
luaL_register(_luaState, NULL, printlib);
lua_pop(_luaState, 1);
}
}
lua_pop(_luaState, 1);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment