16.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

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)
ENDIF()
IF(CAMPVIS_BUILD_LIB_LUA)
IF(WIN32)
# 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)
ENDIF(WIN32)
ADD_SUBDIRECTORY(ext/lua EXCLUDE_FROM_ALL)
......@@ -69,6 +71,17 @@ ENDIF()
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)
LIST(APPEND CampvisGlobalDefinitions "-DCAMPVIS_HAS_SCRIPTING")
ENDIF()
......
......@@ -38,6 +38,7 @@ SET(CampvisApplicationToBeMocced
gui/qtdatahandle.h
gui/logviewerwidget.h
gui/loghighlighter.h
gui/scriptingwidget.h
gui/mdi/mdidockarea.h
gui/mdi/mdidockablewindow.h
gui/mdi/mdidockedwindow.h
......
......@@ -59,6 +59,7 @@ namespace campvis {
, _localContext(0)
, _mainWindow(0)
, _errorTexture(nullptr)
, _luaVmState(nullptr)
, _initialized(false)
, _argc(argc)
, _argv(argv)
......@@ -77,11 +78,9 @@ namespace campvis {
tgtAssert(_initialized == false, "Destructing initialized CampVisApplication, deinitialize first!");
// delete everything in the right order:
for (std::vector< std::pair<AbstractPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) {
delete it->second;
}
for (std::vector<AbstractPipeline*>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
delete *it;
for (std::vector<PipelineRecord>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
delete it->_painter;
delete it->_pipeline;
}
for (std::vector<DataContainer*>::iterator it = _dataContainers.begin(); it != _dataContainers.end(); ++it) {
delete *it;
......@@ -147,6 +146,29 @@ namespace campvis {
tgt::TextureReaderTga trt;
_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
QStringList pipelinesToAdd = this->arguments();
for (int i = 1; i < pipelinesToAdd.size(); ++i) {
......@@ -170,14 +192,10 @@ namespace campvis {
delete _errorTexture;
// Deinit pipeline first
for (std::vector<AbstractPipeline*>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
(*it)->deinit();
}
// Now deinit painters:
for (std::vector< std::pair<AbstractPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) {
it->second->deinit();
// Deinit pipeline and painter first
for (std::vector<PipelineRecord>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
it->_pipeline->deinit();
it->_painter->deinit();
}
_mainWindow->deinit();
......@@ -227,8 +245,8 @@ namespace campvis {
pipeline->setCanvas(canvas);
painter->setErrorTexture(_errorTexture);
_visualizations.push_back(std::make_pair(pipeline, painter));
_pipelines.push_back(pipeline);
PipelineRecord pr = { pipeline, painter };
_pipelines.push_back(pr);
_mainWindow->addVisualizationPipelineWidget(name, canvas);
......@@ -238,6 +256,14 @@ namespace campvis {
makeJobOnHeap<CampVisApplication, tgt::GLCanvas*, AbstractPipeline*>(this, &CampVisApplication::initGlContextAndPipeline, canvas, pipeline),
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();
}
......@@ -284,8 +310,8 @@ namespace campvis {
LINFO("Rebuilding shaders from file successful.");
}
for (std::vector<AbstractPipeline*>::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<PipelineRecord>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
for (std::vector<AbstractProcessor*>::const_iterator pit = it->_pipeline->getProcessors().begin(); pit != it->_pipeline->getProcessors().end(); ++pit) {
if (VisualizationProcessor* tester = dynamic_cast<VisualizationProcessor*>(*pit)) {
tester->invalidate(AbstractProcessor::INVALID_RESULT);
}
......@@ -293,5 +319,11 @@ namespace campvis {
}
}
#ifdef CAMPVIS_HAS_SCRIPTING
LuaVmState* CampVisApplication::getLuaVmState() {
return _luaVmState;
}
#endif
}
......@@ -35,6 +35,10 @@
#include "core/datastructures/datacontainer.h"
#ifdef CAMPVIS_HAS_SCRIPTING
#include "scripting/glue/luavmstate.h"
#endif
namespace tgt {
class GLCanvas;
class QtThreadedCanvas;
......@@ -45,6 +49,7 @@ namespace campvis {
class AbstractPipeline;
class MainWindow;
class CampVisPainter;
class LuaVmState;
/**
* The CampVisApplication class wraps Pipelines, Evaluators and Painters all together and takes
......@@ -62,6 +67,12 @@ namespace campvis {
friend class MainWindow;
public:
/// Record storing a pipeline together with its painter and Lua VM state.
struct PipelineRecord {
AbstractPipeline* _pipeline;
CampVisPainter* _painter;
};
/**
* Creates a new CampVisApplication.
* \param argc number of passed arguments
......@@ -124,6 +135,15 @@ namespace campvis {
*/
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.
sigslot::signal0<> s_PipelinesChanged;
......@@ -135,9 +155,7 @@ namespace campvis {
/// All pipelines
std::vector<AbstractPipeline*> _pipelines;
/// All visualisations (i.e. Pipelines with their corresponding painters/canvases)
std::vector< std::pair<AbstractPipeline*, CampVisPainter*> > _visualizations;
std::vector<PipelineRecord> _pipelines;
/// All DataContainers
std::vector<DataContainer*> _dataContainers;
......@@ -153,8 +171,12 @@ namespace campvis {
/// Main window hosting GUI stuff
MainWindow* _mainWindow;
/// Error texture to show if there is no output found
tgt::Texture* _errorTexture;
/// the global LuaVmState of this application
LuaVmState* _luaVmState;
/// Flag, whether CampVisApplication was correctly initialized
bool _initialized;
......
......@@ -58,6 +58,7 @@ namespace campvis {
, _selectedProcessor(0)
, _selectedDataContainer(0)
, _logViewer(0)
, _scriptingConsoleWidget(nullptr)
{
tgtAssert(_application != 0, "Application must not be 0.");
ui.setupUi(this);
......@@ -130,6 +131,12 @@ namespace campvis {
_logViewer = new LogViewerWidget(this);
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();
this->populateMainMenu();
......@@ -185,11 +192,17 @@ namespace campvis {
}
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() {
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) {
......@@ -263,6 +276,9 @@ namespace campvis {
_dcInspectorWidget->init();
_logViewer->init();
if (_scriptingConsoleWidget)
_scriptingConsoleWidget->init();
}
void MainWindow::deinit() {
......@@ -270,6 +286,9 @@ namespace campvis {
_dcInspectorWidget->deinit();
_logViewer->deinit();
if (_scriptingConsoleWidget)
_scriptingConsoleWidget->deinit();
}
void MainWindow::addVisualizationPipelineWidget(const std::string& name, QWidget* canvas) {
......@@ -311,4 +330,12 @@ namespace campvis {
_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 @@
#include "application/gui/pipelinetreewidget.h"
#include "application/gui/properties/propertycollectionwidget.h"
#include "application/gui/logviewerwidget.h"
#include "application/gui/scriptingwidget.h"
#include "application/tools/qtexteditlog.h"
#include "application/ui_mainwindow.h"
......@@ -105,7 +106,7 @@ namespace campvis {
/// Qt signal for updating the PropertyCollectionWidget
void updatePropCollectionWidget(HasPropertyCollection*, DataContainer*);
private slots:
public slots:
/**
* Slot to be called by the PipelineWidget when the selected item changes.
* \param index Index of the selected item
......@@ -128,6 +129,9 @@ namespace campvis {
/// Slot to be called when all shaders shall be rebuilt.
void onRebuildShadersClicked();
/// Slot to be called when a Lua command shall be executed.
void onLuaCommandExecuted(const QString& cmd);
private:
/**
......@@ -185,6 +189,8 @@ namespace campvis {
DataContainer* _selectedDataContainer; ///< currently selected DataContainer
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
};
}
......
// ================================================================================================
//
// 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".
//
// 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.
//
// ================================================================================================
#ifndef SCRIPTINGWIDGET_H__
#define SCRIPTINGWIDGET_H__
#include "sigslot/sigslot.h"
#include "tgt/logmanager.h"
#include "tgt/painter.h"
#include "tbb/mutex.h"
#include "application/tools/bufferinglog.h"
#include "application/gui/loghighlighter.h"
#include <deque>
#include <QComboBox>
#include <QLabel>
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QString>
#include <QPushButton>
#include <QLineEdit>
#include <QTextEdit>
namespace campvis {
/**
* 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 ScriptingWidget.
* \param parent Parent Qt widget, may be 0 (default)
*/
explicit ScriptingWidget(QWidget* parent = 0);
/**
* Destructor.
*/
~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
*/
void setupGUI();
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
*
* This function adds the specified message to the log viewer's message cache and
* displays it.
*
* \param message message to append to the log viewer
*/
void appendMessage(const QString& message);
private slots:
/**
* Delete all messages from the log viewer
*/
void clearLog();
void execute();
signals:
void s_commandExecuted(const QString& cmd);
private:
QTextEdit* _consoleDisplay; ///< Text edit to hold the console output
QLineEdit* _editCommand; ///< Text field to enter Lua commands
QPushButton* _btnExecute; ///< Button to execute command
QPushButton* _btnClear; ///< Button to clear the console output
std::deque<QString> _history; ///< History of executed commands
int _currentPosition; ///< Current position in command history
};
}
#endif // SCRIPTINGWIDGET_H__
inspect ={
_VERSION = 'inspect.lua 2.0.0',
_URL = 'http://github.com/kikito/inspect.lua',
_DESCRIPTION = 'human-readable representations of tables',
_LICENSE = [[
MIT LICENSE
Copyright (c) 2013 Enrique García Cota
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF