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

Merge remote-tracking branch 'remotes/origin/refactor-pipeline-concept' into development

parents 37886930 73cd3ed8
......@@ -28,24 +28,6 @@
// ================================================================================================
#include "application/campvisapplication.h"
#include "modules/advancedusvis/pipelines/advancedusvis.h"
#include "modules/advancedusvis/pipelines/cmbatchgeneration.h"
#include "modules/vis/pipelines/ixpvdemo.h"
#include "modules/vis/pipelines/dvrvis.h"
#include "modules/vis/pipelines/volumerendererdemo.h"
#include "modules/vis/pipelines/volumeexplorerdemo.h"
#include "modules/vis/pipelines/slicevis.h"
#ifdef HAS_KISSCL
#include "modules/opencl/pipelines/openclpipeline.h"
#endif
#ifdef CAMPVIS_HAS_MODULE_SCR_MSK
#include "modules/scr_msk/pipelines/uscompounding.h"
#endif
#ifdef CAMPVIS_HAS_MODULE_COLUMBIA
#include "modules/columbia/pipelines/columbia1.h"
#endif
#ifdef Q_WS_X11
#include <X11/Xlib.h>
......@@ -66,27 +48,6 @@ int main(int argc, char** argv) {
#endif
CampVisApplication app(argc, argv);
// app.addVisualizationPipeline("Advanced Ultrasound Visualization", new AdvancedUsVis());
// app.addVisualizationPipeline("Confidence Map Generation", new CmBatchGeneration());
// app.addVisualizationPipeline("IXPV", new IxpvDemo());
app.addVisualizationPipeline("SliceVis", new SliceVis());
// app.addVisualizationPipeline("DVRVis", new DVRVis());
// app.addVisualizationPipeline("VolumeRendererDemo", new VolumeRendererDemo());
app.addVisualizationPipeline("VolumeExplorerDemo", new VolumeExplorerDemo());
#ifdef HAS_KISSCL
// app.addVisualizationPipeline("DVR with OpenCL", new OpenCLPipeline());
#endif
#ifdef CAMPVIS_HAS_MODULE_SCR_MSK
app.addVisualizationPipeline("US Compounding", new UsCompounding());
#endif
#ifdef CAMPVIS_HAS_MODULE_COLUMBIA
app.addVisualizationPipeline("Columbia", new Columbia1());
#endif
app.init();
int toReturn = app.run();
app.deinit();
......
......@@ -47,9 +47,10 @@
#include "application/gui/mainwindow.h"
#include "core/tools/opengljobprocessor.h"
#include "core/tools/simplejobprocessor.h"
#include "core/tools/stringutils.h"
#include "core/tools/quadrenderer.h"
#include "core/pipeline/abstractpipeline.h"
#include "core/pipeline/visualizationpipeline.h"
#include "modules/pipelinefactory.h"
namespace campvis {
......@@ -78,17 +79,29 @@ namespace campvis {
tgtAssert(_initialized == false, "Destructing initialized CampVisApplication, deinitialize first!");
// delete everything in the right order:
for (std::vector< std::pair<VisualizationPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) {
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<DataContainer*>::iterator it = _dataContainers.begin(); it != _dataContainers.end(); ++it) {
delete *it;
}
}
void CampVisApplication::init() {
tgtAssert(_initialized == false, "Tried to initialize CampVisApplication twice.");
// parse argument list and create pipelines
QStringList pipelinesToAdd = this->arguments();
for (int i = 1; i < pipelinesToAdd.size(); ++i) {
DataContainer* dc = createAndAddDataContainer("DataContainer #" + StringUtils::toString(_dataContainers.size() + 1));
AbstractPipeline* p = PipelineFactory::getRef().createPipeline(pipelinesToAdd[i].toStdString(), dc);
if (p != 0)
addPipeline(pipelinesToAdd[i].toStdString(), p);
}
// Init TGT
tgt::InitFeature::Features featureset = tgt::InitFeature::ALL;
tgt::init(featureset);
......@@ -167,7 +180,7 @@ namespace campvis {
}
// Now init painters:
for (std::vector< std::pair<VisualizationPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) {
for (std::vector< std::pair<AbstractPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) {
it->second->init();
}
......@@ -190,7 +203,7 @@ namespace campvis {
}
// Now deinit painters:
for (std::vector< std::pair<VisualizationPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) {
for (std::vector< std::pair<AbstractPipeline*, CampVisPainter*> >::iterator it = _visualizations.begin(); it != _visualizations.end(); ++it) {
it->second->deinit();
}
......@@ -235,33 +248,57 @@ namespace campvis {
return toReturn;
}
void CampVisApplication::addPipeline(AbstractPipeline* pipeline) {
tgtAssert(_initialized == false, "Adding pipelines after initialization is currently not supported.");
void CampVisApplication::addPipeline(const std::string& name, AbstractPipeline* pipeline) {
tgtAssert(pipeline != 0, "Pipeline must not be 0.");
_pipelines.push_back(pipeline);
// if CAMPVis is already fully initialized, we need to temporarily shut down its
// OpenGL job processor, since we need to create a new context.
if (_initialized) {
GLJobProc.pause();
{
tgt::QtThreadedCanvas* canvas = CtxtMgr.createContext(name, "CAMPVis", tgt::ivec2(512, 512));
tgt::GLContextScopedLock lock(canvas->getContext());
addPipelineImpl(canvas, name, pipeline);
}
GLJobProc.resume();
}
else {
tgt::QtThreadedCanvas* canvas = CtxtMgr.createContext(name, "CAMPVis", tgt::ivec2(512, 512));
addPipelineImpl(canvas, name, pipeline);
}
s_PipelinesChanged();
}
void CampVisApplication::addVisualizationPipeline(const std::string& name, VisualizationPipeline* vp) {
tgtAssert(_initialized == false, "Adding pipelines after initialization is currently not supported.");
tgtAssert(vp != 0, "Pipeline must not be 0.");
// create canvas and painter for the VisPipeline and connect all together
tgt::QtThreadedCanvas* canvas = CtxtMgr.createContext(name, "CAMPVis", tgt::ivec2(512, 512));
void CampVisApplication::addPipelineImpl(tgt::QtThreadedCanvas* canvas, const std::string& name, AbstractPipeline* pipeline) {
// create canvas and painter for the pipeline and connect all together
GLJobProc.registerContext(canvas);
_mainWindow->addVisualizationPipelineWidget(name, canvas);
canvas->init();
CampVisPainter* painter = new CampVisPainter(canvas, vp);
CampVisPainter* painter = new CampVisPainter(canvas, pipeline);
canvas->setPainter(painter, false);
pipeline->setCanvas(canvas);
_visualizations.push_back(std::make_pair(vp, painter));
_visualizations.push_back(std::make_pair(pipeline, painter));
_pipelines.push_back(pipeline);
vp->setCanvas(canvas);
addPipeline(vp);
if (_initialized) {
LGL_ERROR;
pipeline->init();
LGL_ERROR;
painter->init();
LGL_ERROR;
}
CtxtMgr.releaseCurrentContext();
_mainWindow->addVisualizationPipelineWidget(name, canvas);
// enable pipeline and invalidate all processors
pipeline->setEnabled(true);
for (std::vector<AbstractProcessor*>::const_iterator it = pipeline->getProcessors().begin(); it != pipeline->getProcessors().end(); ++it) {
(*it)->invalidate(AbstractProcessor::INVALID_RESULT);
}
}
void CampVisApplication::registerDockWidget(Qt::DockWidgetArea area, QDockWidget* dock) {
......@@ -270,4 +307,12 @@ namespace campvis {
_mainWindow->addDockWidget(area, dock);
}
DataContainer* CampVisApplication::createAndAddDataContainer(const std::string& name) {
DataContainer* dc = new DataContainer(name);
_dataContainers.push_back(dc);
s_DataContainersChanged();
return dc;
}
}
......@@ -38,6 +38,8 @@
#include <utility>
#include <vector>
#include "core/datastructures/datacontainer.h"
namespace tgt {
class QtThreadedCanvas;
}
......@@ -46,7 +48,6 @@ namespace campvis {
class AbstractPipeline;
class MainWindow;
class CampVisPainter;
class VisualizationPipeline;
/**
* The CampVisApplication class wraps Pipelines, Evaluators and Painters all together and takes
......@@ -89,25 +90,14 @@ namespace campvis {
void deinit();
/**
* Adds a pipeline which doesn't need visualization (OpenGL) support.
* Each pipeline will automatically get its own evaluator.
*
* \note If you want to add a pipeline that needs a valid OpenGL context, use
* addVisualizationPipeline() instead.
* \param pipeline Pipeline to add, must not need OpenGL support.
*/
void addPipeline(AbstractPipeline* pipeline);
/**
* Adds a visualization pipeline (i.e. a pipeline that needs a OpenGL context).
* For each added pipeline, an OpenGL context will be created (for the evaluation
* and rendering).
* Adds a pipeline to this CAMPVis application.
* Each pipeline will automatically get its own OpenGL context, the corresponding CampvisPainter
* and all necessary connections will be created.
*
* \note You do \b not need to call addPipeline.
* \param name Name of the OpenGL context to create for the pipeline.
* \param vp VisualizationPipeline to add.
* \param vp AbstractPipeline to add.
*/
void addVisualizationPipeline(const std::string& name, VisualizationPipeline* vp);
void addPipeline(const std::string& name, AbstractPipeline* pipeline);
/**
* Adds a dock widget to the main window.
......@@ -125,14 +115,30 @@ namespace campvis {
*/
int run();
/**
* Creates a new DataContainer with the given name.
* \param name Name of the new DataContainer
* \return The newly created DataContainer
*/
DataContainer* createAndAddDataContainer(const std::string& name);
/// 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;
private:
/// All pipelines (incuding VisualizationPipelines)
void addPipelineImpl(tgt::QtThreadedCanvas* canvas, const std::string& name, AbstractPipeline* pipeline);
/// All pipelines
std::vector<AbstractPipeline*> _pipelines;
/// All visualisations (i.e. VisualizationPipelines with their corresponding painters/canvases)
std::vector< std::pair<VisualizationPipeline*, CampVisPainter*> > _visualizations;
/// All visualisations (i.e. Pipelines with their corresponding painters/canvases)
std::vector< std::pair<AbstractPipeline*, CampVisPainter*> > _visualizations;
/// All DataContainers
std::vector<DataContainer*> _dataContainers;
/// A local OpenGL context used for initialization
tgt::QtThreadedCanvas* _localContext;
......
......@@ -41,7 +41,7 @@
#include "core/datastructures/imagedata.h"
#include "core/datastructures/renderdata.h"
#include "core/pipeline/visualizationpipeline.h"
#include "core/pipeline/abstractpipeline.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
#include "core/tools/quadrenderer.h"
......@@ -49,7 +49,7 @@
namespace campvis {
const std::string CampVisPainter::loggerCat_ = "CAMPVis.core.CampVisPainter";
CampVisPainter::CampVisPainter(tgt::GLCanvas* canvas, VisualizationPipeline* pipeline)
CampVisPainter::CampVisPainter(tgt::GLCanvas* canvas, AbstractPipeline* pipeline)
: Runnable()
, tgt::Painter(canvas)
, _pipeline(0)
......@@ -97,7 +97,7 @@ namespace campvis {
glViewport(0, 0, size.x, size.y);
// try get Data
DataContainer::ScopedTypedData<RenderData> rd(_pipeline->getDataContainer(), _pipeline->getRenderTargetID());
ScopedTypedData<RenderData> rd(_pipeline->getDataContainer(), _pipeline->getRenderTargetID());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (rd != 0) {
// activate shader
......@@ -175,7 +175,7 @@ namespace campvis {
}
}
void CampVisPainter::setPipeline(VisualizationPipeline* pipeline) {
void CampVisPainter::setPipeline(AbstractPipeline* pipeline) {
tgtAssert(pipeline != 0, "The given pipeline must not be 0.");
if (_pipeline != 0) {
_pipeline->s_renderTargetChanged.disconnect(this);
......
......@@ -44,16 +44,16 @@ namespace tgt {
}
namespace campvis {
class VisualizationPipeline;
class AbstractPipeline;
/**
* Painter class for CAMPVis, rendering the render target of a VisualizationPipeline.
* Painter class for CAMPVis, rendering the render target of an AbstractPipeline.
* This painter implements Runnable, hence, it runs in its own thread and the associated canvas
* must be of type QtThreadedCanvas.
* Rendering is implemented using condidional wait - hence the canvas is only updated when
* \a pipeline emits the s_RenderTargetChanged signal.
*
* \sa Runnable, VisualizationPipeline
* \sa Runnable, AbstractPipeline
*/
class CampVisPainter : public Runnable, public tgt::Painter, public sigslot::has_slots<> {
public:
......@@ -62,7 +62,7 @@ namespace campvis {
* \param canvas Canvas to render on
* \param pipeline Pipeline to render
*/
CampVisPainter(tgt::GLCanvas* canvas, VisualizationPipeline* pipeline);
CampVisPainter(tgt::GLCanvas* canvas, AbstractPipeline* pipeline);
/**
* Destructor, stops and waits for the rendering thread if it's still running.
......@@ -106,7 +106,7 @@ namespace campvis {
* Pipeline with the render target to render.
* \param pipeline Pipeline to render
*/
void setPipeline(VisualizationPipeline* pipeline);
void setPipeline(AbstractPipeline* pipeline);
/**
* Slot being notified when the pipeline's render target changed.
......@@ -121,7 +121,7 @@ namespace campvis {
static const std::string loggerCat_;
VisualizationPipeline* _pipeline; ///< Pipeline to render
AbstractPipeline* _pipeline; ///< Pipeline to render
tgt::Shader* _copyShader; ///< Shader for copying the render target to the framebuffer.
tbb::atomic<bool> _dirty; ///< Flag whether render result is dirty and needs to be rerendered.
std::condition_variable _renderCondition; ///< conditional wait condition for rendering
......
......@@ -35,18 +35,25 @@
#include "application/gui/datacontainerinspectorcanvas.h"
#include "application/gui/qtdatahandle.h"
#include "application/gui/visualizationpipelinewrapper.h"
#include "core/datastructures/datacontainer.h"
#include "core/pipeline/abstractpipeline.h"
#include "core/pipeline/abstractprocessor.h"
#include "core/tools/stringutils.h"
#include "modules/pipelinefactory.h"
#include <QMdiSubWindow>
#include <QScrollBar>
namespace campvis {
MainWindow::MainWindow(CampVisApplication* application)
: QMainWindow()
, _application(application)
, _mdiArea(0)
, _containerWidget(0)
, _cbPipelineFactory(0)
, _btnPipelineFactory(0)
, _pipelineWidget(0)
, _propCollectionWidget(0)
, _dcInspectorWidget(0)
......@@ -55,6 +62,7 @@ namespace campvis {
, _btnShowDataContainerInspector(0)
, _selectedPipeline(0)
, _selectedProcessor(0)
, _selectedDataContainer(0)
, _logViewer(0)
{
tgtAssert(_application != 0, "Application must not be 0.");
......@@ -64,6 +72,7 @@ namespace campvis {
MainWindow::~MainWindow() {
_application->s_PipelinesChanged.disconnect(this);
_application->s_DataContainersChanged.disconnect(this);
delete _dcInspectorWidget;
}
......@@ -81,9 +90,24 @@ namespace campvis {
_mdiArea->tileSubWindows();
setCentralWidget(_mdiArea);
_containerWidget = new QWidget(this);
QGridLayout* _cwLayout = new QGridLayout(_containerWidget);
_cbPipelineFactory = new QComboBox(_containerWidget);
std::vector<std::string> registeredPipelines = PipelineFactory::getRef().getRegisteredPipelines();
for (std::vector<std::string>::const_iterator it = registeredPipelines.begin(); it != registeredPipelines.end(); ++it)
_cbPipelineFactory->addItem(QString::fromStdString(*it));
_cwLayout->addWidget(_cbPipelineFactory, 0, 0);
_btnPipelineFactory = new QPushButton("Add Pipeline", _containerWidget);
_cwLayout->addWidget(_btnPipelineFactory, 0, 1);
_pipelineWidget = new PipelineTreeWidget();
_pipelineWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
ui.pipelineTreeDock->setWidget(_pipelineWidget);
_containerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
_cwLayout->addWidget(_pipelineWidget, 1, 0, 1, 2);
_containerWidget->setLayout(_cwLayout);
ui.pipelineTreeDock->setWidget(_containerWidget);
_pipelinePropertiesScrollArea = new QScrollArea();
_pipelinePropertiesScrollArea->setWidgetResizable(true);
......@@ -115,8 +139,8 @@ namespace campvis {
_dcInspectorWidget = new DataContainerInspectorWidget();
connect(
this, SIGNAL(updatePipelineWidget(const std::vector<AbstractPipeline*>&)),
_pipelineWidget, SLOT(update(const std::vector<AbstractPipeline*>&)));
this, SIGNAL(updatePipelineWidget(const std::vector<DataContainer*>&, const std::vector<AbstractPipeline*>&)),
_pipelineWidget, SLOT(update(const std::vector<DataContainer*>&, const std::vector<AbstractPipeline*>&)));
connect(
_pipelineWidget, SIGNAL(clicked(const QModelIndex&)),
this, SLOT(onPipelineWidgetItemClicked(const QModelIndex&)));
......@@ -129,7 +153,12 @@ namespace campvis {
connect(
_btnShowDataContainerInspector, SIGNAL(clicked()),
this, SLOT(onBtnShowDataContainerInspectorClicked()));
connect(
_btnPipelineFactory, SIGNAL(clicked()),
this, SLOT(onBtnPipelineFactoryClicked()));
_application->s_PipelinesChanged.connect(this, &MainWindow::onPipelinesChanged);
_application->s_DataContainersChanged.connect(this, &MainWindow::onDataContainersChanged);
}
bool MainWindow::eventFilter(QObject* watched, QEvent* event) {
......@@ -142,38 +171,51 @@ namespace campvis {
}
void MainWindow::onPipelinesChanged() {
emit updatePipelineWidget(_application->_pipelines);
emit updatePipelineWidget(_application->_dataContainers, _application->_pipelines);
}
void MainWindow::onDataContainersChanged() {
emit updatePipelineWidget(_application->_dataContainers, _application->_pipelines);
}
void MainWindow::onPipelineWidgetItemClicked(const QModelIndex& index) {
if (index.isValid()) {
// Yak, this is so ugly - another reason why GUI programming sucks...
QVariant item = index.data(Qt::UserRole);
HasPropertyCollection* ptr = static_cast<HasPropertyCollection*>(item.value<void*>());
if (item.isValid()) {
HasPropertyCollection* ptr = static_cast<HasPropertyCollection*>(item.value<void*>());
if (AbstractPipeline* pipeline = dynamic_cast<AbstractPipeline*>(ptr)) {
_selectedPipeline = pipeline;
_selectedProcessor = 0;
}
else if (AbstractProcessor* processor = dynamic_cast<AbstractProcessor*>(ptr)) {
_selectedProcessor = processor;
QVariant parentItem = index.parent().data(Qt::UserRole);
HasPropertyCollection* pptr = static_cast<HasPropertyCollection*>(parentItem.value<void*>());
if (AbstractPipeline* pipeline = dynamic_cast<AbstractPipeline*>(pptr)) {
if (AbstractPipeline* pipeline = dynamic_cast<AbstractPipeline*>(ptr)) {
_selectedPipeline = pipeline;
_selectedProcessor = 0;
_selectedDataContainer = &pipeline->getDataContainer();
}
else if (AbstractProcessor* processor = dynamic_cast<AbstractProcessor*>(ptr)) {
_selectedProcessor = processor;
QVariant parentItem = index.parent().data(Qt::UserRole);
HasPropertyCollection* pptr = static_cast<HasPropertyCollection*>(parentItem.value<void*>());
if (AbstractPipeline* pipeline = dynamic_cast<AbstractPipeline*>(pptr)) {
_selectedPipeline = pipeline;
_selectedDataContainer = &pipeline->getDataContainer();
}
}
}
emit updatePropCollectionWidget(ptr, &_selectedPipeline->getDataContainer());
emit updatePropCollectionWidget(ptr, &_selectedPipeline->getDataContainer());
}
else {
emit updatePropCollectionWidget(0, 0);
_selectedDataContainer = 0;
}
}
else {
emit updatePropCollectionWidget(0, 0);
_selectedDataContainer = 0;
}
}
QSize MainWindow::sizeHint() const {
return QSize(800, 450);
return QSize(1000, 600);
}
void MainWindow::onBtnExecuteClicked() {
......@@ -239,4 +281,14 @@ namespace campvis {
return dockWidget;
}
void MainWindow::onBtnPipelineFactoryClicked() {
std::string name = this->_cbPipelineFactory->currentText().toStdString();
DataContainer* dc = _selectedDataContainer;
if (dc == 0) {
dc = _application->createAndAddDataContainer("DataContainer #" + StringUtils::toString(_application->_dataContainers.size() + 1));
}
AbstractPipeline* p = PipelineFactory::getRef().createPipeline(name, dc);
_application->addPipeline(name, p);
}
}
......@@ -42,6 +42,7 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QComboBox>
#include <QMdiArea>
#include <QScrollArea>
#include <vector>
......@@ -80,11 +81,6 @@ namespace campvis {
*/
void deinit();
/**
* Slot to be called by the application when its collection of pipelines has changed.
*/
void onPipelinesChanged();
/**
* Size hint for the default window size
* \return QSize(800, 450)
......@@ -108,7 +104,8 @@ namespace campvis {
signals:
/// Qt signal for updating the PipelineWidget.
void updatePipelineWidget(const std::vector<AbstractPipeline*>&);
void updatePipelineWidget(const std::vector<DataContainer*>&, const std::vector<AbstractPipeline*>&);
/// Qt signal for updating the PropertyCollectionWidget
void updatePropCollectionWidget(HasPropertyCollection*, DataContainer*);
......@@ -129,7 +126,22 @@ namespace campvis {
*/
void onBtnShowDataContainerInspectorClicked();
/// Slot to be called when _btnPipelineFactory was clicked;
void onBtnPipelineFactoryClicked();
private:
/**
* Slot to be called by the application when its collection of pipelines has changed.
*/
void onPipelinesChanged();
/**
* Slot to be called by the application when its collection of DataContainers has changed.
*/
void onDataContainersChanged();
/**
* Setup Qt GUI stuff
*/
......@@ -152,6 +164,9 @@ namespace campvis {
CampVisApplication* _application; ///< Pointer to the application hosting the whole stuff
QMdiArea* _mdiArea; ///< MDI area (the window's central widget)
QWidget* _containerWidget; ///< Widget to manage the app's DataContainers and pipelines
QComboBox* _cbPipelineFactory; ///< Combobox for selecting the Pipelines from the PipelineFactory
QPushButton* _btnPipelineFactory; ///< Button to add a Pipeline from the factory
PipelineTreeWidget* _pipelineWidget; ///< Widget for browsing the active pipelines
QWidget* _pipelinePropertiesWidget; ///< Widget showing the selected pipeline's properties
QScrollArea* _pipelinePropertiesScrollArea; ///< Scroll area for _pipelinePropertiesWidget
......@@ -164,6 +179,8 @@ namespace campvis {
AbstractPipeline* _selectedPipeline; ///< currently selected pipeline
AbstractProcessor* _selectedProcessor; ///< currently selected processor
DataContainer* _selectedDataContainer; ///< currently selected DataContainer
LogViewerWidget* _logViewer; ///< Widget displaying log messages
std::vector<QDockWidget*> _primaryDocks; ///< Docks located in top docking area of the main window
};
......
......@@ -45,6 +45,38 @@ namespace campvis {
// = TreeModel items ==============================================================================
DataContainerTreeItem::DataContainerTreeItem(DataContainer* dc, TreeItem* parent)
: TreeItem(parent)
, _dataContainer(dc)
{
tgtAssert(_dataContainer != 0, "Pipeline must not be 0.");