VolumeReader::read
void VolumeReader::read(Volume *volume)
{
this->executePixelDataReader(volume);
this->showMessageBoxWithLastError();
}
m_lastError = m_volumePixelDataReader->read(fileList);
if (m_lastError == VolumePixelDataReader::NoError)
{
// Tot ha anat ok, assignem les dades al volum
volume->setPixelData(m_volumePixelDataReader->getVolumePixelData());
runPostprocessors(volume);
fixSpacingIssues(volume);
}
else
{
volume->convertToNeutralVolume();
this->logWarningLastError(fileList);
}
void Volume::setPixelData(VolumePixelData *pixelData)
{
Q_ASSERT(pixelData != 0);
m_volumePixelData = pixelData;
// Set the number of phases to the new pixel data
m_volumePixelData->setNumberOfPhases(m_numberOfPhases);
}
更新slice分析
void Q2DViewer::updateSliceToDisplay(int value, SliceDimension dimension)
{
if (hasInput())
{
int oldSlice = getCurrentSlice();
int oldPhase = getCurrentPhase();
// First update the index of the corresponding dimension
switch (dimension)
{
case SpatialDimension:
getMainDisplayUnit()->setSlice(value);
updateSecondaryVolumesSlices();
break;
case TemporalDimension:
getMainDisplayUnit()->setPhase(value);
break;
}
// Then update display (image and associated annotations)
updateImageSlices();
void Q2DViewer::updateImageSlices()
{
// Ens assegurem que tenim dades vàlides
if (!getMainInput()->isPixelDataLoaded())
{
return;
}
// We only need to update the main vdu, since it will modify the camera and thus the others will also be updated
getMainDisplayUnit()->updateImageSlice(m_renderer->GetActiveCamera());
// TODO Si separem els renderers potser caldria aplicar-ho a cada renderer?
getRenderer()->ResetCameraClippingRange();
}
//此处更新相机焦点
void VolumeDisplayUnit::updateImageSlice(vtkCamera *camera)
{
if (!m_volume || !m_volume->isPixelDataLoaded())
{
return;
}
int imageIndex = getSlice();
int zIndex = this->getViewPlane().getZIndex();
double origin[3];
m_volume->getOrigin(origin);
double spacing[3];
m_volume->getSpacing(spacing);
double focalPoint[3];
camera->GetFocalPoint(focalPoint);
focalPoint[zIndex] = origin[zIndex] + imageIndex * spacing[zIndex];
camera->SetFocalPoint(focalPoint);
}
//最适合的填充窗口
void QViewer::fitRenderingIntoViewport()
{
// First we get the bounds of the current rendered item in world coordinates
double bounds[6];
getCurrentRenderedItemBounds(bounds);
double topCorner[3];
double bottomCorner[3];
for (int i = 0; i < 3; ++i)
{
topCorner[i] = bounds[i * 2];
bottomCorner[i] = bounds[i * 2 + 1];
}
// Scaling the viewport to fit the current item bounds
if (scaleToFit3D(topCorner, bottomCorner, m_defaultFitIntoViewportMarginRate))
{
render();
}
}
bool QViewer::scaleToFit3D(double topCorner[3], double bottomCorner[3], double marginRate)
{
if (!hasInput())
{
return false;
}
// Calcular la width i height en coordenades de display
double displayTopLeft[3], displayBottomRight[3];
this->computeWorldToDisplay(topCorner[0], topCorner[1], topCorner[2], displayTopLeft);
this->computeWorldToDisplay(bottomCorner[0], bottomCorner[1], bottomCorner[2], displayBottomRight);
// Recalculem tenint en compte el display
double width, height;
width = fabs(displayTopLeft[0] - displayBottomRight[0]);
height = fabs(displayTopLeft[1] - displayBottomRight[1]);
QSize size = this->getRenderWindowSize();
double ratio = qMin(size.width() / width, size.height() / height);
double factor = ratio * (1.0 - marginRate);
return adjustCameraScaleFactor(factor);
}
bool QViewer::adjustCameraScaleFactor(double factor)
{
if (MathTools::isNaN(factor))
{
DEBUG_LOG("Scale factor is NaN. No scale factor will be applied.");
return false;
}
vtkRenderer *renderer = getRenderer();
if (!renderer)
{
DEBUG_LOG("Renderer is NULL. No scale factor will be applied.");
return false;
}
getActiveCamera()->Zoom(factor);
if (this->getInteractor()->GetLightFollowCamera())
{
renderer->UpdateLightsGeometryToFollowCamera();
}
return true;
}
//工具链
注册事件
void Q2DViewerExtension::initializeTools()
{
// Creem el tool manager
m_toolManager = new ToolManager(this);
// Obtenim les accions de cada tool que volem
m_zoomToolButton->setDefaultAction(m_toolManager->registerTool("ZoomTool"));
m_slicingToolButton->setDefaultAction(m_toolManager->registerTool("SlicingMouseTool"));
m_translateLeftToolButton->setDefaultAction(m_toolManager->registerTool("TranslateLeftTool"));
m_windowLevelLeftToolButton->setDefaultAction(m_toolManager->registerTool("WindowLevelLeftTool"));
m_toolManager->registerTool("TranslateTool");
m_toolManager->registerTool("WindowLevelTool");
m_referenceLinesToolButton->setDefaultAction(m_toolManager->registerTool("ReferenceLinesTool"));
void QViewer::setupInteraction()
{
Q_ASSERT(m_renderer);
// TODO Fer això aquí? o fer-ho en el tool manager?
this->getInteractor()->RemoveObservers(vtkCommand::LeftButtonPressEvent);
this->getInteractor()->RemoveObservers(vtkCommand::RightButtonPressEvent);
this->getInteractor()->RemoveObservers(vtkCommand::MouseWheelForwardEvent);
this->getInteractor()->RemoveObservers(vtkCommand::MouseWheelBackwardEvent);
this->getInteractor()->RemoveObservers(vtkCommand::MiddleButtonPressEvent);
this->getInteractor()->RemoveObservers(vtkCommand::CharEvent);
m_vtkQtConnections = vtkEventQtSlotConnect::New();
// Despatxa qualsevol event-> tools
m_vtkQtConnections->Connect(this->getInteractor(), vtkCommand::AnyEvent, this, SLOT(eventHandler(vtkObject*, unsigned long, void*, void*,
vtkCommand*)));
void QViewer::eventHandler(vtkObject *object, unsigned long vtkEvent, void *clientData, void *callData, vtkCommand *command)
{
.........
switch (vtkEvent)
{
case QVTKWidget::ContextMenuEvent:
case vtkCommand::LeftButtonPressEvent:
case vtkCommand::RightButtonPressEvent:
case vtkCommand::MiddleButtonPressEvent:
case vtkCommand::MouseWheelForwardEvent:
case vtkCommand::MouseWheelBackwardEvent:
m_mouseHasMoved = false;
setActive(true);
if (vtkEvent == vtkCommand::LeftButtonPressEvent && getInteractor()->GetRepeatCount() == 1)
{
emit doubleClicked();
}
break;
case vtkCommand::MouseMoveEvent:
m_mouseHasMoved = true;
break;
case vtkCommand::RightButtonReleaseEvent:
if (!m_mouseHasMoved)
{
contextMenuRelease();
}
break;
}
emit eventReceived(vtkEvent);
}
}
QViewer::QViewer(QWidget *parent)
: QWidget(parent), m_mainVolume(0), m_contextMenuActive(true), m_mouseHasMoved(false), m_voiLutData(0),
m_isRenderingEnabled(true), m_isActive(false)
{
m_lastAngleDelta = QPoint();
m_defaultFitIntoViewportMarginRate = 0.0;
m_vtkWidget = new QVTKWidget(this);
m_vtkWidget->setFocusPolicy(Qt::WheelFocus);
m_renderer = vtkRenderer::New();
m_windowToImageFilter = vtkWindowToImageFilter::New();
setupRenderWindow();
this->setCurrentViewPlane(OrthogonalPlane::XYPlane);
// Connectem els events
setupInteraction();
m_toolProxy = new ToolProxy(this);
connect(this, SIGNAL(eventReceived(unsigned long)), m_toolProxy, SLOT(forwardEvent(unsigned long)));
void ToolProxy::forwardEvent(unsigned long eventID)
{
// No es pot fer un foreach sobre un map perquè retorna parella d'elements, per això passem tots els elements del map a una QList.
QList<Tool*> toolsList = m_toolsMap.values();
foreach (Tool *tool, toolsList)
{
tool->handleEvent(eventID);
}
}
工具在下面代码中激活
void ToolManager::triggeredToolAction(const QString &toolName)
{
// TODO Cal repassar tot això. Hauria d'anar amb llistes internes de tools activades/desactivades
// obtenim l'acció que l'ha provocat
QAction *toolAction = getRegisteredToolAction(toolName);
if (toolAction)
{
// Si està checked és que s'ha d'activar, altrament desactivar
if (toolAction->isChecked())
{
activateTool(toolName);
}
else
{
deactivateTool(toolName);
}
}
else
{
DEBUG_LOG(QString("No hi ha cap tool Action per la tool anomenada: ") + toolName);
}
}
void ToolManager::activateTool(const QString &toolName)
{
// TODO Caldria comprovar si la tool es troba en un grup exclusiu per "fer fora" les altres tools
// en el cas que prescindíssim del mecanisme que fem servir amb QActionToolGroup
QList<ViewerToolConfigurationPairType> viewerConfigList = m_toolViewerMap.values(toolName);
ToolData *data = m_sharedToolDataRepository.value(toolName);
// Declarem aquestes variables per fer-ho més llegible
QViewer *viewer;
ToolConfiguration *configuration;
foreach (const ViewerToolConfigurationPairType &pair, viewerConfigList)
{
viewer = pair.first;
configuration = pair.second;
Tool *tool = 0;
// Hem de comprovar si el proxy ja té o no la tool
if (!viewer->getToolProxy()->isToolActive(toolName))
{
// Com que el proxy no té aquesta tool
// la produim i la posem a punt amb les dades i la configuració
tool = m_toolRegistry->getTool(toolName, viewer);
// Si no tenim cap configuració guardada, no cal fer res, es queda amb la que té per defecte
if (configuration)
{
tool->setConfiguration(configuration);
}
// Afegim la tool al proxy
viewer->getToolProxy()->addTool(tool);
// Comprovem les dades per si cal donar-n'hi
if (tool->hasSharedData())
{
// No hi són al repositori, les obtindrem de la pròpia tool i les registrarem al repositori
if (!data)
{
data = tool->getToolData();
m_sharedToolDataRepository[toolName] = data;
}
else
{
// Si ja les hem creat abans, li assignem les de la primera tool creada
tool->setToolData(data);
}
}
}
}
}
//单个按钮触发机制
m_slicingToolButton->setDefaultAction(m_toolManager->registerTool("SlicingMouseTool"));
connect(toolAction, SIGNAL(triggered()), m_toolsActionSignalMapper, SLOT(map()));
connect(m_toolsActionSignalMapper, SIGNAL(mapped(const QString&)), SLOT(triggeredToolAction(const QString&)));
void ToolManager::triggeredToolAction(const QString &toolName)
{
// TODO Cal repassar tot això. Hauria d'anar amb llistes internes de tools activades/desactivades
// obtenim l'acció que l'ha provocat
QAction *toolAction = getRegisteredToolAction(toolName);
if (toolAction)
{
// Si està checked és que s'ha d'activar, altrament desactivar
if (toolAction->isChecked())
{
activateTool(toolName);
}
else
{
deactivateTool(toolName);
}
}
else
{
DEBUG_LOG(QString("No hi ha cap tool Action per la tool anomenada: ") + toolName);
}
}
activateTool中激活相应的事件
void VolumeReader::read(Volume *volume)
{
this->executePixelDataReader(volume);
this->showMessageBoxWithLastError();
}
m_lastError = m_volumePixelDataReader->read(fileList);
if (m_lastError == VolumePixelDataReader::NoError)
{
// Tot ha anat ok, assignem les dades al volum
volume->setPixelData(m_volumePixelDataReader->getVolumePixelData());
runPostprocessors(volume);
fixSpacingIssues(volume);
}
else
{
volume->convertToNeutralVolume();
this->logWarningLastError(fileList);
}
void Volume::setPixelData(VolumePixelData *pixelData)
{
Q_ASSERT(pixelData != 0);
m_volumePixelData = pixelData;
// Set the number of phases to the new pixel data
m_volumePixelData->setNumberOfPhases(m_numberOfPhases);
}
更新slice分析
void Q2DViewer::updateSliceToDisplay(int value, SliceDimension dimension)
{
if (hasInput())
{
int oldSlice = getCurrentSlice();
int oldPhase = getCurrentPhase();
// First update the index of the corresponding dimension
switch (dimension)
{
case SpatialDimension:
getMainDisplayUnit()->setSlice(value);
updateSecondaryVolumesSlices();
break;
case TemporalDimension:
getMainDisplayUnit()->setPhase(value);
break;
}
// Then update display (image and associated annotations)
updateImageSlices();
void Q2DViewer::updateImageSlices()
{
// Ens assegurem que tenim dades vàlides
if (!getMainInput()->isPixelDataLoaded())
{
return;
}
// We only need to update the main vdu, since it will modify the camera and thus the others will also be updated
getMainDisplayUnit()->updateImageSlice(m_renderer->GetActiveCamera());
// TODO Si separem els renderers potser caldria aplicar-ho a cada renderer?
getRenderer()->ResetCameraClippingRange();
}
//此处更新相机焦点
void VolumeDisplayUnit::updateImageSlice(vtkCamera *camera)
{
if (!m_volume || !m_volume->isPixelDataLoaded())
{
return;
}
int imageIndex = getSlice();
int zIndex = this->getViewPlane().getZIndex();
double origin[3];
m_volume->getOrigin(origin);
double spacing[3];
m_volume->getSpacing(spacing);
double focalPoint[3];
camera->GetFocalPoint(focalPoint);
focalPoint[zIndex] = origin[zIndex] + imageIndex * spacing[zIndex];
camera->SetFocalPoint(focalPoint);
}
//最适合的填充窗口
void QViewer::fitRenderingIntoViewport()
{
// First we get the bounds of the current rendered item in world coordinates
double bounds[6];
getCurrentRenderedItemBounds(bounds);
double topCorner[3];
double bottomCorner[3];
for (int i = 0; i < 3; ++i)
{
topCorner[i] = bounds[i * 2];
bottomCorner[i] = bounds[i * 2 + 1];
}
// Scaling the viewport to fit the current item bounds
if (scaleToFit3D(topCorner, bottomCorner, m_defaultFitIntoViewportMarginRate))
{
render();
}
}
bool QViewer::scaleToFit3D(double topCorner[3], double bottomCorner[3], double marginRate)
{
if (!hasInput())
{
return false;
}
// Calcular la width i height en coordenades de display
double displayTopLeft[3], displayBottomRight[3];
this->computeWorldToDisplay(topCorner[0], topCorner[1], topCorner[2], displayTopLeft);
this->computeWorldToDisplay(bottomCorner[0], bottomCorner[1], bottomCorner[2], displayBottomRight);
// Recalculem tenint en compte el display
double width, height;
width = fabs(displayTopLeft[0] - displayBottomRight[0]);
height = fabs(displayTopLeft[1] - displayBottomRight[1]);
QSize size = this->getRenderWindowSize();
double ratio = qMin(size.width() / width, size.height() / height);
double factor = ratio * (1.0 - marginRate);
return adjustCameraScaleFactor(factor);
}
bool QViewer::adjustCameraScaleFactor(double factor)
{
if (MathTools::isNaN(factor))
{
DEBUG_LOG("Scale factor is NaN. No scale factor will be applied.");
return false;
}
vtkRenderer *renderer = getRenderer();
if (!renderer)
{
DEBUG_LOG("Renderer is NULL. No scale factor will be applied.");
return false;
}
getActiveCamera()->Zoom(factor);
if (this->getInteractor()->GetLightFollowCamera())
{
renderer->UpdateLightsGeometryToFollowCamera();
}
return true;
}
//工具链
注册事件
void Q2DViewerExtension::initializeTools()
{
// Creem el tool manager
m_toolManager = new ToolManager(this);
// Obtenim les accions de cada tool que volem
m_zoomToolButton->setDefaultAction(m_toolManager->registerTool("ZoomTool"));
m_slicingToolButton->setDefaultAction(m_toolManager->registerTool("SlicingMouseTool"));
m_translateLeftToolButton->setDefaultAction(m_toolManager->registerTool("TranslateLeftTool"));
m_windowLevelLeftToolButton->setDefaultAction(m_toolManager->registerTool("WindowLevelLeftTool"));
m_toolManager->registerTool("TranslateTool");
m_toolManager->registerTool("WindowLevelTool");
m_referenceLinesToolButton->setDefaultAction(m_toolManager->registerTool("ReferenceLinesTool"));
void QViewer::setupInteraction()
{
Q_ASSERT(m_renderer);
// TODO Fer això aquí? o fer-ho en el tool manager?
this->getInteractor()->RemoveObservers(vtkCommand::LeftButtonPressEvent);
this->getInteractor()->RemoveObservers(vtkCommand::RightButtonPressEvent);
this->getInteractor()->RemoveObservers(vtkCommand::MouseWheelForwardEvent);
this->getInteractor()->RemoveObservers(vtkCommand::MouseWheelBackwardEvent);
this->getInteractor()->RemoveObservers(vtkCommand::MiddleButtonPressEvent);
this->getInteractor()->RemoveObservers(vtkCommand::CharEvent);
m_vtkQtConnections = vtkEventQtSlotConnect::New();
// Despatxa qualsevol event-> tools
m_vtkQtConnections->Connect(this->getInteractor(), vtkCommand::AnyEvent, this, SLOT(eventHandler(vtkObject*, unsigned long, void*, void*,
vtkCommand*)));
void QViewer::eventHandler(vtkObject *object, unsigned long vtkEvent, void *clientData, void *callData, vtkCommand *command)
{
.........
switch (vtkEvent)
{
case QVTKWidget::ContextMenuEvent:
case vtkCommand::LeftButtonPressEvent:
case vtkCommand::RightButtonPressEvent:
case vtkCommand::MiddleButtonPressEvent:
case vtkCommand::MouseWheelForwardEvent:
case vtkCommand::MouseWheelBackwardEvent:
m_mouseHasMoved = false;
setActive(true);
if (vtkEvent == vtkCommand::LeftButtonPressEvent && getInteractor()->GetRepeatCount() == 1)
{
emit doubleClicked();
}
break;
case vtkCommand::MouseMoveEvent:
m_mouseHasMoved = true;
break;
case vtkCommand::RightButtonReleaseEvent:
if (!m_mouseHasMoved)
{
contextMenuRelease();
}
break;
}
emit eventReceived(vtkEvent);
}
}
QViewer::QViewer(QWidget *parent)
: QWidget(parent), m_mainVolume(0), m_contextMenuActive(true), m_mouseHasMoved(false), m_voiLutData(0),
m_isRenderingEnabled(true), m_isActive(false)
{
m_lastAngleDelta = QPoint();
m_defaultFitIntoViewportMarginRate = 0.0;
m_vtkWidget = new QVTKWidget(this);
m_vtkWidget->setFocusPolicy(Qt::WheelFocus);
m_renderer = vtkRenderer::New();
m_windowToImageFilter = vtkWindowToImageFilter::New();
setupRenderWindow();
this->setCurrentViewPlane(OrthogonalPlane::XYPlane);
// Connectem els events
setupInteraction();
m_toolProxy = new ToolProxy(this);
connect(this, SIGNAL(eventReceived(unsigned long)), m_toolProxy, SLOT(forwardEvent(unsigned long)));
void ToolProxy::forwardEvent(unsigned long eventID)
{
// No es pot fer un foreach sobre un map perquè retorna parella d'elements, per això passem tots els elements del map a una QList.
QList<Tool*> toolsList = m_toolsMap.values();
foreach (Tool *tool, toolsList)
{
tool->handleEvent(eventID);
}
}
工具在下面代码中激活
void ToolManager::triggeredToolAction(const QString &toolName)
{
// TODO Cal repassar tot això. Hauria d'anar amb llistes internes de tools activades/desactivades
// obtenim l'acció que l'ha provocat
QAction *toolAction = getRegisteredToolAction(toolName);
if (toolAction)
{
// Si està checked és que s'ha d'activar, altrament desactivar
if (toolAction->isChecked())
{
activateTool(toolName);
}
else
{
deactivateTool(toolName);
}
}
else
{
DEBUG_LOG(QString("No hi ha cap tool Action per la tool anomenada: ") + toolName);
}
}
void ToolManager::activateTool(const QString &toolName)
{
// TODO Caldria comprovar si la tool es troba en un grup exclusiu per "fer fora" les altres tools
// en el cas que prescindíssim del mecanisme que fem servir amb QActionToolGroup
QList<ViewerToolConfigurationPairType> viewerConfigList = m_toolViewerMap.values(toolName);
ToolData *data = m_sharedToolDataRepository.value(toolName);
// Declarem aquestes variables per fer-ho més llegible
QViewer *viewer;
ToolConfiguration *configuration;
foreach (const ViewerToolConfigurationPairType &pair, viewerConfigList)
{
viewer = pair.first;
configuration = pair.second;
Tool *tool = 0;
// Hem de comprovar si el proxy ja té o no la tool
if (!viewer->getToolProxy()->isToolActive(toolName))
{
// Com que el proxy no té aquesta tool
// la produim i la posem a punt amb les dades i la configuració
tool = m_toolRegistry->getTool(toolName, viewer);
// Si no tenim cap configuració guardada, no cal fer res, es queda amb la que té per defecte
if (configuration)
{
tool->setConfiguration(configuration);
}
// Afegim la tool al proxy
viewer->getToolProxy()->addTool(tool);
// Comprovem les dades per si cal donar-n'hi
if (tool->hasSharedData())
{
// No hi són al repositori, les obtindrem de la pròpia tool i les registrarem al repositori
if (!data)
{
data = tool->getToolData();
m_sharedToolDataRepository[toolName] = data;
}
else
{
// Si ja les hem creat abans, li assignem les de la primera tool creada
tool->setToolData(data);
}
}
}
}
}
//单个按钮触发机制
m_slicingToolButton->setDefaultAction(m_toolManager->registerTool("SlicingMouseTool"));
connect(toolAction, SIGNAL(triggered()), m_toolsActionSignalMapper, SLOT(map()));
connect(m_toolsActionSignalMapper, SIGNAL(mapped(const QString&)), SLOT(triggeredToolAction(const QString&)));
void ToolManager::triggeredToolAction(const QString &toolName)
{
// TODO Cal repassar tot això. Hauria d'anar amb llistes internes de tools activades/desactivades
// obtenim l'acció que l'ha provocat
QAction *toolAction = getRegisteredToolAction(toolName);
if (toolAction)
{
// Si està checked és que s'ha d'activar, altrament desactivar
if (toolAction->isChecked())
{
activateTool(toolName);
}
else
{
deactivateTool(toolName);
}
}
else
{
DEBUG_LOG(QString("No hi ha cap tool Action per la tool anomenada: ") + toolName);
}
}
activateTool中激活相应的事件