Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

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

Merge branch 'fancy-scripting' into 'development'

Fancy scripting

See merge request !118
parents 47f0f8c9 112c3559
[submodule "ext/lua"]
path = ext/lua
url = https://github.com/LuaDist/lua
ignore = dirty
# = CAMPVis - Yet another medical visualization framework =====================
PROJECT(CAMPVis)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0 FATAL_ERROR)
CMAKE_MINIMUM_REQUIRED(VERSION 3.0.0 FATAL_ERROR)
SET(CAMPVIS_VERSION 1.0.0)
......@@ -51,30 +51,15 @@ ENDIF()
# build scripting targets when enabled
IF(CAMPVIS_ENABLE_SCRIPTING)
# build Lua from source
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)
# If the above command failed, provide a hint how to fix the problem
IF(NOT TARGET liblua)
MESSAGE(SEND_ERROR "Did you forget to run `git submodule update --init`?")
ENDIF(NOT TARGET liblua)
ADD_SUBDIRECTORY(ext/lua)
SET(LUA_DIR "${CampvisHome}/ext/lua")
# 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)
LIST(APPEND CampvisGlobalIncludeDirs "${LUA_DIR}/src" "${CMAKE_BINARY_DIR}/ext/lua")
LIST(APPEND CampvisGlobalExternalLibs "liblua")
LIST(APPEND CampvisGlobalDefinitions "-DCAMPVIS_HAS_SCRIPTING")
ADD_SUBDIRECTORY(scripting)
LIST(APPEND CampvisGlobalDefinitions "-DCAMPVIS_HAS_SCRIPTING")
ENDIF()
IF(CAMPVIS_BUILD_APPLICATION)
......
......@@ -5,7 +5,7 @@ INCLUDE(../cmake/commonconf.cmake)
MESSAGE(STATUS "Configuring CAMPVis-Application")
FILE(GLOB CampvisApplicationSources
*.cpp
campvisapplication.cpp
gui/*.cpp
gui/adjusterwidgets/*.cpp
gui/mdi/*.cpp
......@@ -14,7 +14,8 @@ FILE(GLOB CampvisApplicationSources
)
FILE(GLOB CampvisApplicationHeaders
*.h
applicationapi.h
campvisapplication.h
glsl/*.frag
glsl/*.vert
gui/*.h
......@@ -28,6 +29,7 @@ FILE(GLOB CampvisApplicationForms
)
SET(CampvisApplicationToBeMocced
gui/completinglualineedit.h
gui/mainwindow.h
gui/datacontainerinspectorcanvas.h
gui/datacontainerinspectorwidget.h
......@@ -38,6 +40,7 @@ SET(CampvisApplicationToBeMocced
gui/qtdatahandle.h
gui/logviewerwidget.h
gui/loghighlighter.h
gui/luatablewidget.h
gui/scriptingwidget.h
gui/workflowcontrollerwidget.h
gui/mdi/mdidockarea.h
......@@ -64,8 +67,8 @@ SET(CampvisApplicationToBeMocced
gui/properties/statuspropertywidget.h
gui/properties/transferfunctionpropertywidget.h
tools/bufferinglog.h
tools/qtjobprocessor.h
tools/qtexteditlog.h
tools/qtjobprocessor.h
)
......@@ -103,19 +106,31 @@ IF(CAMPVIS_ENABLE_SCRIPTING)
LIST(APPEND CampvisMainLibs campvis-scripting)
ENDIF(CAMPVIS_ENABLE_SCRIPTING)
ADD_EXECUTABLE(campvis-application
ADD_LIBRARY(campvis-application-lib
${CampvisApplicationSources} ${CampvisApplicationHeaders}
${CampvisApplicationMoc}
)
ADD_DEFINITIONS(${CampvisGlobalDefinitions} ${CampvisModulesDefinitions} ${CampvisApplicationDefinitions} ${QT_DEFINITIONS})
INCLUDE_DIRECTORIES(${CampvisGlobalIncludeDirs} ${CampvisModulesIncludeDirs})
TARGET_LINK_LIBRARIES(campvis-application ${CampvisMainLibs} ${CampvisGlobalExternalLibs} ${CampvisModulesExternalLibs} ${QT_LIBRARIES})
TARGET_LINK_LIBRARIES(campvis-application-lib ${CampvisMainLibs} ${CampvisGlobalExternalLibs} ${CampvisModulesExternalLibs} ${QT_LIBRARIES})
# if campvis-core is built as a shared library, CMake will define the following flag to instruct
# the code to export DLL symbols
SET_TARGET_PROPERTIES(campvis-application-lib PROPERTIES DEFINE_SYMBOL "CAMPVIS_APPLICATION_BUILD_DLL")
IF(CAMPVIS_GROUP_SOURCE_FILES)
DEFINE_SOURCE_GROUPS_FROM_SUBDIR(CampvisApplicationSources ${CampvisHome} "")
DEFINE_SOURCE_GROUPS_FROM_SUBDIR(CampvisApplicationHeaders ${CampvisHome} "")
ENDIF()
INSTALL(TARGETS campvis-application-lib DESTINATION exports EXPORT campvis-targets)
ADD_EXECUTABLE(campvis-application "campvis.cpp")
TARGET_LINK_LIBRARIES(campvis-application campvis-application-lib ${CampvisMainLibs} ${CampvisGlobalExternalLibs} ${CampvisModulesExternalLibs} ${QT_LIBRARIES})
IF(CAMPVIS_DEPLOY_SHADERS)
LIST(APPEND CampvisShaderDirectories "application/data")
LIST(APPEND CampvisShaderDirectories "application/glsl")
......
......@@ -2,7 +2,7 @@
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved,
// 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
......@@ -22,59 +22,28 @@
//
// ================================================================================================
#include "luapipeline.h"
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
#include "glue/globalluatable.h"
#include "glue/luavmstate.h"
namespace campvis {
LuaPipeline::LuaPipeline(const std::string name, std::string scriptPath, DataContainer* dc)
: AutoEvaluationPipeline(dc)
, _name(name)
, _scriptPath(scriptPath)
, _luaVmState(new LuaVmState())
, _pipelineTable(_luaVmState->getGlobalTable()->getTable("pipeline"))
{
// Let Lua know where CAMPVis modules are located
if (!_luaVmState->execString("package.cpath = '" CAMPVIS_LUA_MODS_PATH "'"))
return;
// Load CAMPVis' core Lua module to have SWIG glue for AutoEvaluationPipeline available
if (!_luaVmState->execString("require(\"campvis\")"))
return;
// Make this pipeline object available to the script so that it can configure it
if (!_luaVmState->injectGlobalObjectPointer(this, "campvis::AutoEvaluationPipeline *", "instance"))
return;
// Try executing the pipeline's Lua script
if (!_luaVmState->execFile(scriptPath))
return;
if (!_pipelineTable->isValid())
std::cerr << "No valid Lua pipeline found (global variable `pipeline` is not a table)" << std::endl;
else
_pipelineTable->callInstanceMethod("ctor");
}
LuaPipeline::~LuaPipeline() {
}
void LuaPipeline::init() {
AutoEvaluationPipeline::init();
_pipelineTable->callInstanceMethod("init");
}
void LuaPipeline::deinit() {
AutoEvaluationPipeline::deinit();
_pipelineTable->callInstanceMethod("deinit");
}
}
#ifndef APPLICATIONAPI_H__
#define APPLICATIONAPI_H__
#ifdef CAMPVIS_DYNAMIC_LIBS
#ifdef CAMPVIS_APPLICATION_BUILD_DLL
// building library -> export symbols
#ifdef WIN32
#define CAMPVIS_APPLICATION_API __declspec(dllexport)
#else
#define CAMPVIS_APPLICATION_API
#endif
#else
// including library -> import symbols
#ifdef WIN32
#define CAMPVIS_APPLICATION_API __declspec(dllimport)
#else
#define CAMPVIS_APPLICATION_API
#endif
#endif
#else
// building/including static library -> do nothing
#define CAMPVIS_APPLICATION_API
#endif
#endif // APPLICATIONAPI_H__
%module application
%include factory.i
%include std_pair.i
%include std_string.i
%include std_vector.i
%import "core/bindings/campvis.i"
%{
#include "application/campvisapplication.h"
#include "core/properties/allproperties.h"
#include "core/pipeline/abstractprocessor.h"
#include "core/pipeline/abstractworkflow.h"
#include "core/pipeline/autoevaluationpipeline.h"
%}
namespace campvis {
class CampVisApplication {
public:
CampVisApplication(int& argc, char** argv);
~CampVisApplication();
void init();
void deinit();
int run();
%apply SWIGTYPE *DISOWN {AbstractPipeline* pipeline};
void addPipeline(AbstractPipeline* pipeline);
DataContainer* createAndAddDataContainer(const std::string& name);
%clear AbstractPipeline* pipeline;
void rebuildAllShadersFromFiles();
void setPipelineVisibility(AbstractPipeline* pipeline, bool visibility);
/// Signal emitted when the collection of pipelines has changed.
sigslot::signal0 s_PipelinesChanged;
/// Signal emitted when the collection of DataContainers has changed.
sigslot::signal0 s_DataContainersChanged;
};
}
%luacode {
print("Module campvis-application loaded")
}
......@@ -49,12 +49,12 @@
#include "core/datastructures/imagerepresentationconverter.h"
#include "core/pipeline/visualizationprocessor.h"
#include "application/tools/qtjobprocessor.h"
#include "tools/qtjobprocessor.h"
#include <QApplication>
#ifdef CAMPVIS_HAS_SCRIPTING
#include "scripting/gen_pipelineregistration.h"
#include "scripting/glue/luavmstate.h"
#endif
namespace campvis {
......@@ -115,9 +115,11 @@ namespace campvis {
LERROR("Error setting up Lua VM.");
// Load CAMPVis' core Lua module to have SWIG glue for AutoEvaluationPipeline available
if (! _luaVmState->execString("require(\"cgt\")"))
LERROR("Error setting up Lua VM.");
if (! _luaVmState->execString("require(\"campvis\")"))
LERROR("Error setting up Lua VM.");
if (! _luaVmState->execString("require(\"cgt\")"))
if (! _luaVmState->execString("require(\"application\")"))
LERROR("Error setting up Lua VM.");
if (! _luaVmState->execString("pipelines = {}"))
......@@ -125,6 +127,9 @@ namespace campvis {
if (! _luaVmState->execString("inspect = require 'inspect'"))
LERROR("Error setting up Lua VM.");
if (! _luaVmState->injectGlobalObjectPointer(this, "campvis::CampVisApplication *", "application"))
LERROR("Could not inject the pipeline into the Lua VM.");
#endif
}
......@@ -142,7 +147,7 @@ namespace campvis {
std::vector<AbstractPipeline*> pipelines = w->getPipelines();
for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
addPipeline((*it)->getName(), *it);
addPipeline(*it);
}
_workflows.push_back(w);
......@@ -158,12 +163,12 @@ namespace campvis {
}
else {
DataContainer* dc = createAndAddDataContainer("DataContainer #" + StringUtils::toString(_dataContainers.size() + 1));
AbstractPipeline* p = PipelineFactory::getRef().createPipeline(pipelinesToAdd[i].toStdString(), dc);
AbstractPipeline* p = PipelineFactory::getRef().createPipeline(pipelinesToAdd[i].toStdString(), *dc);
if (p != nullptr)
addPipeline(pipelinesToAdd[i].toStdString(), p);
addPipeline(p);
}
}
_initialized = true;
}
......@@ -221,31 +226,29 @@ namespace campvis {
return toReturn;
}
void CampVisApplication::addPipeline(const std::string& name, AbstractPipeline* pipeline) {
void CampVisApplication::addPipeline(AbstractPipeline* pipeline) {
cgtAssert(pipeline != 0, "Pipeline must not be 0.");
// create canvas and painter for the pipeline and connect all together
cgt::QtThreadedCanvas* canvas = new cgt::QtThreadedCanvas("CAMPVis", cgt::ivec2(512, 512));
cgt::QtThreadedCanvas* canvas = new cgt::QtThreadedCanvas(pipeline->getName(), cgt::ivec2(512, 512));
canvas->init();
pipeline->setCanvas(canvas);
pipeline->getPipelinePainter()->setErrorTexture(_errorTexture);
_pipelines.push_back(pipeline);
_pipelineWindows[pipeline] = _mainWindow->addVisualizationPipelineWidget(name, canvas);
_pipelineWindows[pipeline] = _mainWindow->addVisualizationPipelineWidget(pipeline->getName(), canvas);
// initialize context (GLEW) and pipeline in OpenGL thread)
initGlContextAndPipeline(canvas, pipeline);
#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.");
if (! _luaVmState->injectObjectPointerToTableField(pipeline, "campvis::AutoEvaluationPipeline *", "pipelines", pipeline->getName()))
LERROR("Could not inject the pipeline into the Lua VM.");
_luaVmState->execString("inspect(pipelines)");
#endif
GLCtxtMgr.releaseContext(canvas, false);
......@@ -296,7 +299,7 @@ namespace campvis {
for (auto it = _pipelines.begin(); it != _pipelines.end(); ++it) {
for (auto pit = (*it)->getProcessors().cbegin(); pit != (*it)->getProcessors().cend(); ++pit) {
if (VisualizationProcessor* tester = dynamic_cast<VisualizationProcessor*>(*pit)) {
tester->invalidate(AbstractProcessor::INVALID_RESULT);
tester->invalidate(AbstractProcessor::INVALID_RESULT);
}
}
}
......@@ -316,3 +319,4 @@ namespace campvis {
}
}
......@@ -34,11 +34,7 @@
#include <vector>
#include "core/datastructures/datacontainer.h"
#ifdef CAMPVIS_HAS_SCRIPTING
#include "scripting/glue/luavmstate.h"
#endif
#include "application/applicationapi.h"
namespace cgt {
class GLCanvas;
......@@ -65,7 +61,7 @@ namespace campvis {
* 5. call deinit()
* 6. You can now safely destroy your CampVisApplication
*/
class CampVisApplication : public QApplication {
class CAMPVIS_APPLICATION_API CampVisApplication : public QApplication {
friend class MainWindow;
public:
......@@ -97,10 +93,9 @@ namespace campvis {
* Each pipeline will automatically get its own OpenGL context, the corresponding CampvisPainter
* and all necessary connections will be created.
*
* \param name Name of the OpenGL context to create for the pipeline.
* \param vp AbstractPipeline to add.
*/
void addPipeline(const std::string& name, AbstractPipeline* pipeline);
void addPipeline(AbstractPipeline* pipeline);
/**
* Adds a dock widget to the main window.
......
......@@ -26,6 +26,7 @@
#define DOUBLEADJUSTERWIDGET_H__
#include "abstractadjusterwidget.h"
#include "application/applicationapi.h"
namespace campvis {
/**
......@@ -34,7 +35,7 @@ namespace campvis {
* DoubleAdjusterWidget consists of a slider, which can be used to quickly change numeric values, and a spin box,
* which is better suited for precise adjustments.
*/
class DoubleAdjusterWidget : public AbstractAdjusterWidget<double> {
class CAMPVIS_APPLICATION_API DoubleAdjusterWidget : public AbstractAdjusterWidget<double> {
Q_OBJECT
......
......@@ -26,6 +26,7 @@
#define INTADJUSTERWIDGET_H__
#include "abstractadjusterwidget.h"
#include "application/applicationapi.h"
namespace campvis {
/**
......@@ -34,7 +35,7 @@ namespace campvis {
* IntAdjusterWidget consists of a slider, which can be used to quickly change integer values, and a spin box,
* which is better suited for precise adjustments.
*/
class IntAdjusterWidget : public AbstractAdjusterWidget<int> {
class CAMPVIS_APPLICATION_API IntAdjusterWidget : public AbstractAdjusterWidget<int> {
Q_OBJECT
public:
......
// ================================================================================================
//
// 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 "completinglualineedit.h"
#include "application/gui/luatablewidget.h"
#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QKeyEvent>
#include <QDirModel>
#include <QScrollBar>
namespace campvis {
LuaCompleter::LuaCompleter(LuaVmState* luaVmState, QWidget* parent)
: QCompleter(parent)
, _luaVmState(luaVmState)
{
auto luaTreeModel = new LuaTableTreeModel(this);
luaTreeModel->setData(_luaVmState, LuaTreeItem::COMPLETER_MODEL);
setModel(luaTreeModel);
}
LuaCompleter::~LuaCompleter() {
}
QStringList LuaCompleter::splitPath(const QString& path) const {
QStringList toReturn;
toReturn.push_back("[Global Variables]");
int start = 0;
for (int end = start; end < path.length(); ++end) {
if (path[end] == '.') {
toReturn.push_back(path.mid(start, end-start));
start = end+1;
}
if (path[end] == ':') {
toReturn.push_back(path.mid(start, end-start));
toReturn.push_back("[Methods]");
start = end+1;
}
}
toReturn.push_back(path.right(path.length() - start));
return toReturn;
}
// ================================================================================================
CompletingLuaLineEdit::CompletingLuaLineEdit(LuaVmState* luaVmState, QWidget* parent)
: QLineEdit(parent)
, _completer(nullptr)
{
setCompleter(new LuaCompleter(luaVmState, this));
}
CompletingLuaLineEdit::~CompletingLuaLineEdit() {
}
void CompletingLuaLineEdit::setCompleter(LuaCompleter* completer) {
if (_completer)
QObject::disconnect(_completer, 0, this, 0);
_completer = completer;
if (! _completer)
return;
_completer->setWidget(this);
_completer->setCompletionMode(QCompleter::PopupCompletion);
_completer->setCaseSensitivity(Qt::CaseInsensitive);
connect(_completer, SIGNAL(activated(QString)), this, SLOT(insertCompletion(QString)));
}
LuaCompleter* CompletingLuaLineEdit::completer() const {
return _completer;
}
void CompletingLuaLineEdit::keyPressEvent(QKeyEvent *e) {
if (_completer && _completer->popup()->isVisible()) {
// The following keys are forwarded by the completer to the widget
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
e->ignore();
return; // let the completer do default behavior
default:
break;
}
}
bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_Space); // CTRL+Space
if (!_completer || !isShortcut) // do not process the shortcut when we have a completer
QLineEdit::keyPressEvent(e);
const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
if (!_completer || (ctrlOrShift && e->text().isEmpty()))
return;
QString textUnderCursor = text().left(cursorPosition());
int pos = int(textUnderCursor.toStdString().find_last_of(" ()[]{}"));
QString completionPrefix = textUnderCursor.right(textUnderCursor.length() - pos - 1);
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 1)) {
_completer->popup()->hide();
return;
}
if (completionPrefix != _completer->completionPrefix()) {
_completer->setCompletionPrefix(completionPrefix);
_completer->popup()->setCurrentIndex(_completer->completionModel()->index(0, 0));
}
QRect cr = cursorRect();
cr.setWidth(_completer->popup()->sizeHintForColumn(0)
+ _completer->popup()->verticalScrollBar()->sizeHint().width());
_completer->complete(cr);
}
void CompletingLuaLineEdit::insertCompletion(QString completitionString) {
if (_completer->widget() != this)
return;
QString textUnderCursor = text().left(cursorPosition());
size_t pos = textUnderCursor.toStdString().find_last_of(" .:()[]{}");
if (pos == std::string::npos)
pos = 0;
else
pos += 1;
QString fullText = text();
fullText.replace(int(pos), textUnderCursor.length() - int(pos), completitionString);
setText(fullText);
}
}
......@@ -2,7 +2,7 @@
//
// This file is part of the CAMPVis Software Framework.