|
|
#include "nmWxGridVTKContainerWidget.h"
|
|
|
// Qt 核心头文件
|
|
|
#include <QVBoxLayout>
|
|
|
#include <QFrame>
|
|
|
#include <QDebug>
|
|
|
#include <QPixmap> // 用于捕获部件内容为图像
|
|
|
#include <QFileDialog> // 用于文件保存对话框
|
|
|
#include <QMessageBox> // 用于显示消息框
|
|
|
#include <QApplication> // 用于访问全局应用程序对象 (如剪贴板)
|
|
|
#include <QClipboard> // 用于剪贴板操作
|
|
|
#include <QPrinter> // 用于打印机抽象
|
|
|
#include <QPrintDialog> // 用于打印设置对话框
|
|
|
#include <QPrintPreviewDialog> // 用于打印预览对话框
|
|
|
#include <QPainter> // 用于绘制打印内容
|
|
|
#include <QMenu> // 用于创建右键上下文菜单
|
|
|
#include <QAction> // 用于菜单动作
|
|
|
#include <QLinearGradient>
|
|
|
#include <QCoreApplication>
|
|
|
|
|
|
// VTK 核心头文件
|
|
|
#include <vtkAutoInit.h>
|
|
|
VTK_MODULE_INIT(vtkRenderingOpenGL)
|
|
|
VTK_MODULE_INIT(vtkInteractionStyle)
|
|
|
|
|
|
// VTK 可视化相关
|
|
|
#include <vtkActor.h>
|
|
|
#include <vtkDataSetMapper.h>
|
|
|
#include <vtkProperty.h>
|
|
|
#include <vtkRenderWindow.h>
|
|
|
#include <vtkRenderer.h>
|
|
|
#include <vtkSmartPointer.h>
|
|
|
#include <vtkUnstructuredGrid.h>
|
|
|
#include <vtkCellData.h>
|
|
|
#include <vtkLookupTable.h>
|
|
|
#include <vtkDataArray.h>
|
|
|
#include <QVTKWidget.h>
|
|
|
#include <vtkRendererCollection.h>
|
|
|
#include <vtkWindowToImageFilter.h>
|
|
|
#include <vtkImageData.h>
|
|
|
#include <vtkImageFlip.h>
|
|
|
#include <vtkPNGWriter.h>
|
|
|
#include <vtkUnsignedCharArray.h>
|
|
|
#include <vtkPointData.h>
|
|
|
|
|
|
// 项目特定头文件
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
|
|
|
// Windows 库链接 (保留必要项)
|
|
|
#pragma comment(lib,"User32.lib")
|
|
|
#pragma comment(lib,"gdi32.lib")
|
|
|
#pragma comment(lib,"OpenGL32.lib")
|
|
|
#pragma comment(lib,"AdvAPI32.lib")
|
|
|
#pragma comment(lib,"shell32.lib")
|
|
|
|
|
|
nmWxGridVTKContainerWidget::nmWxGridVTKContainerWidget(QWidget *parent) : QWidget(parent)
|
|
|
{
|
|
|
m_pVtkWidget = nullptr;
|
|
|
this->initLayout();
|
|
|
this->initVTKWidget();
|
|
|
}
|
|
|
|
|
|
QImage nmWxGridVTKContainerWidget::createQImage1(int nWidth, int nHeight, vtkUnsignedCharArray* pScalars)
|
|
|
{
|
|
|
QImage qImage(nWidth, nHeight, QImage::Format_ARGB32);
|
|
|
vtkIdType tupleIndex = 0;
|
|
|
int nImageBitIndex = 0;
|
|
|
QRgb* pImageBits = (QRgb*)qImage.bits();
|
|
|
unsigned char* scalarTuples = pScalars->GetPointer(0);
|
|
|
|
|
|
for(int j = 0; j < nHeight; j++) {
|
|
|
for(int i = 0; i < nWidth; i++) {
|
|
|
unsigned char* tuple = scalarTuples + (tupleIndex++);
|
|
|
QRgb color = qRgba(tuple[0], tuple[0], tuple[0], 255);
|
|
|
*(pImageBits + (nImageBitIndex++)) = color;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return qImage;
|
|
|
}
|
|
|
|
|
|
QImage nmWxGridVTKContainerWidget::createQImage2(int nWidth, int nHeight, vtkUnsignedCharArray* pScalars)
|
|
|
{
|
|
|
QImage qImage(nWidth, nHeight, QImage::Format_ARGB32);
|
|
|
vtkIdType tupleIndex = 0;
|
|
|
int nImageBitIndex = 0;
|
|
|
QRgb* pImageBits = (QRgb*)qImage.bits();
|
|
|
unsigned char* scalarTuples = pScalars->GetPointer(0);
|
|
|
|
|
|
for(int j = 0; j < nHeight; j++) {
|
|
|
for(int i = 0; i < nWidth; i++) {
|
|
|
unsigned char* tuple = scalarTuples + (tupleIndex++ * 2);
|
|
|
QRgb color = qRgba(tuple[0], tuple[0], tuple[0], tuple[1]);
|
|
|
*(pImageBits + (nImageBitIndex++)) = color;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return qImage;
|
|
|
}
|
|
|
|
|
|
QImage nmWxGridVTKContainerWidget::createQImage3(int nWidth, int nHeight, vtkUnsignedCharArray* pScalars)
|
|
|
{
|
|
|
QImage qImage(nWidth, nHeight, QImage::Format_ARGB32);
|
|
|
vtkIdType tupleIndex = 0;
|
|
|
int nImageBitIndex = 0;
|
|
|
QRgb* pImageBits = (QRgb*)qImage.bits();
|
|
|
unsigned char* scalarTuples = pScalars->GetPointer(0);
|
|
|
|
|
|
for(int j = 0; j < nHeight; j++) {
|
|
|
for(int i = 0; i < nWidth; i++) {
|
|
|
unsigned char* tuple = scalarTuples + (tupleIndex++ * 3);
|
|
|
QRgb color = qRgba(tuple[0], tuple[1], tuple[2], 255);
|
|
|
*(pImageBits + (nImageBitIndex++)) = color;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return qImage;
|
|
|
}
|
|
|
|
|
|
QImage nmWxGridVTKContainerWidget::createQImage4(int nWidth, int nHeight, vtkUnsignedCharArray* pScalars)
|
|
|
{
|
|
|
QImage qImage(nWidth, nHeight, QImage::Format_ARGB32);
|
|
|
vtkIdType tupleIndex = 0;
|
|
|
int nImageBitIndex = 0;
|
|
|
QRgb* pImageBits = (QRgb*)qImage.bits();
|
|
|
unsigned char* scalarTuples = pScalars->GetPointer(0);
|
|
|
|
|
|
for(int j = 0; j < nHeight; j++) {
|
|
|
for(int i = 0; i < nWidth; i++) {
|
|
|
unsigned char* tuple = scalarTuples + (tupleIndex++ * 4);
|
|
|
QRgb color = qRgba(tuple[0], tuple[1], tuple[2], tuple[3]);
|
|
|
*(pImageBits + (nImageBitIndex++)) = color;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return qImage;
|
|
|
}
|
|
|
|
|
|
QImage nmWxGridVTKContainerWidget::createQImage(vtkImageData* pImageData)
|
|
|
{
|
|
|
if(!pImageData)
|
|
|
return QImage();
|
|
|
|
|
|
int nWidth = pImageData->GetDimensions()[0];
|
|
|
int nHeight = pImageData->GetDimensions()[1];
|
|
|
vtkUnsignedCharArray* pScalars = vtkUnsignedCharArray::SafeDownCast(pImageData->GetPointData()->GetScalars());
|
|
|
|
|
|
if(!nWidth || !nHeight || !pScalars)
|
|
|
return QImage();
|
|
|
|
|
|
switch(pScalars->GetNumberOfComponents()) {
|
|
|
case 1:
|
|
|
return createQImage1(nWidth, nHeight, pScalars);
|
|
|
|
|
|
case 2:
|
|
|
return createQImage2(nWidth, nHeight, pScalars);
|
|
|
|
|
|
case 3:
|
|
|
return createQImage3(nWidth, nHeight, pScalars);
|
|
|
|
|
|
case 4:
|
|
|
return createQImage4(nWidth, nHeight, pScalars);
|
|
|
}
|
|
|
|
|
|
return QImage();
|
|
|
}
|
|
|
|
|
|
// --- **新增:事件过滤器实现** ---
|
|
|
bool nmWxGridVTKContainerWidget::eventFilter(QObject *pObj, QEvent *event)
|
|
|
{
|
|
|
// 检查事件是否来自我们的 VTK 部件,并且是鼠标按下事件
|
|
|
if(pObj == m_pVtkWidget && event->type() == QEvent::MouseButtonPress) {
|
|
|
QMouseEvent *pMouseEVent = static_cast<QMouseEvent*>(event);
|
|
|
|
|
|
if(pMouseEVent->button() == Qt::RightButton) {
|
|
|
// 在右键点击 m_pVtkWidget 时,直接显示我们的自定义菜单
|
|
|
QMenu menu(this); // 菜单的父对象仍然是 nmWxGridVTKContainerWidget
|
|
|
|
|
|
QString appDir = QCoreApplication::applicationDirPath();
|
|
|
appDir = appDir.section('/', 0, -2); // 获取上一级目录
|
|
|
|
|
|
// 设置菜单图标
|
|
|
QIcon saveIcon(appDir + "/Res/Icon/SaveImg.png");
|
|
|
QIcon copyIcon(appDir + "/Res/Icon/Copy.png");
|
|
|
QIcon printIcon(appDir + "/Res/Icon/Print.png");
|
|
|
QIcon printPreviewIcon(appDir + "/Res/Icon/PrePrint.png");
|
|
|
|
|
|
// 添加“保存为图片”动作,并为其添加图标
|
|
|
QAction *pSaveAction = new QAction(saveIcon, tr("Save as Image"), this);
|
|
|
connect(pSaveAction, SIGNAL(triggered()), this, SLOT(saveWidgetAsImage()));
|
|
|
menu.addAction(pSaveAction);
|
|
|
|
|
|
// 添加“复制图片”动作,并为其添加图标
|
|
|
QAction *pCopyAction = new QAction(copyIcon, tr("Copy Image"), this);
|
|
|
connect(pCopyAction, SIGNAL(triggered()), this, SLOT(copyWidgetImage()));
|
|
|
menu.addAction(pCopyAction);
|
|
|
|
|
|
menu.addSeparator(); // 添加分隔线
|
|
|
|
|
|
// 添加“打印”动作,并为其添加图标
|
|
|
QAction *pPrintAction = new QAction(printIcon, tr("Print"), this);
|
|
|
connect(pPrintAction, SIGNAL(triggered()), this, SLOT(printWidget()));
|
|
|
menu.addAction(pPrintAction);
|
|
|
|
|
|
// 添加“打印预览”动作,并为其添加图标
|
|
|
QAction *pPrintPreviewAction = new QAction(printPreviewIcon, tr("Print Preview"), this);
|
|
|
connect(pPrintPreviewAction, SIGNAL(triggered()), this, SLOT(printPreviewWidget()));
|
|
|
menu.addAction(pPrintPreviewAction);
|
|
|
|
|
|
menu.exec(pMouseEVent->globalPos()); // 在鼠标的全局位置执行菜单
|
|
|
|
|
|
return true; // **事件已处理,停止 QVTKWidget 的进一步处理**
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 对于其他事件或对象,将它们传递给基类的事件过滤器
|
|
|
return QWidget::eventFilter(pObj, event);
|
|
|
}
|
|
|
|
|
|
void nmWxGridVTKContainerWidget::initVTKWidget()
|
|
|
{
|
|
|
// 从数据中心获取当前非结构化网格对象
|
|
|
nmDataAnalyzeManager* pCurDataManager = nmDataAnalyzeManager::getCurrentInstance();
|
|
|
Q_ASSERT(nullptr != pCurDataManager);
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> pGrid = pCurDataManager->getUnstructuredGrid();
|
|
|
Q_ASSERT(nullptr != pGrid);
|
|
|
|
|
|
// 创建自定义 nmWxQVTKWidget 的实例。
|
|
|
// 此部件将处理其自身的上下文菜单事件。
|
|
|
m_pVtkWidget = new QVTKWidget(this); // 'this' 是 QVTKWidget 的父部件。
|
|
|
m_pMainLayout->addWidget(m_pVtkWidget); // 将 VTK 部件添加到 m_pMainLayout 的布局中。
|
|
|
|
|
|
// 安装事件过滤器
|
|
|
m_pVtkWidget->installEventFilter(this);
|
|
|
|
|
|
// 创建映射器
|
|
|
vtkSmartPointer<vtkDataSetMapper> pMapper = vtkSmartPointer<vtkDataSetMapper>::New();
|
|
|
pMapper->SetInputData(pGrid); // 传入非结构化网格
|
|
|
|
|
|
// 检查是否有Material场数据
|
|
|
vtkSmartPointer<vtkCellData> pCellData = pGrid->GetCellData();
|
|
|
vtkDataArray* pMaterialArray = pCellData->GetArray("Material");
|
|
|
|
|
|
if(pMaterialArray) {
|
|
|
// 创建颜色映射表
|
|
|
vtkSmartPointer<vtkLookupTable> pLut = vtkSmartPointer<vtkLookupTable>::New();
|
|
|
pLut->SetNumberOfColors(256);
|
|
|
pLut->SetHueRange(0.67, 0.0); // 从蓝色到红色
|
|
|
pLut->SetSaturationRange(1.0, 1.0);
|
|
|
pLut->SetValueRange(1.0, 1.0);
|
|
|
pLut->Build();
|
|
|
|
|
|
pMapper->SetScalarModeToUseCellData();
|
|
|
pMapper->SelectColorArray("Material");
|
|
|
pMapper->SetScalarRange(pMaterialArray->GetRange());
|
|
|
pMapper->SetLookupTable(pLut);
|
|
|
}
|
|
|
|
|
|
// 创建actor并设置属性
|
|
|
|
|
|
vtkSmartPointer<vtkActor> pActor = vtkSmartPointer<vtkActor>::New();
|
|
|
pActor->SetMapper(pMapper);
|
|
|
vtkProperty* pActorProperty = pActor->GetProperty();
|
|
|
pActorProperty->SetRepresentationToSurface();
|
|
|
pActorProperty->EdgeVisibilityOn(); // 显示边缘
|
|
|
pActorProperty->SetEdgeVisibility(true); // 允许交互
|
|
|
pActorProperty->SetColor(0, 120.0 / 255.0, 215.0 / 255.0); // 设置面的颜色
|
|
|
|
|
|
// 创建渲染器并设置背景
|
|
|
vtkSmartPointer<vtkRenderer> pRenderer = vtkSmartPointer<vtkRenderer>::New();
|
|
|
pRenderer->AddActor(pActor);
|
|
|
pRenderer->SetGradientBackground(true);
|
|
|
pRenderer->SetBackground(0.67, 0.82, 0.94); // 浅蓝
|
|
|
pRenderer->SetBackground2(0.0, 0.5, 1.0); // 深蓝
|
|
|
pRenderer->ResetCamera();
|
|
|
|
|
|
// 创建渲染窗口
|
|
|
vtkSmartPointer<vtkRenderWindow> pRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
|
|
|
pRenderWindow->AddRenderer(pRenderer);
|
|
|
|
|
|
// 将 VTK 渲染窗口设置到我们的 nmWxQVTKWidget。
|
|
|
m_pVtkWidget->SetRenderWindow(pRenderWindow); // 设置渲染窗口
|
|
|
pRenderWindow->Render(); // 执行初始渲染
|
|
|
}
|
|
|
|
|
|
void nmWxGridVTKContainerWidget::initLayout()
|
|
|
{
|
|
|
m_pMainLayout = new QVBoxLayout(this); // 设置当前Widget的布局
|
|
|
|
|
|
// 移除布局的默认边距和间距
|
|
|
m_pMainLayout->setContentsMargins(0, 0, 0, 0); // 设置边距为0
|
|
|
m_pMainLayout->setSpacing(0); // 设置子控件间距为0
|
|
|
}
|
|
|
|
|
|
// --- 图片和打印操作 ---
|
|
|
|
|
|
// saveWidgetAsImage: 将 m_pVtkWidget 的内容保存到图像文件。
|
|
|
void nmWxGridVTKContainerWidget::saveWidgetAsImage()
|
|
|
{
|
|
|
// 检查 QVTKWidget 及其底层 VTK 渲染窗口是否已初始化。
|
|
|
if(!m_pVtkWidget || !m_pVtkWidget->GetRenderWindow()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 将 QVTKWidget 的当前内容抓取为 QPixmap。
|
|
|
QPixmap pixmap = QPixmap::grabWidget(m_pVtkWidget);
|
|
|
|
|
|
// 打开文件保存对话框,让用户选择保存位置和格式。
|
|
|
QString sFileName = QFileDialog::getSaveFileName(this,
|
|
|
tr("Save Image"),
|
|
|
"", // 默认目录 (空字符串表示当前工作目录)
|
|
|
tr("PNG Image (*.png);;JPEG Image (*.jpg);;BMP Image (*.bmp)")); // 文件过滤器
|
|
|
|
|
|
// 如果用户提供了文件名 (没有取消对话框)。
|
|
|
if(!sFileName.isEmpty()) {
|
|
|
// 尝试将 pixmap 保存到选定的文件。
|
|
|
pixmap.save(sFileName);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// copyWidgetImage: 将 m_pVtkWidget 的内容复制到剪贴板。
|
|
|
void nmWxGridVTKContainerWidget::copyWidgetImage()
|
|
|
{
|
|
|
// 检查 QVTKWidget 及其底层 VTK 渲染窗口是否已初始化。
|
|
|
if(!m_pVtkWidget || !m_pVtkWidget->GetRenderWindow()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 将 QVTKWidget 的当前内容抓取为 QPixmap。
|
|
|
QPixmap pixmap = QPixmap::grabWidget(m_pVtkWidget);
|
|
|
|
|
|
// 获取全局剪贴板实例并设置抓取的 pixmap。
|
|
|
QClipboard *pClipboard = QApplication::clipboard();
|
|
|
pClipboard->setPixmap(pixmap);
|
|
|
}
|
|
|
|
|
|
// printWidget: 打印 m_pVtkWidget 的内容。
|
|
|
void nmWxGridVTKContainerWidget::printWidget()
|
|
|
{
|
|
|
// 检查 QVTKWidget 及其底层 VTK 渲染窗口是否已初始化。
|
|
|
if(!m_pVtkWidget || !m_pVtkWidget->GetRenderWindow()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QPrinter printer; // 创建 QPrinter 对象。
|
|
|
QPrintDialog printDialog(&printer, this); // 创建用于打印设置的打印对话框。
|
|
|
|
|
|
// 如果用户接受打印对话框(点击“打印”)。
|
|
|
if(printDialog.exec() == QDialog::Accepted) {
|
|
|
// 调用 renderWidgetForPrint 槽函数,将打印逻辑统一。
|
|
|
// 这样,实际打印也会应用缩放和居中。
|
|
|
renderWidgetForPrint(&printer);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// printPreviewWidget: 显示 m_pVtkWidget 的打印预览。
|
|
|
void nmWxGridVTKContainerWidget::printPreviewWidget()
|
|
|
{
|
|
|
// 检查 QVTKWidget 及其底层 VTK 渲染窗口是否已初始化。
|
|
|
if(!m_pVtkWidget || !m_pVtkWidget->GetRenderWindow()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QPrinter printer(QPrinter::HighResolution); // 创建高分辨率打印机以获得更好的预览质量。
|
|
|
QPrintPreviewDialog preview(&printer, this); // 创建打印预览对话框。
|
|
|
preview.resize(800, 600); // 设置预览窗体大小
|
|
|
|
|
|
// 将预览对话框的 paintRequested 信号连接到我们的自定义渲染槽。
|
|
|
// 当预览对话框需要绘制页面时,将调用此槽。
|
|
|
connect(&preview, SIGNAL(paintRequested(QPrinter*)),
|
|
|
this, SLOT(renderWidgetForPrint(QPrinter*)));
|
|
|
|
|
|
preview.exec(); // 显示打印预览对话框。
|
|
|
}
|
|
|
|
|
|
// renderWidgetForPrint: 辅助槽函数,用于渲染 m_pVtkWidget 的内容以进行打印/预览。
|
|
|
void nmWxGridVTKContainerWidget::renderWidgetForPrint(QPrinter *printer)
|
|
|
{
|
|
|
// 调用辅助函数获取包含VTK渲染内容的QImage
|
|
|
QImage image = getVTKRenderWindowAsImage();
|
|
|
|
|
|
if(image.isNull()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QPainter painter(printer); // 创建与给定打印机关联的 QPainter。
|
|
|
|
|
|
// 计算缩放因子,以按比例将部件内容适应到打印页面。
|
|
|
// 这里使用捕获到的QImage的尺寸,而不是QVTKWidget的尺寸,因为QImage已经是实际渲染内容的准确捕获。
|
|
|
double xscale = printer->pageRect().width() / (double)image.width();
|
|
|
double yscale = printer->pageRect().height() / (double)image.height();
|
|
|
// 使用较小的缩放因子,以确保整个内容完全适应页面内,不会被裁剪。
|
|
|
double scale = qMin(xscale, yscale);
|
|
|
|
|
|
painter.scale(scale, scale); // 然后应用缩放变换
|
|
|
|
|
|
// 将捕获到的 QImage 绘制到打印机上。
|
|
|
// 这样,VTK渲染的所有内容(包括渐变背景)都会被完整地打印出来。
|
|
|
painter.drawImage(0, 0, image);
|
|
|
}
|
|
|
|
|
|
QImage nmWxGridVTKContainerWidget::getVTKRenderWindowAsImage()
|
|
|
{
|
|
|
// 检查 QVTKWidget 及其底层的 VTK 渲染窗口是否已初始化。
|
|
|
if(!m_pVtkWidget || !m_pVtkWidget->GetRenderWindow()) {
|
|
|
qWarning() << "QVTKWidget or RenderWindow is not initialized.";
|
|
|
return QImage(); // 返回空图像
|
|
|
}
|
|
|
|
|
|
// 获取 VTK 渲染窗口的指针
|
|
|
vtkRenderWindow* pRenderWindow = m_pVtkWidget->GetRenderWindow();
|
|
|
|
|
|
// 确保在捕获前渲染最新帧
|
|
|
pRenderWindow->Render();
|
|
|
|
|
|
// 使用 vtkWindowToImageFilter 将渲染窗口的内容转换为 VTK 图像数据
|
|
|
vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter =
|
|
|
vtkSmartPointer<vtkWindowToImageFilter>::New();
|
|
|
windowToImageFilter->SetInput(pRenderWindow);
|
|
|
// 明确设置为 RGB
|
|
|
windowToImageFilter->SetInputBufferTypeToRGB();
|
|
|
// 从后台缓冲区读取以确保完整渲染
|
|
|
windowToImageFilter->ReadFrontBufferOff();
|
|
|
windowToImageFilter->Update(); // 执行图像转换
|
|
|
|
|
|
// 获取 VTK 图像数据 (未经翻转)
|
|
|
vtkImageData* pImageData = windowToImageFilter->GetOutput();
|
|
|
|
|
|
// VTK 图像数据的原点在左下角,而 Qt 的 QImage 原点在左上角,需要垂直翻转
|
|
|
vtkSmartPointer<vtkImageFlip> flip = vtkSmartPointer<vtkImageFlip>::New();
|
|
|
flip->SetInputData(pImageData);
|
|
|
//flip->SetInputConnection(windowToImageFilter->GetOutputPort()); // 用这个方法翻转后图像显示的是黑色的!!!
|
|
|
flip->SetFilteredAxis(1); // 翻转 Y 轴(垂直翻转)
|
|
|
flip->Update(); // 翻转操作在这里执行
|
|
|
|
|
|
// 获取翻转后的 VTK 图像数据
|
|
|
vtkImageData* pFlippedImageData = flip->GetOutput(); // 获取翻转后的数据
|
|
|
|
|
|
return createQImage(pFlippedImageData);
|
|
|
}
|