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

Started working on fancy scripting support:

* Split up campvis-application executable into campvis-application library and campvis executable. This allows to create a Lua module for the stuff in campvis-application.
* Added Lua binding stub for campvis-application
* Revised LuaTable and it's offsprings to (almost) fully model the Lua table model
* Added MetatableLuaTable to model Lua's metatables
* LuaTable supports caching the current field state in a value map supporting lazy instantiation
* Added LuaTableTreeModel transforming the LuaTable structure into a QAbstractItemModel
* Extended ScriptingWidget to contain both a LuaTableTreeWidget containing a variable view as well as with a LuaCompleter automatically completing the typed Lua commands with the variables extracted from the lua state.

refs #643
parent 47f0f8c9
......@@ -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,7 +106,7 @@ IF(CAMPVIS_ENABLE_SCRIPTING)
LIST(APPEND CampvisMainLibs campvis-scripting)
ENDIF(CAMPVIS_ENABLE_SCRIPTING)
ADD_EXECUTABLE(campvis-application
ADD_LIBRARY(campvis-application
${CampvisApplicationSources} ${CampvisApplicationHeaders}
${CampvisApplicationMoc}
)
......@@ -111,11 +114,22 @@ ADD_DEFINITIONS(${CampvisGlobalDefinitions} ${CampvisModulesDefinitions} ${Campv
INCLUDE_DIRECTORIES(${CampvisGlobalIncludeDirs} ${CampvisModulesIncludeDirs})
TARGET_LINK_LIBRARIES(campvis-application ${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 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()
ADD_EXECUTABLE(campvis "campvis.cpp")
TARGET_LINK_LIBRARIES(campvis campvis-application ${CampvisMainLibs} ${CampvisGlobalExternalLibs} ${CampvisModulesExternalLibs} ${QT_LIBRARIES})
IF(CAMPVIS_DEPLOY_SHADERS)
LIST(APPEND CampvisShaderDirectories "application/data")
LIST(APPEND CampvisShaderDirectories "application/glsl")
......
// ================================================================================================
//
// 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 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
%include "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();
void addPipeline(const std::string& name, AbstractPipeline* pipeline);
DataContainer* createAndAddDataContainer(const std::string& name);
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 {
function application.newPipeline (name, o)
if not name then
error("A name must be provided when creating a new pipeline!")
end
o = o or {} -- create object if user does not provide one
setmetatable(o, {__index = instance})
return o
end
print("Module campvis-application loaded")
}
......@@ -49,7 +49,7 @@
#include "core/datastructures/imagerepresentationconverter.h"
#include "core/pipeline/visualizationprocessor.h"
#include "application/tools/qtjobprocessor.h"
#include "tools/qtjobprocessor.h"
#include <QApplication>
......@@ -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
}
......@@ -163,7 +168,7 @@ namespace campvis {
addPipeline(pipelinesToAdd[i].toStdString(), p);
}
}
_initialized = true;
}
......@@ -239,7 +244,6 @@ namespace campvis {
#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()))
......@@ -296,7 +300,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 +320,4 @@ namespace campvis {
}
}
......@@ -34,7 +34,7 @@
#include <vector>
#include "core/datastructures/datacontainer.h"
#include "application/applicationapi.h"
#ifdef CAMPVIS_HAS_SCRIPTING
#include "scripting/glue/luavmstate.h"
......@@ -65,7 +65,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:
......
// ================================================================================================
//
// 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/scriptingwidget.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);
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("[Metatable]");
//toReturn.push_back(".instance");
toReturn.push_back(".fn");
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());
size_t pos = 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(pos, textUnderCursor.length() - pos, completitionString);
setText(fullText);
}
}
// ================================================================================================
//
// 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 COMPLETINGLUALINEEDIT_H__
#define COMPLETINGLUALINEEDIT_H__
#include <QCompleter>
#include <QLineEdit>
#include "scripting/glue/luatable.h"
namespace campvis {
// FIXME: clean up, add documentation
class LuaCompleter : public QCompleter {
public:
LuaCompleter(LuaVmState* luaVmState, QWidget* parent);
virtual ~LuaCompleter();
virtual QStringList splitPath(const QString& path) const override;
private:
LuaVmState* _luaVmState;
};
class CompletingLuaLineEdit : public QLineEdit {
Q_OBJECT;
public:
CompletingLuaLineEdit(LuaVmState* luaVmState, QWidget* parent);
virtual ~CompletingLuaLineEdit();
void setCompleter(LuaCompleter* completer);
LuaCompleter* completer() const;
protected:
void keyPressEvent(QKeyEvent *e);
private slots:
void insertCompletion(QString completitionString);
private:
LuaCompleter* _completer;
};
}
#endif // COMPLETINGLUALINEEDIT_H__
<
// ================================================================================================
//
// 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 "luatablewidget.h"
#include "cgt/assert.h"
#include "application/gui/qtdatahandle.h"
#include "core/datastructures/datacontainer.h"
#include "core/datastructures/abstractdata.h"
#include "core/datastructures/dataseries.h"
#include "core/datastructures/lightsourcedata.h"
#include "core/datastructures/facegeometry.h"
#include "core/datastructures/meshgeometry.h"
#include "core/datastructures/indexedmeshgeometry.h"
#include "core/datastructures/multiindexedgeometry.h"
#include "core/datastructures/imageseries.h"
#include "core/datastructures/imagerepresentationdisk.h"
#include "core/datastructures/imagerepresentationlocal.h"
#include "core/datastructures/imagerepresentationgl.h"
#include "core/datastructures/renderdata.h"
#include "core/tools/stringutils.h"
#ifdef CAMPVIS_HAS_MODULE_COLUMBIA
#include "modules/columbia/datastructures/fiberdata.h"
#endif
#include "scripting/glue/luatable.h"
#include "scripting/glue/globalluatable.h"
#include "scripting/glue/metatableluatable.h"
#include "scripting/glue/regularluatable.h"
#include "scripting/glue/luavmstate.h"
#include <QHeaderView>
#include <QStringList>
namespace campvis {
namespace {
const int COLUMN_NAME = 0;
const int COLUMN_TYPE = 1;
const int COLUMN_VALUE = 2;
}
// = TreeModel items ==============================================================================
LuaTreeItem::LuaTreeItem(const std::string& name, int type, TreeItem* parent /*= nullptr*/) : TreeItem(parent)
, _name(name)
, _type(type)
{
}
QVariant LuaTreeItem::getData(int column, int role) const {
switch (role) {
case Qt::EditRole: // fallthrough
case Qt::DisplayRole:
if (column == COLUMN_NAME)
return QVariant(QString::fromStdString(_name));
else if (column == COLUMN_TYPE)
return QVariant(QString::fromStdString(lua_typename(0, _type)));
else if (column == COLUMN_VALUE)
return getValue();
else
return QVariant();
default:
return QVariant();
}
}
QString LuaTreeItem::getValue() const {
return QString("");
}
LuaTreeRootItem::LuaTreeRootItem(TreeItem* parent /*= 0*/)
: TreeItem(parent)
{}
// ================================================================================================
QVariant LuaTreeRootItem::getData(int column, int role) const {
if (role == Qt::DisplayRole) {
if (column == COLUMN_NAME)
return QVariant(QString("Name"));
else if (column == COLUMN_TYPE)
return QVariant(QString("Data Type"));
else if (column == COLUMN_VALUE)
return QVariant(QString("Value"));
}
return QVariant();
}
LuaTreeRootItem::~LuaTreeRootItem() {