实现MultiBlock选择与高亮功能

在 ParaView/VTK 二次开发中,实现对 MultiBlockDataSet 中各个 Block 的选择和高亮是一个常见需求。本文将详细介绍如何实现点选、框选、多选等功能,并提供完整的代码实现。

功能概述

我们要实现的功能包括:

  • 点选:鼠标点击选中单个 Block
  • 框选:拖拽矩形区域选中多个 Block
  • Ctrl + 点击:多选/取消选择
  • 高亮显示:选中的 Block 以特殊效果显示
  • 同源检查:只能选择同一数据源下的 Block

核心技术点

1. 使用 vtkSMRenderViewProxy 进行硬件加速选择

ParaView 提供了 vtkSMRenderViewProxy 类来实现硬件加速的选择功能。相比逐点采样的软件选择,性能提升显著。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 获取 RenderViewProxy
vtkSMRenderViewProxy *renderViewProxy = m_renderView->getRenderViewProxy();

int rect[4] = {x1, y1, x2, y2};

vtkSmartPointer<vtkCollection> selectedRepresentations =
vtkSmartPointer<vtkCollection>::New();
vtkSmartPointer<vtkCollection> selectionSources =
vtkSmartPointer<vtkCollection>::New();

// 调用 SelectSurfaceCells 进行硬件加速选择
renderViewProxy->SelectSurfaceCells(rect, selectedRepresentations,
selectionSources, false);

2. 将 Cell Selection 转换为 Block Selection

SelectSurfaceCells 返回的是 Cell 级别的选择,需要使用 vtkSMSelectionHelper::ConvertSelection 转换为 Block 选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <vtkSMSelectionHelper.h>
#include <vtkSelectionNode.h>

// 获取 output port 和 selected source
pqOutputPort *opPort = pqRepr->getOutputPortFromInput();
vtkSMSourceProxy *selectedSource =
vtkSMSourceProxy::SafeDownCast(opPort->getSource()->getProxy());

// 将 cell selection 转换为 block selection
vtkSMSourceProxy *blockSelectionSource = vtkSMSourceProxy::SafeDownCast(
vtkSMSelectionHelper::ConvertSelection(
vtkSelectionNode::BLOCKS,
selectionSource,
selectedSource,
opPort->getPortNumber()));

// 从 block selection source 中提取 block IDs
vtkSMPropertyHelper blocksHelper(blockSelectionSource->GetProperty("Blocks"));
for (unsigned int j = 0; j < blocksHelper.GetNumberOfElements(); ++j) {
int blockId = blocksHelper.GetAsInt(j);
// 处理选中的 blockId
}

// 清理
blockSelectionSource->Delete();

3. 点选实现

使用 vtkSMRenderViewProxy::PickBlock 实现单点选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int performPick(int x, int y, pqPipelineSource **pickedSource) {
vtkSMRenderViewProxy *renderViewProxy = m_renderView->getRenderViewProxy();

// 调用 PickBlock 获取选中的 block
vtkSMProxy *reprProxy = nullptr;
vtkIdType flatIndex = renderViewProxy->PickBlock(x, y, &reprProxy);

if (flatIndex < 0 || !reprProxy)
return -1;

// 获取对应的 pqPipelineSource
pqServerManagerModel *smmodel = pqApplicationCore::instance()->getServerManagerModel();
pqDataRepresentation *pqRepr = smmodel->findItem<pqDataRepresentation*>(reprProxy);

if (pqRepr && pickedSource) {
*pickedSource = pqRepr->getInput();
}

return static_cast<int>(flatIndex);
}

4. 同源检查

在多选或框选时,只允许选择同一数据源下的 Block:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pqPipelineSource *currentSource = getCurrentSource();

// 单选时:不同源发出信号,让外部处理
if (!m_ctrlPressed) {
if (pickedSource && pickedSource != currentSource) {
emit otherSourcePicked(pickedSource);
return;
}
}

// Ctrl+多选时:不同源直接忽略
if (m_ctrlPressed) {
if (pickedSource && pickedSource != currentSource) {
return; // 不发信号,不选择
}
}

// 框选时:遍历结果,跳过不同源
for (int i = 0; i < selectedRepresentations->GetNumberOfItems(); ++i) {
pqPipelineSource *pickedSource = pqRepr->getInput();
if (pickedSource != currentSource)
continue; // 跳过不同源
// 处理选中的 block
}

高亮效果实现

方案一:特征边高亮(Feature Edges)

使用 vtkFeatureEdges 提取选中 Block 的特征边进行高亮显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vtkSmartPointer<vtkDataSetSurfaceFilter> surfaceFilter = 
vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
vtkSmartPointer<vtkFeatureEdges> featureEdges =
vtkSmartPointer<vtkFeatureEdges>::New();

featureEdges->BoundaryEdgesOn();
featureEdges->FeatureEdgesOn();
featureEdges->ManifoldEdgesOff();
featureEdges->NonManifoldEdgesOff();
featureEdges->SetFeatureAngle(30.0);

surfaceFilter->SetInputData(dataSet);
surfaceFilter->Update();

featureEdges->SetInputConnection(surfaceFilter->GetOutputPort());
featureEdges->Update();

方案二:表面覆盖高亮(Surface Overlay)

创建一个覆盖在选中 Block 表面的半透明/不透明 Actor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 使用单独的 overlay renderer 避免 Z-fighting
m_overlayRenderer = vtkSmartPointer<vtkRenderer>::New();
m_overlayRenderer->SetLayer(1);
m_overlayRenderer->SetPreserveDepthBuffer(true);
m_overlayRenderer->SetPreserveColorBuffer(false);

// 设置 RenderWindow 多层渲染
renderWindow->SetNumberOfLayers(2);
m_overlayRenderer->SetActiveCamera(mainRenderer->GetActiveCamera());
renderWindow->AddRenderer(m_overlayRenderer);

// 高亮 Actor 属性
vtkProperty *prop = m_highlightActor->GetProperty();
prop->SetRepresentationToSurface();
prop->SetColor(1.0, 1.0, 0.0); // 黄色
prop->SetOpacity(1.0); // 完全覆盖
prop->SetLighting(true);
prop->SetAmbient(0.6);
prop->SetDiffuse(0.4);

框选 UI 实现

自定义 RubberBand

由于 QRubberBand::setStyleSheet 通常不起作用,需要自定义绘制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class SelectionRubberBand : public QRubberBand {
public:
SelectionRubberBand(QWidget *parent = nullptr)
: QRubberBand(QRubberBand::Rectangle, parent) {}

void setColor(const QColor &color) { m_color = color; }

protected:
void paintEvent(QPaintEvent *) override {
QPainter painter(this);
// 填充半透明背景
QColor fillColor = m_color;
fillColor.setAlpha(50);
painter.fillRect(rect(), fillColor);
// 绘制边框
QPen pen(m_color);
pen.setWidth(2);
painter.setPen(pen);
painter.drawRect(rect().adjusted(0, 0, -1, -1));
}

private:
QColor m_color = QColor(0, 120, 215);
};

与 MultiBlockInspector 同步

选中 Block 后,需要同步更新 TreeWidget 的选择状态,并自动展开父节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void syncInspectorSelection() {
m_inspector->clearSelection();

for (QTreeWidgetItem *item : m_selectedItems) {
if (item) {
// 展开所有父节点
QTreeWidgetItem *parent = item->parent();
while (parent) {
parent->setExpanded(true);
parent = parent->parent();
}
item->setSelected(true);
}
}

// 滚动到第一个选中项
if (!m_selectedItems.isEmpty() && m_selectedItems.first()) {
m_inspector->scrollToItem(m_selectedItems.first(),
QAbstractItemView::EnsureVisible);
}
}

关键头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <pqApplicationCore.h>
#include <pqDataRepresentation.h>
#include <pqOutputPort.h>
#include <pqPipelineSource.h>
#include <pqRenderView.h>
#include <pqServerManagerModel.h>
#include <vtkCollection.h>
#include <vtkSelectionNode.h>
#include <vtkSMPropertyHelper.h>
#include <vtkSMRenderViewProxy.h>
#include <vtkSMRepresentationProxy.h>
#include <vtkSMSelectionHelper.h>
#include <vtkSMSourceProxy.h>

总结

实现 ParaView 中 MultiBlock 的选择功能,核心是利用 vtkSMRenderViewProxy 提供的硬件加速选择接口,配合 vtkSMSelectionHelper 进行选择类型转换。高亮效果可以根据需求选择特征边或表面覆盖方案。通过事件过滤器拦截鼠标事件,可以实现点选、框选、多选等丰富的交互功能。

参考资料


实现MultiBlock选择与高亮功能
http://aojian-blog.oss-cn-wuhan-lr.aliyuncs.com/2025/12/04/selection/
作者
遨见
发布于
2025年12月4日
许可协议