/*========================================================================= medInria Copyright (c) INRIA 2019. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #include "resliceToolBox.h" #include #include #include #include #include #include #include #include #include #include #include class resliceToolBoxPrivate { public: QPushButton *b_startReslice, *b_stopReslice, *b_saveImage, *b_reset; QComboBox *bySpacingOrSize; QLabel *helpBegin; medDoubleParameterL *spacingOrSizeX, *spacingOrSizeY, *spacingOrSizeZ; medAbstractLayeredView *currentView; medResliceViewer *resliceViewer; dtkSmartPointer reformatedImage; QWidget *reformatOptions; medVtkImageInfo* imageInfo; }; resliceToolBox::resliceToolBox (QWidget *parent) : medAbstractSelectableToolBox (parent), d(new resliceToolBoxPrivate) { // Fill the toolBox QWidget *resliceToolBoxBody = new QWidget(this); d->b_startReslice = new QPushButton("Start Reslice", resliceToolBoxBody); d->b_startReslice->setCheckable(false); d->b_startReslice->setObjectName("startReformatButton"); d->b_stopReslice = new QPushButton("Stop Reslice", resliceToolBoxBody); d->b_stopReslice->setCheckable(false); d->b_stopReslice->setObjectName("stopReformatButton"); d->b_saveImage = new QPushButton("Save Image", resliceToolBoxBody); d->b_saveImage->setCheckable(false); d->b_saveImage->setObjectName("saveImageButton"); // User can choose pixel or millimeter resample QHBoxLayout * resampleLayout = new QHBoxLayout(); QLabel *bySpacingOrSizeLabel = new QLabel("Select your resample parameter ", resliceToolBoxBody); d->bySpacingOrSize = new QComboBox(resliceToolBoxBody); d->bySpacingOrSize->setObjectName("bySpacingOrSize"); d->bySpacingOrSize->addItem("Spacing"); d->bySpacingOrSize->addItem("Size"); resampleLayout->addWidget(bySpacingOrSizeLabel); resampleLayout->addWidget(d->bySpacingOrSize); connect(d->bySpacingOrSize, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(switchSpacingAndDimension(const QString&))); // Spinboxes of resample values QWidget *spinBoxes = new QWidget(resliceToolBoxBody); QVBoxLayout *spacingSpinBoxLayout = new QVBoxLayout(resliceToolBoxBody); QHBoxLayout *spacingSpinBoxLayoutX = new QHBoxLayout(); spacingSpinBoxLayoutX->setAlignment(Qt::AlignCenter); d->spacingOrSizeX = new medDoubleParameterL("X", this); d->spacingOrSizeX->getSpinBox()->setAccessibleName("SpacingOrSizeX"); d->spacingOrSizeX->setObjectName("SpacingOrSizeX"); d->spacingOrSizeX->setRange(0,100); d->spacingOrSizeX->getSpinBox()->setSuffix(" mm"); d->spacingOrSizeX->setSingleStep(0.1f); spacingSpinBoxLayoutX->addWidget(d->spacingOrSizeX->getLabel()); spacingSpinBoxLayoutX->addWidget(d->spacingOrSizeX->getSpinBox()); spacingSpinBoxLayout->addLayout(spacingSpinBoxLayoutX); QHBoxLayout *spacingSpinBoxLayoutY = new QHBoxLayout(); spacingSpinBoxLayoutY->setAlignment(Qt::AlignCenter); d->spacingOrSizeY = new medDoubleParameterL("Y", this); d->spacingOrSizeY->getSpinBox()->setAccessibleName("SpacingOrSizeY"); d->spacingOrSizeY->setObjectName("SpacingOrSizeY"); d->spacingOrSizeY->setRange(0,100); d->spacingOrSizeY->getSpinBox()->setSuffix(" mm"); d->spacingOrSizeY->setSingleStep(0.1f); spacingSpinBoxLayoutY->addWidget(d->spacingOrSizeY->getLabel()); spacingSpinBoxLayoutY->addWidget(d->spacingOrSizeY->getSpinBox()); spacingSpinBoxLayout->addLayout(spacingSpinBoxLayoutY); QHBoxLayout *spacingSpinBoxLayoutZ = new QHBoxLayout(); spacingSpinBoxLayoutZ->setAlignment(Qt::AlignCenter); d->spacingOrSizeZ = new medDoubleParameterL("Z", this); d->spacingOrSizeZ->getSpinBox()->setAccessibleName("SpacingOrSizeZ"); d->spacingOrSizeZ->setObjectName("SpacingOrSizeZ"); d->spacingOrSizeZ->setRange(0,100); d->spacingOrSizeZ->getSpinBox()->setSuffix(" mm"); d->spacingOrSizeZ->setSingleStep(0.1f); spacingSpinBoxLayoutZ->addWidget(d->spacingOrSizeZ->getLabel()); spacingSpinBoxLayoutZ->addWidget(d->spacingOrSizeZ->getSpinBox()); spacingSpinBoxLayout->addLayout(spacingSpinBoxLayoutZ); spinBoxes->setLayout(spacingSpinBoxLayout); QVBoxLayout *resliceToolBoxLayout = new QVBoxLayout(resliceToolBoxBody); d->helpBegin = new QLabel("Drop a data in the view and click on 'Start Reslice'.", resliceToolBoxBody); d->helpBegin->setObjectName("helpBegin"); d->helpBegin->setStyleSheet("font: italic"); resliceToolBoxLayout->addWidget(d->helpBegin); QLabel *help1 = new QLabel("To change the windowing left click on a 2D view.", resliceToolBoxBody); QLabel *help2 = new QLabel("To reset axes in a view press 'o'.", resliceToolBoxBody); QLabel *help3 = new QLabel("To reset windowing in a view press 'r'.\n", resliceToolBoxBody); help1->setStyleSheet("font: italic"); help2->setStyleSheet("font: italic"); help3->setStyleSheet("font: italic"); resliceToolBoxLayout->addWidget(d->b_startReslice); d->reformatOptions = new QWidget(resliceToolBoxBody); QVBoxLayout *reformatOptionsLayout = new QVBoxLayout(d->reformatOptions); d->reformatOptions->setLayout(reformatOptionsLayout); d->reformatOptions->hide(); resliceToolBoxLayout->addWidget(d->reformatOptions); reformatOptionsLayout->addWidget(help1); reformatOptionsLayout->addWidget(help2); reformatOptionsLayout->addWidget(help3); reformatOptionsLayout->addLayout(resampleLayout); reformatOptionsLayout->addWidget(spinBoxes); reformatOptionsLayout->addWidget(d->b_saveImage); reformatOptionsLayout->addWidget(d->b_stopReslice); resliceToolBoxBody->setLayout(resliceToolBoxLayout); this->addWidget(resliceToolBoxBody); // Connections connect(d->b_startReslice,SIGNAL(clicked()),this,SLOT(startReformat())); connect(d->b_stopReslice,SIGNAL(clicked()),this,SLOT(stopReformat())); d->resliceViewer = nullptr; d->currentView = nullptr; } resliceToolBox::~resliceToolBox() { delete d->resliceViewer; d->resliceViewer = nullptr; d->currentView = nullptr; delete d; d = nullptr; } bool resliceToolBox::registered() { return medToolBoxFactory::instance()->registerToolBox(); } dtkPlugin* resliceToolBox::plugin() { medPluginManager* pm = medPluginManager::instance(); dtkPlugin* plugin = pm->plugin ( "Reformat" ); return plugin; } void resliceToolBox::startReformat() { if (d->currentView && getWorkspace()) { medAbstractData* data = d->currentView->layerData(d->currentView->currentLayer()); bool is3D = false; // Toolbox does not work with meshes or vector images if (data->identifier().contains("itkDataImage") && !data->identifier().contains("Vector")) { if (dynamic_cast(data)->Dimension() == 3) { is3D = true; } if (d->currentView->layersCount() && is3D) { d->helpBegin->hide(); d->reformatOptions->show(); d->b_startReslice->hide(); d->resliceViewer = new medResliceViewer(d->currentView, getWorkspace()->tabbedViewContainers()); d->resliceViewer->setToolBox(this); getWorkspace()->tabbedViewContainers()->setAcceptDrops(false); medViewContainer * container = getWorkspace()->tabbedViewContainers()->insertNewTab(0, "Reslice"); getWorkspace()->tabbedViewContainers()->setCurrentIndex(0); container->changeDefaultWidget(d->resliceViewer->viewWidget()); connect(d->resliceViewer,SIGNAL(imageReformatedGenerated()), this,SLOT(saveReformatedImage()), Qt::UniqueConnection); connect(container, SIGNAL(viewRemoved()), this, SLOT(stopReformat()), Qt::UniqueConnection); connect(d->spacingOrSizeX, SIGNAL(valueChanged(double)), d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); connect(d->spacingOrSizeY, SIGNAL(valueChanged(double)), d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); connect(d->spacingOrSizeZ, SIGNAL(valueChanged(double)), d->resliceViewer, SLOT(askedSpacingOrSizeChange(double)), Qt::UniqueConnection); connect(d->b_saveImage, SIGNAL(clicked()), d->resliceViewer, SLOT(saveImage()), Qt::UniqueConnection); d->reformatedImage = nullptr; // close the initial tab which is not needed anymore getWorkspace()->tabbedViewContainers()->removeTab(1); updateView(); } else { medMessageController::instance()->showError(tr("Drop a 3D volume in the view"), 3000); } } else { medMessageController::instance()->showError(tr("Drop a 3D volume in the view"), 3000); } } else { medMessageController::instance()->showError(tr("Drop a 3D volume in the view"), 3000); } } void resliceToolBox::stopReformat() { if (getWorkspace()) { d->bySpacingOrSize->setCurrentIndex(0); // init resample parameter d->helpBegin->show(); d->reformatOptions->hide(); d->b_startReslice->show(); getWorkspace()->tabbedViewContainers()->removeTab(0); getWorkspace()->tabbedViewContainers()->insertNewTab(0, getWorkspace()->name()); getWorkspace()->tabbedViewContainers()->setCurrentIndex(0); if (d->currentView) { getWorkspace()->tabbedViewContainers()->getFirstSelectedContainer()->addData(d->currentView->layerData(0)); } disconnect(d->resliceViewer); delete d->resliceViewer; d->resliceViewer = nullptr; QList toolBoxes = getWorkspace()->toolBoxes(); for (int i = 0; i < toolBoxes.length(); i++) { toolBoxes[i]->show(); } } } void resliceToolBox::clear() { stopReformat(); } void resliceToolBox::updateView() { medAbstractView* view = this->getWorkspace()->tabbedViewContainers()->getFirstSelectedContainerView(); d->currentView = nullptr; if (view) { medAbstractLayeredView *layeredView = qobject_cast(view); if (dynamic_cast(layeredView->layerData(layeredView->currentLayer()))) { d->currentView = layeredView; // Get the original image information and display it vtkImageView2D *view2d = static_cast(d->currentView->backend())->view2D; d->imageInfo = view2d->GetMedVtkImageInfo(); displayInfoOnCurrentView(); } } } void resliceToolBox::displayInfoOnCurrentView() { if (d->bySpacingOrSize->currentIndex() == 0) // Spacing { double *spacing = d->imageInfo->spacing; d->spacingOrSizeX->setValue(spacing[0]); d->spacingOrSizeY->setValue(spacing[1]); d->spacingOrSizeZ->setValue(spacing[2]); } else // Dimension { int *dimension = d->imageInfo->dimensions; d->spacingOrSizeX->setValue(dimension[0]); d->spacingOrSizeY->setValue(dimension[1]); d->spacingOrSizeZ->setValue(dimension[2]); } } void resliceToolBox::saveReformatedImage() { generateReformatedImage(); if (d->reformatedImage && d->reformatedImage->data()) { medDataManager::instance()->importData(d->reformatedImage, false); } } void resliceToolBox::generateReformatedImage() { if (d->resliceViewer) { d->reformatedImage = d->resliceViewer->getOutput(); } } void resliceToolBox::switchSpacingAndDimension(const QString & value) { if (value == "Spacing") { d->spacingOrSizeX->getSpinBox()->setSuffix(" mm"); d->spacingOrSizeX->setRange(0, 100); d->spacingOrSizeX->setSingleStep(0.1f); d->spacingOrSizeY->getSpinBox()->setSuffix(" mm"); d->spacingOrSizeY->setRange(0, 100); d->spacingOrSizeY->setSingleStep(0.1f); d->spacingOrSizeZ->getSpinBox()->setSuffix(" mm"); d->spacingOrSizeZ->setRange(0, 100); d->spacingOrSizeZ->setSingleStep(0.1f); } else // Dimension { d->spacingOrSizeX->getSpinBox()->setSuffix(" px"); d->spacingOrSizeX->setRange(0, 10000); d->spacingOrSizeX->setSingleStep(10); d->spacingOrSizeY->getSpinBox()->setSuffix(" px"); d->spacingOrSizeY->setRange(0, 10000); d->spacingOrSizeY->setSingleStep(10); d->spacingOrSizeZ->getSpinBox()->setSuffix(" px"); d->spacingOrSizeZ->setRange(0, 10000); d->spacingOrSizeZ->setSingleStep(10); } displayInfoOnCurrentView(); } medAbstractData *resliceToolBox::processOutput() { if (!d->reformatedImage) { generateReformatedImage(); } return d->reformatedImage; } void resliceToolBox::changeButtonValue(QString buttonName, double value) { if (buttonName == "SpacingOrSizeX") { d->spacingOrSizeX->setValue(value); } else if (buttonName == "SpacingOrSizeY") { d->spacingOrSizeY->setValue(value); } else if (buttonName == "SpacingOrSizeZ") { d->spacingOrSizeZ->setValue(value); } }