前言
在基于3D Slicer的医学影像项目中可能涉及到自定义光标的情况 ,这里给出slicer光标设置流程分析,并提供一种实现自定义光标的方法。
一、slicer光标设置流程分析
slicer是基于QT和VTK的,其光标设置也是如此,实际上使用的是VTK和QT的结合。
其光标设置流程包括两种模式:
-
通过display manager
-
通过sliceview直调
需要注意的是:通过display manager 实现的途径中都是使用vtk cursor值,比如VTK_CURSOR_ARROW,VTK_CURSOR_DEFAULT,VTK_CURSOR_CUSTOM,只在最后设置到QWidget时才将其转换为 Qt::ArrowCursor这样的qt光标对象,转换函数为QVTKRenderWindowAdapter::QVTKInternals::setCursor(int vtk_cursor)。
而直调方式则是直接设置QCursor到QWidget中。
此外,由于slicer的qSlicerMouseModeToolBarPrivate::updateCursor()会根据当前action不同以及vtkMRMLInteractionNode模式不同,动态设置光标,会将直接通过窗口设置的光标覆盖。
二、自定义光标
实现自定义光标,需要首先把slicer自己的qSlicerMouseModeToolBarPrivate::updateCursor禁用,防止其修改光标。然后再按照display manager设置光标的流程设置光标。
以vtkMRMLCrosshairDisplayableManager类为例,这里给出的方式是
给displaymanager添加设置光标的回调函数:
void SetMouseCursor(int cursor) override;
typedef std::function<int(vtkMRMLNode* viewnode, int widgetstate,int cursor)> CustomCursorCallback;
CustomCursorCallback customcursorCallback;
void SetCustomCursorCallback(CustomCursorCallback callback);
重写SetMouseCursor,由其调用回调函数:
void vtkMRMLCrosshairDisplayableManager::SetMouseCursor(int cursor) {
this->Superclass::SetMouseCursor(cursor);//must call first let renderwindow record the cursor
int done = 0;
if (customcursorCallback && this->Internal->SliceIntersectionWidget) {
done = customcursorCallback(this->GetMRMLDisplayableNode(), this->Internal->SliceIntersectionWidget->GetWidgetState(),cursor);
}
if(done == 0) {
this->Superclass::SetMouseCursor(cursor);
}
}
回调函数负责将vtk cursor值转为所需光标,并设置给对应的sliceview窗口,示例如下:
int vtkSlicerEditorLogic::CustomCursorCallbackForCrosshair(vtkMRMLNode* viewnode, int widgetstate, int cursor) {
QCursor qcursor;
if (cursor == VTK_CURSOR_CUSTOM) {
QIcon icon(":/Icons/custom.png");
qcursor = QCursor(icon.pixmap(icon.actualSize(QSize(24, 24))));
}
else if (cursor == VTK_CURSOR_ARROW) {
QIcon icon(":/Icons/arrow.png");
qcursor = QCursor(icon.pixmap(icon.actualSize(QSize(24, 24))));
}
else {
return 0;
}
qSlicerLayoutManager* layoutManager = GetApplication()->layoutManager();
if (layoutManager) {
QWidget* viewwidget = layoutManager->viewWidget(viewnode);
qMRMLSliceWidget* slicewidget = dynamic_cast<qMRMLSliceWidget*>(viewwidget);
if (slicewidget) {
slicewidget->sliceView()->setViewCursor(qcursor);
}
return 1;
}
return 0;
}