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

datacontainerinspectorwidget.cpp 21.3 KB
Newer Older
1
2
// ================================================================================================
// 
schultezub's avatar
schultezub committed
3
// This file is part of the CAMPVis Software Framework.
4
// 
5
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
schultezub's avatar
schultezub committed
6
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
7
//      Chair for Computer Aided Medical Procedures
8
9
//      Technische Universität München
//      Boltzmannstr. 3, 85748 Garching b. München, Germany
10
// 
schultezub's avatar
schultezub committed
11
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
12
// 
13
14
15
16
// 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
17
// 
18
19
20
21
// 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.
22
23
24
25
26
27
// 
// ================================================================================================

#include "datacontainerinspectorwidget.h"

#include "tgt/assert.h"
28
#include "tgt/logmanager.h"
29
#include "tgt/filesystem.h"
30
31
32
33
34
#include "tgt/shadermanager.h"
#include "tgt/textureunit.h"
#include "tgt/qt/qtcontextmanager.h"
#include "tgt/qt/qtthreadedcanvas.h"

35
36
37
38
39
40
41
42
#ifdef CAMPVIS_HAS_MODULE_DEVIL
#include <IL/il.h>
#include <IL/ilu.h>
#endif

#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"

43
#include "core/datastructures/abstractdata.h"
44
#include "core/datastructures/datacontainer.h"
45
#include "core/datastructures/facegeometry.h"
46
#include "core/datastructures/geometrydata.h"
47
#include "core/datastructures/imagerepresentationgl.h"
48
#include "core/datastructures/renderdata.h"
49

50
51
52
53
#ifdef CAMPVIS_HAS_MODULE_COLUMBIA
#include "modules/columbia/datastructures/fiberdata.h"
#endif

54
#include "application/gui/datacontainertreewidget.h"
55
#include "application/gui/qtdatahandle.h"
56
#include "application//gui/datacontainerfileloaderwidget.h"
57
#include "modules/io/processors/genericimagereader.h"
58

59
60
#include <QFileDialog>

schultezub's avatar
schultezub committed
61
namespace campvis {
62

63
64
    const std::string DataContainerInspectorWidget::loggerCat_ = "CAMPVis.application.DataContainerInspectorWidget";

65
66
    DataContainerInspectorWidget::DataContainerInspectorWidget(QWidget* parent) 
        : QWidget(parent)
67
        , _inited(false)
68
69
70
        , _dataContainer(0)
        , _dctWidget(0)
        , _canvas(0)
71
        , _pcWidget(0)
72
73
74
        , _mainLayout(0)
        , _infoWidget(0)
        , _infoWidgetLayout(0)
75
76
77
78
79
80
81
82
83
84
85
86
87
        , _lblName(0)
        , _lblLocalMemoryFootprint(0)
        , _lblVideoMemoryFootprint(0)
        , _lblTimestamp(0)
        , _lblSize(0)
        , _lblBounds(0)
        , _colorWidget(0)
        , _colorWidgetLayout(0)
        , _lblColorVal(0)
        , _colorValWidget(0)
        , _ColorValWidgetPalette(0)
        , _btnLoadFile(0)
        , _btnSaveToFile(0)
88
        , _propEditorWid(0)
89
90
91
92
93
    {
        setupGUI();
    }

    DataContainerInspectorWidget::~DataContainerInspectorWidget() {
94
95
96
        if (_dataContainer != 0) {
            _dataContainer->s_dataAdded.disconnect(this);
        }
97
98
99
100
    }

    void DataContainerInspectorWidget::setDataContainer(DataContainer* dataContainer) {
        if (_dataContainer != 0) {
101
            _dataContainer->s_dataAdded.disconnect(this);
102
103
104
105
        }

        _dataContainer = dataContainer;
        _dctWidget->update(dataContainer);
106
        updateInfoWidget();
107

108
        if (_dataContainer != 0) {
109
            _dataContainer->s_dataAdded.connect(this, &DataContainerInspectorWidget::onDataContainerDataAdded);
110
111
112
        }
    }

113
114
115
116
    DataContainer* DataContainerInspectorWidget::getDataContainer() {
        return _dataContainer;
    }

117
    void DataContainerInspectorWidget::onDataContainerDataAdded(const std::string& key, const DataHandle& dh) {
118
119
        // copy QtDataHandle because signal will be handled by a different thread an indefinite amount of time later:
        emit dataContainerChanged(QString::fromStdString(key), QtDataHandle(dh));
120
121
122
    }

    QSize DataContainerInspectorWidget::sizeHint() const {
123
        return QSize(800, 600);
124
125
    }

126

127
    void DataContainerInspectorWidget::setupGUI() {
schultezub's avatar
schultezub committed
128
129
        setWindowTitle(tr("DataContainer Inspector"));

130
        _mainLayout = new QGridLayout();
131
132
133
        _mainLayout->setSpacing(4);
        setLayout(_mainLayout);

134
        // left column
135
        _dctWidget = new DataContainerTreeWidget(this);
136
137
        _dctWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
        _dctWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
138
        _dctWidget->setMinimumWidth(256);
139
        _mainLayout->addWidget(_dctWidget, 0, 0);
140

141
142
143
144
145
146
147
148
149
150
151
152
153
        _btnLoadFile = new QPushButton(tr("Load File"), _infoWidget);
        _mainLayout->addWidget(_btnLoadFile, 1, 0);

#ifdef CAMPVIS_HAS_MODULE_DEVIL
        _btnSaveToFile = new QPushButton(tr("Save to File"), _infoWidget);
        _btnSaveToFile->setDisabled(true);
        _mainLayout->addWidget(_btnSaveToFile, 2, 0);

        connect(_btnSaveToFile, SIGNAL(clicked()), this, SLOT(onBtnSaveToFileClicked()));
#endif


        // right column
154
        _infoWidget = new QWidget(this);
155
        _infoWidgetLayout = new QGridLayout();
156
157
158
159
        _infoWidgetLayout->setSpacing(4);
        _infoWidget->setLayout(_infoWidgetLayout);

        _lblName = new QLabel(QString("Name: "), _infoWidget);
160
        _infoWidgetLayout->addWidget(_lblName, 0, 0);
161

162
163
        _lblTimestamp = new QLabel("Timestamp: ", _infoWidget);
        _infoWidgetLayout->addWidget(_lblTimestamp, 0, 1);
164

165
166
        _lblLocalMemoryFootprint = new QLabel(QString("Local Memory: "), _infoWidget);
        _infoWidgetLayout->addWidget(_lblLocalMemoryFootprint, 1, 0);
167

168
169
        _lblVideoMemoryFootprint = new QLabel(QString("Video Memory: "), _infoWidget);
        _infoWidgetLayout->addWidget(_lblVideoMemoryFootprint, 1, 1);
170

171
        _lblSize = new QLabel(tr("Size: "), _infoWidget);
172
173
174
175
        _infoWidgetLayout->addWidget(_lblSize, 2, 0);

        _lblBounds = new QLabel(tr("World Bounds:"), _infoWidget);
        _infoWidgetLayout->addWidget(_lblBounds, 3, 0, 1, 2);
176

177
178
179
180
181
182
183
184
        _colorWidget = new QWidget(this);
        _lblColorVal = new QLabel(tr("Color: n/a"), _colorWidget);
        
        _colorValWidget = new QWidget(_colorWidget);
        _colorValWidget->setAutoFillBackground(true);
        _colorValWidget->setFixedSize(16, 16);
        
        _ColorValWidgetPalette = new QPalette(palette());
185
        _ColorValWidgetPalette->setColor(QPalette::Background, Qt::gray);
186
187
188
189
190
191
192
193
194
195
        _colorValWidget->setPalette(*(_ColorValWidgetPalette));
        
        _colorWidgetLayout = new QHBoxLayout();
        _colorWidgetLayout->setSpacing(0);
        _colorWidgetLayout->setMargin(0);
        _colorWidget->setLayout(_colorWidgetLayout);
        
        _colorWidgetLayout->addWidget(_lblColorVal);
        _colorWidgetLayout->addWidget(_colorValWidget);

196
        _infoWidgetLayout->addWidget(_colorWidget, 4, 0, 1, 2);
197

198
        _canvas = new DataContainerInspectorCanvas(_infoWidget);
199
        _canvas->setMinimumSize(QSize(100, 100));
200
        _infoWidgetLayout->addWidget(_canvas, 5, 0, 1, 2);
201

202
        _pcWidget = new PropertyCollectionWidget(_infoWidget);
203
        _pcWidget->updatePropCollection(_canvas, _dataContainer);
204
        _infoWidgetLayout->addWidget(_pcWidget, 6, 0, 1, 2);
205

206
        _mainLayout->addWidget(_infoWidget, 0, 1, 3, 1);
207

208
        qRegisterMetaType<QtDataHandle>("QtDataHandle");
209
210
211
212
213
214
        connect(
            _dctWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), 
            this, SLOT(onDCTWidgetSelectionModelSelectionChanged(const QItemSelection&, const QItemSelection&)));
        connect(
            this, SIGNAL(dataContainerChanged(const QString&, QtDataHandle)),
            _canvas, SLOT(onDataContainerChanged(const QString&, QtDataHandle)));
215
        connect(
216
217
            this, SIGNAL(dataContainerChanged(const QString&, QtDataHandle)),
            _dctWidget->getTreeModel(), SLOT(onDataContainerChanged(const QString&, QtDataHandle)));
218
        connect(
219
220
            _btnLoadFile, SIGNAL(clicked()),
            this, SLOT(onBtnLoadFileClicked()));
221
222
    }

223
    void DataContainerInspectorWidget::updateColor(){
224
        const tgt::Color& color = _canvas->getCapturedColor();
225
226


227
        _lblColorVal->setText(QString("Color: [%1, %2, %3, %4]").arg(QString::number(color.r), QString::number(color.g), QString::number(color.b), QString::number(color.a)));
228
        
229
        _ColorValWidgetPalette->setColor(QPalette::Background, QColor(static_cast<int>(color.r * 255), static_cast<int>(color.g * 255), static_cast<int>(color.b * 255)));
230
231
        _colorValWidget->setPalette(*_ColorValWidgetPalette);
    }
232

233
    void DataContainerInspectorWidget::updateDepth(){
234
235
236
237
238

        float depth = _canvas->getCapturedDepth();

        _lblColorVal->setText(QString("Depth: %1").arg(QString::number(depth)));
        
239
        _ColorValWidgetPalette->setColor(QPalette::Background, QColor(static_cast<int>(depth * 255), static_cast<int>(depth * 255), static_cast<int>(depth * 255)));
240
241
242
        _colorValWidget->setPalette(*_ColorValWidgetPalette);
    }

243
    void DataContainerInspectorWidget::updateInfoWidget() {
244
245
246
        if (!_inited)
            return;

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
        // get the selection from the tree widget
        const QModelIndexList& indices = _dctWidget->selectionModel()->selectedRows();
        std::vector< std::pair<QString, QtDataHandle> > handles;
        float _localFootprint = 0.f;
        float _videoFootprint = 0.f;

        // iterate through the indices of the selection
        for (QModelIndexList::const_iterator index = indices.begin(); index != indices.end(); ++index) {
            if (! index->isValid())
                continue;
            // get DataHandle and Handle name
            QVariant item = index->data(Qt::UserRole);
            QtDataHandle handle = item.value<QtDataHandle>();
            QModelIndex idxName = index->sibling(index->row(), 0);

            // only consider non-empty DataHandles
            if (handle.getData() != 0) {
                handles.push_back(std::make_pair(idxName.data(Qt::DisplayRole).toString(), handle));
                _localFootprint += handle.getData()->getLocalMemoryFootprint();
                _videoFootprint += handle.getData()->getVideoMemoryFootprint();
            }
schultezub's avatar
schultezub committed
268
        }
269

270
271
272
273
        // update labels
        if (handles.size() == 1) {
            _lblName->setText("Name: " + handles.front().first);
            _lblTimestamp->setText("Timestamp: " + QString::number(handles.front().second.getTimestamp()));
274

275
            if (const ImageData* tester = dynamic_cast<const ImageData*>(handles.front().second.getData())) {
276
                _canvas->p_transferFunction.setImageHandle(handles.front().second);
277
278
279
280
281
282
283
284
                std::ostringstream ss;

                ss << tester->getSize();
                _lblSize->setText(tr("Size: ") + QString::fromStdString(ss.str()));

                ss.str("");
                ss << tester->getWorldBounds();
                _lblBounds->setText(tr("World Bounds: ") + QString::fromStdString(ss.str())); 
285

286
287
288
289
290
291
292
                _canvas->p_currentSlice.setVisible(tester->getDimensionality() == 3);
                _canvas->p_transferFunction.setVisible(true);
                _canvas->p_meshSolidColor.setVisible(false);
                _canvas->p_renderRChannel.setVisible(true);
                _canvas->p_renderGChannel.setVisible(true);
                _canvas->p_renderBChannel.setVisible(true);
                _canvas->p_renderAChannel.setVisible(true);
293
294
295
296
            }
            else if (const GeometryData* tester = dynamic_cast<const GeometryData*>(handles.front().second.getData())) {
                _lblSize->setText(tr("Size: n/a"));

297
                std::ostringstream ss;
298
                ss << tester->getWorldBounds();
299
                _lblBounds->setText(tr("World Bounds: ") + QString::fromStdString(ss.str()));
300
301
302
303
304
305
306
307

                _canvas->p_currentSlice.setVisible(false);
                _canvas->p_transferFunction.setVisible(false);
                _canvas->p_meshSolidColor.setVisible(true);
                _canvas->p_renderRChannel.setVisible(false);
                _canvas->p_renderGChannel.setVisible(false);
                _canvas->p_renderBChannel.setVisible(false);
                _canvas->p_renderAChannel.setVisible(false);
308
            }
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
            else if (const RenderData* tester = dynamic_cast<const RenderData*>(handles.front().second.getData())) {
                const ImageData* id = tester->getNumColorTextures() > 0 ? tester->getColorTexture() : tester->getDepthTexture();
                if (id != 0) {
                    std::ostringstream ss;
                    ss << id->getSize();
                    _lblSize->setText(tr("Size: ") + QString::fromStdString(ss.str()));

                    ss.str("");
                    ss << id->getWorldBounds();
                    _lblBounds->setText(tr("World Bounds: ") + QString::fromStdString(ss.str())); 
                }
                else {
                    _lblSize->setText(tr("Size: n/a"));
                    _lblBounds->setText(tr("World Bounds: n/a")); 
                }
324
325
326
327
328
329
330
331

                _canvas->p_currentSlice.setVisible(false);
                _canvas->p_transferFunction.setVisible(true);
                _canvas->p_meshSolidColor.setVisible(false);
                _canvas->p_renderRChannel.setVisible(true);
                _canvas->p_renderGChannel.setVisible(true);
                _canvas->p_renderBChannel.setVisible(true);
                _canvas->p_renderAChannel.setVisible(true);
332
            }
333
334
335
336
337
338
339
340
341
342
343
344
#ifdef CAMPVIS_HAS_MODULE_COLUMBIA
            else if (const FiberData* tester = dynamic_cast<const FiberData*>(handles.front().second.getData())) {
                std::ostringstream ss;
                ss << "Size: " << tester->numFibers() << " Fibers with " << tester->numSegments() << " Segments.";
                _lblSize->setText(QString::fromStdString(ss.str()));

                ss.str("");
                ss << tester->getWorldBounds();
                _lblBounds->setText(tr("World Bounds: ") + QString::fromStdString(ss.str())); 
            }
#endif
            else {
345

346
                _lblSize->setText(tr("Size: n/a"));
347
348
                _lblBounds->setText(tr("World Bounds: n/a")); 
            }
349
350
351
352
        }
        else {
            _lblName->setText(QString::number(handles.size()) + " DataHandles selected");
            _lblTimestamp->setText("Timestamp: n/a");
353

354
            _canvas->p_transferFunction.setImageHandle(DataHandle(0));
355
        }
356
357
        _lblLocalMemoryFootprint->setText("Local Memory: " + humanizeBytes(_localFootprint));
        _lblVideoMemoryFootprint->setText("Video Memory: " + humanizeBytes(_videoFootprint));
358
359

        // update DataHandles for the DataContainerInspectorCanvas
360
        _canvas->setDataHandles(handles);
361
362
    }

363
364
365
366
367
    QString DataContainerInspectorWidget::humanizeBytes(size_t numBytes) const {
        QString units[5] = { tr(" Bytes"), tr(" KB"), tr(" MB"), tr(" GB"), tr(" TB") };
        size_t index = 0;
        size_t remainder = 0;

368
        while (numBytes > 1024 && index < 4) {
369
370
371
372
373
374
375
376
377
378
379
            remainder = numBytes % 1024;
            numBytes /= 1024;
            ++index;
        }

        if (remainder != 0)
            return QString::number(numBytes) + "." + QString::number(remainder) + units[index];
        else
            return QString::number(numBytes) + units[index];
    }

380
381
    void DataContainerInspectorWidget::init() {
        if (_canvas != 0)
382
            _canvas->init(this);
383
384

        _inited = true;
385
386
387
    }

    void DataContainerInspectorWidget::deinit() {
388
        _inited = false;
389
390
        if (_canvas != 0)
            _canvas->deinit();
schultezub's avatar
schultezub committed
391

392
        _pcWidget->updatePropCollection(0, 0);
393
394
395
396
397
398

        if (_dataContainer != 0) {
            _dataContainer->s_dataAdded.disconnect(this);
        }

        _dataContainer = 0;
399
        _dctWidget->update(0);
400
401
402

        if(_propEditorWid != nullptr)
            _propEditorWid->deinit();
403
404
    }

405
406
    void DataContainerInspectorWidget::onDCTWidgetSelectionModelSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
        updateInfoWidget();
407
408
409
410
411
412
413
414
415
416
417
418

        // get the selection from the tree widget
        const QModelIndexList& indices = _dctWidget->selectionModel()->selectedRows();

        // iterate through the indices of the selection
        for (QModelIndexList::const_iterator index = indices.begin(); index != indices.end(); ++index) {
            if (index->isValid()) {
                _btnSaveToFile->setDisabled(false);
                return;
            }
        }
        _btnSaveToFile->setDisabled(true);
419
420
    }

421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
    void DataContainerInspectorWidget::onBtnSaveToFileClicked() {
        // get the selection from the tree widget
        const QModelIndexList& indices = _dctWidget->selectionModel()->selectedRows();

        // iterate through the indices of the selection
        for (QModelIndexList::const_iterator index = indices.begin(); index != indices.end(); ++index) {
            if (! index->isValid())
                continue;

            // get DataHandle and Handle name
            QVariant item = index->data(Qt::UserRole);
            DataHandle handle = item.value<QtDataHandle>();
            QModelIndex idxName = index->sibling(index->row(), 0);

            // only consider non-empty DataHandles that are ImageData and have render target representations
            if (handle.getData() != 0) {
437
438
439
440
441
442
443
444
445
446
447
448
449
                if (dynamic_cast<const ImageData*>(handle.getData()) || dynamic_cast<const RenderData*>(handle.getData())) {
                    QString dialogCaption = "Export " + idxName.data(Qt::DisplayRole).toString() + " as Image";
                    QString directory = tr("");
                    const QString fileFilter = tr("*.png;;PNG images (*.png)");

                    QString filename = QFileDialog::getSaveFileName(this, dialogCaption, directory, fileFilter);

                    if (! filename.isEmpty()) {
                        // Texture access needs OpenGL context - dispatch method call:
                        GLJobProc.enqueueJob(
                            _canvas, 
                            makeJobOnHeap(&DataContainerInspectorWidget::saveToFile, handle, filename.toStdString()), 
                            OpenGLJobProcessor::SerialJob);
450
451
452
453
454
455
456
457
458
459
460
461
462
                    }
                }
            }
        }
    }

    void DataContainerInspectorWidget::saveToFile(DataHandle handle, std::string filename) {
#ifdef CAMPVIS_HAS_MODULE_DEVIL
        if (tgt::FileSystem::fileExtension(filename).empty()) {
            LERRORC("CAMPVis.application.DataContainerInspectorWidget", "Filename has no extension");
            return;
        }

463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
        // get the ImageData object (either directly or from the RenderData)
        const ImageData* id = 0;
        if (const RenderData* tester = dynamic_cast<const RenderData*>(handle.getData())) {
            id = tester->getColorTexture(0);
        }
        else if (const ImageData* tester = dynamic_cast<const ImageData*>(handle.getData())) {
            id = tester;
        }
        else {
            LERROR("Could not extract image to save.");
            return;
        }

        // extract the data
        WeaklyTypedPointer wtp(WeaklyTypedPointer::UINT8, 1, 0);
        const ImageRepresentationGL* repGL = id->getRepresentation<ImageRepresentationGL>(false);
        if (repGL != 0) // if it's a GL texture, download it (we do not want to use the automatic conversion method here)
            wtp = repGL->getWeaklyTypedPointer();
        else {
            const ImageRepresentationLocal* repLocal = id->getRepresentation<ImageRepresentationLocal>(true);
            if (repLocal != 0)
                wtp = repLocal->getWeaklyTypedPointer();
        }

        if (wtp._pointer == 0) {
            LERROR("Could not extract image to save.");
            return;
        }
491
492
493
494
495
496
497
498


        // create Devil image from image data and write it to file
        ILuint img;
        ilGenImages(1, &img);
        ilBindImage(img);

        // put pixels into IL-Image
499
500
        tgt::ivec2 size = id->getSize().xy();
        ilTexImage(size.x, size.y, 1, static_cast<ILubyte>(wtp._numChannels), wtp.getIlFormat(), wtp.getIlDataType(), wtp._pointer);
501
502
503
504
505
506
507
508
509
510
511
512
513
514
        ilEnable(IL_FILE_OVERWRITE);
        ilResetWrite();
        ILboolean success = ilSaveImage(filename.c_str());
        ilDeleteImages(1, &img);

        if (!success) {
            LERRORC("CAMPVis.application.DataContainerInspectorWidget", "Could not save image to file: " << ilGetError());
        }
#else
        return;
#endif

    }

515
    void DataContainerInspectorWidget::onBtnLoadFileClicked() {
516
517
518
        // delete previous PropertyEditor, then create a new one
        // the final one will be deleted with deinit()
        if(nullptr != _propEditorWid)
519
            _propEditorWid->deinit();
520

521
        _propEditorWid = new DataContainerFileLoaderWidget(this, nullptr);
522
523
        _propEditorWid->setVisible(true);
        
524
525
    }

526
}