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

Merge branch 'lua-console' into 'development'

Lua console

Finished a quick and dirty implementation of a Lua console in CampvisApplication.
parents ac28b5d7 92c3bdef
...@@ -48,8 +48,10 @@ IF(CAMPVIS_BUILD_LIB_TGT) ...@@ -48,8 +48,10 @@ IF(CAMPVIS_BUILD_LIB_TGT)
ENDIF() ENDIF()
IF(CAMPVIS_BUILD_LIB_LUA) IF(CAMPVIS_BUILD_LIB_LUA)
IF(WIN32)
# Keep LUA_BUILD_AS_DLL in sync with BUILD_SHARED_LIBS # Keep LUA_BUILD_AS_DLL in sync with BUILD_SHARED_LIBS
SET(LUA_BUILD_AS_DLL ${BUILD_SHARED_LIBS} CACHE BOOL "Build Lua library as DLL" FORCE) SET(LUA_BUILD_AS_DLL ${BUILD_SHARED_LIBS} CACHE BOOL "Build Lua library as DLL" FORCE)
ENDIF(WIN32)
ADD_SUBDIRECTORY(ext/lua EXCLUDE_FROM_ALL) ADD_SUBDIRECTORY(ext/lua EXCLUDE_FROM_ALL)
...@@ -69,6 +71,17 @@ ENDIF() ...@@ -69,6 +71,17 @@ ENDIF()
IF(CAMPVIS_ENABLE_SCRIPTING) IF(CAMPVIS_ENABLE_SCRIPTING)
# First, find Lua to setup paths for all projects correctly
FIND_PACKAGE(Lua REQUIRED)
IF(LUA_FOUND)
MESSAGE(STATUS "* Found Lua")
LIST(APPEND CampvisGlobalDefinitions ${LUA_DEFINITIONS})
LIST(APPEND CampvisGlobalIncludeDirs ${LUA_INCLUDE_DIR})
LIST(APPEND CampvisGlobalExternalLibs ${LUA_LIBRARY})
ELSE(LUA_FOUND)
MESSAGE(FATAL_ERROR "Lua not found!")
ENDIF(LUA_FOUND)
ADD_SUBDIRECTORY(scripting) ADD_SUBDIRECTORY(scripting)
LIST(APPEND CampvisGlobalDefinitions "-DCAMPVIS_HAS_SCRIPTING") LIST(APPEND CampvisGlobalDefinitions "-DCAMPVIS_HAS_SCRIPTING")
ENDIF() ENDIF()
......
...@@ -38,6 +38,7 @@ SET(CampvisApplicationToBeMocced ...@@ -38,6 +38,7 @@ SET(CampvisApplicationToBeMocced
gui/qtdatahandle.h gui/qtdatahandle.h
gui/logviewerwidget.h gui/logviewerwidget.h
gui/loghighlighter.h gui/loghighlighter.h
gui/scriptingwidget.h
gui/mdi/mdidockarea.h gui/mdi/mdidockarea.h
gui/mdi/mdidockablewindow.h gui/mdi/mdidockablewindow.h
gui/mdi/mdidockedwindow.h gui/mdi/mdidockedwindow.h
......
...@@ -59,6 +59,7 @@ namespace campvis { ...@@ -59,6 +59,7 @@ namespace campvis {
, _localContext(0) , _localContext(0)
, _mainWindow(0) , _mainWindow(0)
, _errorTexture(nullptr) , _errorTexture(nullptr)
, _luaVmState(nullptr)
, _initialized(false) , _initialized(false)
, _argc(argc) , _argc(argc)
, _argv(argv) , _argv(argv)
...@@ -77,11 +78,9 @@ namespace campvis { ...@@ -77,11 +78,9 @@ namespace campvis {
tgtAssert(_initialized == false, "Destructing initialized CampVisApplication, deinitialize first!"); tgtAssert(_initialized == false, "Destructing initialized CampVisApplication, deinitialize first!");
// delete everything in the right order: // delete everything in the right order:
for (std::vector< std::pair<AbstractPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) { for (std::vector<PipelineRecord>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
delete it->second; delete it->_painter;
} delete it->_pipeline;
for (std::vector<AbstractPipeline*>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
delete *it;
} }
for (std::vector<DataContainer*>::iterator it = _dataContainers.begin(); it != _dataContainers.end(); ++it) { for (std::vector<DataContainer*>::iterator it = _dataContainers.begin(); it != _dataContainers.end(); ++it) {
delete *it; delete *it;
...@@ -147,6 +146,29 @@ namespace campvis { ...@@ -147,6 +146,29 @@ namespace campvis {
tgt::TextureReaderTga trt; tgt::TextureReaderTga trt;
_errorTexture = trt.loadTexture(CAMPVIS_SOURCE_DIR "/application/data/no_input.tga", tgt::Texture::LINEAR); _errorTexture = trt.loadTexture(CAMPVIS_SOURCE_DIR "/application/data/no_input.tga", tgt::Texture::LINEAR);
#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 "'"))
LERROR("Error setting up Lua VM.");
// Load CAMPVis' core Lua module to have SWIG glue for AutoEvaluationPipeline available
if (! _luaVmState->execString("require(\"campvis\")"))
LERROR("Error setting up Lua VM.");
if (! _luaVmState->execString("require(\"tgt\")"))
LERROR("Error setting up Lua VM.");
if (! _luaVmState->execString("pipelines = {}"))
LERROR("Error setting up Lua VM.");
if (! _luaVmState->execString("inspect = require 'inspect'"))
LERROR("Error setting up Lua VM.");
#endif
// parse argument list and create pipelines // parse argument list and create pipelines
QStringList pipelinesToAdd = this->arguments(); QStringList pipelinesToAdd = this->arguments();
for (int i = 1; i < pipelinesToAdd.size(); ++i) { for (int i = 1; i < pipelinesToAdd.size(); ++i) {
...@@ -170,14 +192,10 @@ namespace campvis { ...@@ -170,14 +192,10 @@ namespace campvis {
delete _errorTexture; delete _errorTexture;
// Deinit pipeline first // Deinit pipeline and painter first
for (std::vector<AbstractPipeline*>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) { for (std::vector<PipelineRecord>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
(*it)->deinit(); it->_pipeline->deinit();
} it->_painter->deinit();
// Now deinit painters:
for (std::vector< std::pair<AbstractPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) {
it->second->deinit();
} }
_mainWindow->deinit(); _mainWindow->deinit();
...@@ -227,8 +245,8 @@ namespace campvis { ...@@ -227,8 +245,8 @@ namespace campvis {
pipeline->setCanvas(canvas); pipeline->setCanvas(canvas);
painter->setErrorTexture(_errorTexture); painter->setErrorTexture(_errorTexture);
_visualizations.push_back(std::make_pair(pipeline, painter)); PipelineRecord pr = { pipeline, painter };
_pipelines.push_back(pipeline); _pipelines.push_back(pr);
_mainWindow->addVisualizationPipelineWidget(name, canvas); _mainWindow->addVisualizationPipelineWidget(name, canvas);
...@@ -238,6 +256,14 @@ namespace campvis { ...@@ -238,6 +256,14 @@ namespace campvis {
makeJobOnHeap<CampVisApplication, tgt::GLCanvas*, AbstractPipeline*>(this, &CampVisApplication::initGlContextAndPipeline, canvas, pipeline), makeJobOnHeap<CampVisApplication, tgt::GLCanvas*, AbstractPipeline*>(this, &CampVisApplication::initGlContextAndPipeline, canvas, pipeline),
OpenGLJobProcessor::SerialJob); OpenGLJobProcessor::SerialJob);
#ifdef CAMPVIS_HAS_SCRIPTING
if (! _luaVmState->injectObjectPointerToTable(pipeline, "campvis::AutoEvaluationPipeline *", "pipelines", static_cast<int>(_pipelines.size())))
//if (! _luaVmState->injectObjectPointerToTableField(pipeline, "campvis::AutoEvaluationPipeline *", "pipelines", name))
LERROR("Could not inject the pipeline into the Lua VM.");
_luaVmState->execString("inspect(pipelines)");
#endif
s_PipelinesChanged(); s_PipelinesChanged();
} }
...@@ -284,8 +310,8 @@ namespace campvis { ...@@ -284,8 +310,8 @@ namespace campvis {
LINFO("Rebuilding shaders from file successful."); LINFO("Rebuilding shaders from file successful.");
} }
for (std::vector<AbstractPipeline*>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) { for (std::vector<PipelineRecord>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
for (std::vector<AbstractProcessor*>::const_iterator pit = (*it)->getProcessors().begin(); pit != (*it)->getProcessors().end(); ++pit) { for (std::vector<AbstractProcessor*>::const_iterator pit = it->_pipeline->getProcessors().begin(); pit != it->_pipeline->getProcessors().end(); ++pit) {
if (VisualizationProcessor* tester = dynamic_cast<VisualizationProcessor*>(*pit)) { if (VisualizationProcessor* tester = dynamic_cast<VisualizationProcessor*>(*pit)) {
tester->invalidate(AbstractProcessor::INVALID_RESULT); tester->invalidate(AbstractProcessor::INVALID_RESULT);
} }
...@@ -293,5 +319,11 @@ namespace campvis { ...@@ -293,5 +319,11 @@ namespace campvis {
} }
} }
#ifdef CAMPVIS_HAS_SCRIPTING
LuaVmState* CampVisApplication::getLuaVmState() {
return _luaVmState;
}
#endif
} }
...@@ -35,6 +35,10 @@ ...@@ -35,6 +35,10 @@
#include "core/datastructures/datacontainer.h" #include "core/datastructures/datacontainer.h"
#ifdef CAMPVIS_HAS_SCRIPTING
#include "scripting/glue/luavmstate.h"
#endif
namespace tgt { namespace tgt {
class GLCanvas; class GLCanvas;
class QtThreadedCanvas; class QtThreadedCanvas;
...@@ -45,6 +49,7 @@ namespace campvis { ...@@ -45,6 +49,7 @@ namespace campvis {
class AbstractPipeline; class AbstractPipeline;
class MainWindow; class MainWindow;
class CampVisPainter; class CampVisPainter;
class LuaVmState;
/** /**
* The CampVisApplication class wraps Pipelines, Evaluators and Painters all together and takes * The CampVisApplication class wraps Pipelines, Evaluators and Painters all together and takes
...@@ -62,6 +67,12 @@ namespace campvis { ...@@ -62,6 +67,12 @@ namespace campvis {
friend class MainWindow; friend class MainWindow;
public: public:
/// Record storing a pipeline together with its painter and Lua VM state.
struct PipelineRecord {
AbstractPipeline* _pipeline;
CampVisPainter* _painter;
};
/** /**
* Creates a new CampVisApplication. * Creates a new CampVisApplication.
* \param argc number of passed arguments * \param argc number of passed arguments
...@@ -124,6 +135,15 @@ namespace campvis { ...@@ -124,6 +135,15 @@ namespace campvis {
*/ */
void rebuildAllShadersFromFiles(); void rebuildAllShadersFromFiles();
#ifdef CAMPVIS_HAS_SCRIPTING
/**
* Returns the global LuaVmState of this application.
*/
LuaVmState* getLuaVmState();
#endif
/// Signal emitted when the collection of pipelines has changed. /// Signal emitted when the collection of pipelines has changed.
sigslot::signal0<> s_PipelinesChanged; sigslot::signal0<> s_PipelinesChanged;
...@@ -135,9 +155,7 @@ namespace campvis { ...@@ -135,9 +155,7 @@ namespace campvis {
/// All pipelines /// All pipelines
std::vector<AbstractPipeline*> _pipelines; std::vector<PipelineRecord> _pipelines;
/// All visualisations (i.e. Pipelines with their corresponding painters/canvases)
std::vector< std::pair<AbstractPipeline*, CampVisPainter*> > _visualizations;
/// All DataContainers /// All DataContainers
std::vector<DataContainer*> _dataContainers; std::vector<DataContainer*> _dataContainers;
...@@ -153,8 +171,12 @@ namespace campvis { ...@@ -153,8 +171,12 @@ namespace campvis {
/// Main window hosting GUI stuff /// Main window hosting GUI stuff
MainWindow* _mainWindow; MainWindow* _mainWindow;
/// Error texture to show if there is no output found
tgt::Texture* _errorTexture; tgt::Texture* _errorTexture;
/// the global LuaVmState of this application
LuaVmState* _luaVmState;
/// Flag, whether CampVisApplication was correctly initialized /// Flag, whether CampVisApplication was correctly initialized
bool _initialized; bool _initialized;
......
...@@ -58,6 +58,7 @@ namespace campvis { ...@@ -58,6 +58,7 @@ namespace campvis {
, _selectedProcessor(0) , _selectedProcessor(0)
, _selectedDataContainer(0) , _selectedDataContainer(0)
, _logViewer(0) , _logViewer(0)
, _scriptingConsoleWidget(nullptr)
{ {
tgtAssert(_application != 0, "Application must not be 0."); tgtAssert(_application != 0, "Application must not be 0.");
ui.setupUi(this); ui.setupUi(this);
...@@ -130,6 +131,12 @@ namespace campvis { ...@@ -130,6 +131,12 @@ namespace campvis {
_logViewer = new LogViewerWidget(this); _logViewer = new LogViewerWidget(this);
ui.logViewerDock->setWidget(_logViewer); ui.logViewerDock->setWidget(_logViewer);
#ifdef CAMPVIS_HAS_SCRIPTING
_scriptingConsoleWidget = new ScriptingWidget(this);
ui.scriptingConsoleDock->setWidget(_scriptingConsoleWidget);
connect(_scriptingConsoleWidget, SIGNAL(s_commandExecuted(const QString&)), this, SLOT(onLuaCommandExecuted(const QString&)));
#endif
_dcInspectorWidget = new DataContainerInspectorWidget(); _dcInspectorWidget = new DataContainerInspectorWidget();
this->populateMainMenu(); this->populateMainMenu();
...@@ -185,11 +192,17 @@ namespace campvis { ...@@ -185,11 +192,17 @@ namespace campvis {
} }
void MainWindow::onPipelinesChanged() { void MainWindow::onPipelinesChanged() {
emit updatePipelineWidget(_application->_dataContainers, _application->_pipelines); std::vector<AbstractPipeline*> pipelines;
std::for_each(_application->_pipelines.begin(), _application->_pipelines.end(), [&] (const CampVisApplication::PipelineRecord& pr) { pipelines.push_back(pr._pipeline); });
emit updatePipelineWidget(_application->_dataContainers, pipelines);
} }
void MainWindow::onDataContainersChanged() { void MainWindow::onDataContainersChanged() {
emit updatePipelineWidget(_application->_dataContainers, _application->_pipelines); std::vector<AbstractPipeline*> pipelines;
std::for_each(_application->_pipelines.begin(), _application->_pipelines.end(), [&] (const CampVisApplication::PipelineRecord& pr) { pipelines.push_back(pr._pipeline); });
emit updatePipelineWidget(_application->_dataContainers, pipelines);
} }
void MainWindow::onPipelineWidgetItemClicked(const QModelIndex& index) { void MainWindow::onPipelineWidgetItemClicked(const QModelIndex& index) {
...@@ -263,6 +276,9 @@ namespace campvis { ...@@ -263,6 +276,9 @@ namespace campvis {
_dcInspectorWidget->init(); _dcInspectorWidget->init();
_logViewer->init(); _logViewer->init();
if (_scriptingConsoleWidget)
_scriptingConsoleWidget->init();
} }
void MainWindow::deinit() { void MainWindow::deinit() {
...@@ -270,6 +286,9 @@ namespace campvis { ...@@ -270,6 +286,9 @@ namespace campvis {
_dcInspectorWidget->deinit(); _dcInspectorWidget->deinit();
_logViewer->deinit(); _logViewer->deinit();
if (_scriptingConsoleWidget)
_scriptingConsoleWidget->deinit();
} }
void MainWindow::addVisualizationPipelineWidget(const std::string& name, QWidget* canvas) { void MainWindow::addVisualizationPipelineWidget(const std::string& name, QWidget* canvas) {
...@@ -311,4 +330,12 @@ namespace campvis { ...@@ -311,4 +330,12 @@ namespace campvis {
_application->rebuildAllShadersFromFiles(); _application->rebuildAllShadersFromFiles();
} }
void MainWindow::onLuaCommandExecuted(const QString& cmd) {
#ifdef CAMPVIS_HAS_SCRIPTING
if (_application->getLuaVmState() != nullptr) {
_application->getLuaVmState()->execString(cmd.toStdString());
}
#endif
}
} }
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "application/gui/pipelinetreewidget.h" #include "application/gui/pipelinetreewidget.h"
#include "application/gui/properties/propertycollectionwidget.h" #include "application/gui/properties/propertycollectionwidget.h"
#include "application/gui/logviewerwidget.h" #include "application/gui/logviewerwidget.h"
#include "application/gui/scriptingwidget.h"
#include "application/tools/qtexteditlog.h" #include "application/tools/qtexteditlog.h"
#include "application/ui_mainwindow.h" #include "application/ui_mainwindow.h"
...@@ -105,7 +106,7 @@ namespace campvis { ...@@ -105,7 +106,7 @@ namespace campvis {
/// Qt signal for updating the PropertyCollectionWidget /// Qt signal for updating the PropertyCollectionWidget
void updatePropCollectionWidget(HasPropertyCollection*, DataContainer*); void updatePropCollectionWidget(HasPropertyCollection*, DataContainer*);
private slots: public slots:
/** /**
* Slot to be called by the PipelineWidget when the selected item changes. * Slot to be called by the PipelineWidget when the selected item changes.
* \param index Index of the selected item * \param index Index of the selected item
...@@ -128,6 +129,9 @@ namespace campvis { ...@@ -128,6 +129,9 @@ namespace campvis {
/// Slot to be called when all shaders shall be rebuilt. /// Slot to be called when all shaders shall be rebuilt.
void onRebuildShadersClicked(); void onRebuildShadersClicked();
/// Slot to be called when a Lua command shall be executed.
void onLuaCommandExecuted(const QString& cmd);
private: private:
/** /**
...@@ -185,6 +189,8 @@ namespace campvis { ...@@ -185,6 +189,8 @@ namespace campvis {
DataContainer* _selectedDataContainer; ///< currently selected DataContainer DataContainer* _selectedDataContainer; ///< currently selected DataContainer
LogViewerWidget* _logViewer; ///< Widget displaying log messages LogViewerWidget* _logViewer; ///< Widget displaying log messages
ScriptingWidget* _scriptingConsoleWidget; ///< Widget showing the scripting console (if available)
std::vector<QDockWidget*> _primaryDocks; ///< Docks located in top docking area of the main window std::vector<QDockWidget*> _primaryDocks; ///< Docks located in top docking area of the main window
}; };
} }
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "scriptingwidget.h"
#include <QKeyEvent>
namespace campvis {
ScriptingWidget::ScriptingWidget(QWidget* parent)
: QWidget(parent)
, _consoleDisplay(nullptr)
, _editCommand(nullptr)
, _btnExecute(nullptr)
, _btnClear(nullptr)
, _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"));
QVBoxLayout* mainLayout = new QVBoxLayout(this);
QHBoxLayout* controlsLayout = new QHBoxLayout();
mainLayout->addLayout(controlsLayout);
_consoleDisplay = new QTextEdit(this);
_consoleDisplay->setReadOnly(true);
mainLayout->addWidget(_consoleDisplay);
// Use the system's default monospace font at the default size in the log viewer
QFont monoFont = QFont("Monospace");
monoFont.setStyleHint(QFont::TypeWriter);
monoFont.setPointSize(QFont().pointSize() + 1);
_consoleDisplay->document()->setDefaultFont(monoFont);
_editCommand = new QLineEdit(this);
_editCommand->setPlaceholderText(tr("Enter Lua commands here..."));
_editCommand->installEventFilter(this);
controlsLayout->addWidget(_editCommand);
_btnExecute = new QPushButton(tr("&Execute"), this);
controlsLayout->addWidget(_btnExecute);
_btnClear = new QPushButton(tr("&Clear"), this);
controlsLayout->addWidget(_btnClear);
connect(_btnClear, SIGNAL(clicked()), this, SLOT(clearLog()));
connect(_btnExecute, SIGNAL(clicked()), this, SLOT(execute()));
connect(_editCommand, SIGNAL(returnPressed()), this, SLOT(execute()));
}
void ScriptingWidget::appendMessage(const QString& message) {
_consoleDisplay->append(message);
}
void ScriptingWidget::clearLog() {
_consoleDisplay->clear();
}
void ScriptingWidget::execute() {
QString command = _editCommand->text();
appendMessage(tr("> ") + command);
emit s_commandExecuted(command);
_history.push_front(command);
_currentPosition = -1;
_editCommand->clear();
}
bool ScriptingWidget::eventFilter(QObject* obj, QEvent* event) {
if (obj == _editCommand && event->type() == QEvent::KeyPress) {
QKeyEvent* e = static_cast<QKeyEvent*>(event);
if (e->key() == Qt::Key_Up && _currentPosition < static_cast<int>(_history.size())-1) {
++_currentPosition;
_editCommand->setText(_history[_currentPosition]);
return true;
}
if (e->key() == Qt::Key_Down && _currentPosition >= 0) {
--_currentPosition;
if (_currentPosition >= 0)
_editCommand->setText(_history[_currentPosition]);
else
_editCommand->setText(tr(""));
return true;
}
}
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));
}
}
}
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//