BearAttack BearAttack - 3 days ago 5
C++ Question

VTK + QT project: error LNK1169: one or more multiply defined symbols found

My program is using VTK and QT to create a DICOM viewer inside a QT window. I'm trying to setup a custom interactor so I can override input functions. Upon creating this custom interactor class, I'm running into linker errors. My code is below:

FluoroViewer.h:

#ifndef FluoroViewer_H
#define FluoroViewer_H

#include <QMainWindow>

#include <vtkSmartPointer.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkDICOMImageReader.h>
#include <vtkImageViewer2.h>

#include "InteractorStyleImage.h"

namespace Ui {
class FluoroViewer;
}

class FluoroViewer : public QMainWindow {
Q_OBJECT

public:
explicit FluoroViewer(QWidget *parent = 0);
~FluoroViewer();

private slots:
void openDICOMFolder();
void on_loadImages_clicked();

private:
Ui::FluoroViewer *ui;
void drawDICOMSeries(std::string folderDICOM);
vtkSmartPointer<vtkDICOMImageReader> readerDICOMSeries;
vtkSmartPointer<vtkImageViewer2> imageViewerDICOMSeries;
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor;
vtkSmartPointer<InteractorStyleImage> interactorStyle;
};

#endif


FluoroViewer.cxx:

#include "FluoroViewer.h"
#include "ui_FluoroViewer.h"

#include <QFileDialog>

FluoroViewer::FluoroViewer(QWidget *parent) : QMainWindow(parent), ui(new Ui::FluoroViewer) {
ui->setupUi(this);
readerDICOMSeries = vtkSmartPointer<vtkDICOMImageReader>::New();
imageViewerDICOMSeries = vtkSmartPointer<vtkImageViewer2>::New();
renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
//interactorStyle = vtkSmartPointer<InteractorStyleImage>::New();
}

FluoroViewer::~FluoroViewer() {
delete ui;
}

void FluoroViewer::openDICOMFolder() {
QString folderNameDICOM = QFileDialog::getExistingDirectory(this, tr("Open DICOM Folder"), QDir::currentPath(), QFileDialog::ShowDirsOnly);
std::string folderName = folderNameDICOM.toUtf8().constData();
drawDICOMSeries(folderName);
}

void FluoroViewer::drawDICOMSeries(std::string folderDICOM) {
readerDICOMSeries->SetDirectoryName(folderDICOM.c_str());
readerDICOMSeries->Update();

imageViewerDICOMSeries->SetInputConnection(readerDICOMSeries->GetOutputPort());

ui->slider->setMinimum(imageViewerDICOMSeries->GetSliceMin());
ui->slider->setMaximum(imageViewerDICOMSeries->GetSliceMax());

//interactorStyle->SetImageViewer(imageViewerDICOMSeries);

imageViewerDICOMSeries->SetupInteractor(renderWindowInteractor);
//renderWindowInteractor->SetInteractorStyle(interactorStyle);

// SET FLUORO COLOR AND WINDOW LEVELS
imageViewerDICOMSeries->SetColorLevel(523);
imageViewerDICOMSeries->SetColorWindow(-1223);

imageViewerDICOMSeries->SetRenderWindow(ui->vtkRenderer->GetRenderWindow());
imageViewerDICOMSeries->Render();
renderWindowInteractor->Disable();
renderWindowInteractor->Start();
}

void FluoroViewer::on_loadImages_clicked() {
openDICOMFolder();
}


InteractorStyleImage.h:

#ifndef InteractorStyleImage_H
#define InteractorStyleImage_H

#include <vtkSmartPointer.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkImageViewer2.h>
#include <vtkDICOMImageReader.h>
#include <vtkInteractorStyleImage.h>

class InteractorStyleImage : public vtkInteractorStyleImage {
public:
static InteractorStyleImage* New();
vtkTypeMacro(InteractorStyleImage, vtkInteractorStyleImage);

protected:
vtkImageViewer2* imageViewer;
int currentSlice;
int minSlice;
int maxSlice;

public:
void SetImageViewer(vtkImageViewer2* imageViewerTemp);
void MoveSliceForward();
void MoveSliceBackward();

public:
virtual void OnKeyDown() {
std::string key = this->GetInteractor()->GetKeySym();
if (key.compare("Up") == 0) {
cout << "Up arrow key was pressed." << endl;
MoveSliceForward();
}
else if (key.compare("Down") == 0) {
cout << "Down arrow key was pressed." << endl;
MoveSliceBackward();
}
vtkInteractorStyleImage::OnKeyDown();
}

public:
virtual void OnMouseWheelForward() {
cout << "Scrolled mouse wheel forward." << endl;
MoveSliceForward();
//vtkInteractorStyleImage::OnMouseWheelForward();
}

public:
virtual void OnMouseWheelBackward() {
cout << "Scrolled mouse wheel backward." << endl;
if (currentSlice > minSlice) {
MoveSliceBackward();
}
//vtkInteractorStyleImage::OnMouseWheelBackward();
}
};
vtkStandardNewMacro(InteractorStyleImage);

#endif


InteractorStyleImage.cxx:

#include "InteractorStyleImage.h"

void InteractorStyleImage::SetImageViewer(vtkImageViewer2* imageViewerTemp) {
imageViewer = imageViewerTemp;
minSlice = imageViewer->GetSliceMin();
maxSlice = imageViewer->GetSliceMax();
currentSlice = minSlice;
cout << "Slicer: Min = " << minSlice << ", Max = " << maxSlice;
}

void InteractorStyleImage::MoveSliceForward() {
if(currentSlice < maxSlice) {
currentSlice += 1;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
}
}

void InteractorStyleImage::MoveSliceBackward() {
if(currentSlice > minSlice) {
currentSlice -= 1;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
}
}


FluoroViewerDriver.cxx:

#include <QApplication>
#include "FluoroViewer.h"

int main(int argc, char *argv[]) {
QApplication app(argc, argv);

FluoroViewer fluoroViewer;
fluoroViewer.show();

return app.exec();
}


Any thoughts on the issue?

Answer

It's most likely due to vtkStandardNewMacro(). You don't show how you build this program, but I am pretty sure that FluoroViewer ends up in one translation unit and InteractorStyleImage in another. However, FluoroViewer #includes InteractorStyleImage.h, which defines the New function. So you'll end up with two same symbols in two separate objects, and the linker craps out. The solution is to move the vtkStandardNewMacro() invocation line into implementation (*.cpp file).

HTH,

Miro

Comments