QVTKOpenGLNativeWidget中绘制vtkButtonWidget
本文探讨了如何结合Qt和VTK(Visualization Toolkit)开发图形用户界面(GUI),通过一个具体的案例,详细介绍了如何在Qt应用程序中嵌入VTK渲染器,并创建交互式的图形元素,如按钮,以实现更丰富的用户体验。文章还深入剖析了如何处理按钮点击事件,以及如何将Qt的图像数据转换为VTK格式。
介绍: 在现代软件开发中,图形用户界面(GUI)的设计和开发是至关重要的一部分。Qt作为一种流行的跨平台应用程序框架,提供了丰富的GUI开发工具和功能。而VTK作为一个强大的可视化工具包,用于创建复杂的3D图形和数据可视化。本文将介绍如何结合Qt和VTK,利用它们各自的优势,开发出功能丰富、交互性强的图形用户界面。
-
Qt与VTK的结合 Qt和VTK可以无缝地集成在一起,从而实现强大的图形用户界面和数据可视化功能。在本文的示例代码中,我们首先创建了一个Qt应用程序,并使用QVTKOpenGLNativeWidget类创建了一个VTK渲染窗口。这样就可以在Qt应用程序中嵌入VTK渲染器,实现图形界面和数据可视化的结合。
int main(int argc, char** argv) { QApplication a(argc, argv); QVTKOpenGLNativeWidget w; // 创建渲染器 vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); auto reWin = w.renderWindow(); reWin->AddRenderer(renderer); ... w.show(); ... }
-
创建交互式按钮 在我们的示例中,我们使用vtkButtonWidget类创建了一个交互式按钮。这个按钮可以响应用户的点击事件,并执行相应的操作。为了在按钮上显示图像,我们使用了vtkTexturedButtonRepresentation2D类,并将按钮的纹理设置为图像数据。通过这种方式,我们可以在图形界面中创建交互式的按钮,为用户提供更友好的操作体验。
// 创建按钮的2D表示 vtkSmartPointer<vtkTexturedButtonRepresentation2D> btnRepresentation2D = vtkSmartPointer<vtkTexturedButtonRepresentation2D>::New(); btnRepresentation2D->SetPlaceFactor(1); btnRepresentation2D->SetNumberOfStates(2); vtkSmartPointer<vtkPNGReader> reader0 = vtkSmartPointer<vtkPNGReader>::New(); QString tempFilePath0 = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/poweroff.png"; QFile::copy(":/resources/poweroff.png", tempFilePath0); reader0->SetFileName(tempFilePath0.toStdString().c_str()); reader0->Update(); btnRepresentation2D->SetButtonTexture(0, reader0->GetOutput()); QImage poweron = QImage(":/resources/poweron.png"); btnRepresentation2D->SetButtonTexture(1, QImage2vtkImageData(poweron)); // 创建按钮并设置表示 vtkSmartPointer<vtkButtonWidget> btnWidget = vtkSmartPointer<vtkButtonWidget>::New(); btnWidget->SetInteractor(w.interactor()); btnWidget->SetRepresentation(btnRepresentation2D); //btnWidget->SetEnabled(1); btnWidget->On(); // 创建按钮点击事件处理命令对象 vtkSmartPointer<ButtonCallback> callback = vtkSmartPointer<ButtonCallback>::New(); btnWidget->AddObserver(vtkCommand::StateChangedEvent, callback);
-
处理按钮点击事件为了处理按钮的点击事件,我们创建了一个自定义的vtkCommand类,并重写了它的Execute方法。在Execute方法中,我们检查了事件的类型,并根据需要执行相应的操作。通过这种方式,我们可以实现对按钮点击事件的灵活处理,从而实现更加复杂的交互功能。
// 创建一个命令类来处理按钮点击事件 class ButtonCallback : public vtkCommand { public: static ButtonCallback* New() { auto result = new ButtonCallback(); result->InitializeObjectBase(); return result; } virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData) override { // 检查事件类型 if (eventId == vtkCommand::StateChangedEvent) { // 处理按钮状态变化事件 vtkButtonWidget* buttonWidget = reinterpret_cast<vtkButtonWidget*>(caller); qDebug() << vtkCommand::GetStringFromEventId(eventId); vtkButtonRepresentation* representation = reinterpret_cast<vtkButtonRepresentation*>(buttonWidget->GetRepresentation()); qDebug() << "vtkButtonWidget State: " << representation->GetState(); } } };
当然也是可以添加Qt的UI元素的,本文是为了介绍vtkWidget。
-
图像数据转换 在Qt和VTK之间传递图像数据时,需要进行格式转换。在我们的示例中,我们演示了如何将Qt中的QImage对象转换为VTK中的vtkImageData对象。通过这种方式,我们可以实现在Qt应用程序和VTK渲染器之间高效地传递图像数据,从而实现更加丰富的数据可视化功能。
/// @brief 从QImage转换到vtkImageData /// @param src /// @return vtkSmartPointer<vtkImageData> QImage2vtkImageData(const QImage& src) { vtkSmartPointer<vtkImageData> dst = vtkSmartPointer<vtkImageData>::New(); int w = src.width(); int h = src.height(); // 设置维度 dst->SetDimensions(w, h, 1); // 设置数据类型和组件数 vtkNew<vtkInformation> info; dst->SetScalarType(VTK_UNSIGNED_CHAR, info); int channels = src.pixelFormat().channelCount(); dst->SetNumberOfScalarComponents(channels, info); // 分配内存 dst->AllocateScalars(info); // 获取图像数据的首地址 unsigned char* dstPtr = static_cast<unsigned char*>(dst->GetScalarPointer()); // 遍历图像像素并复制到vtkImageData中 for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { // 获取像素值 QRgb pixel = src.pixel(x, y); // QImage的行顺序是从上到下,vtkImageData的行顺序是从下到上 switch (channels) { case 1: dstPtr[(h - y - 1) * w + x] = qGray(pixel); break; case 3: dstPtr[((h - y - 1) * w + x) * 3 + 0] = qRed(pixel); dstPtr[((h - y - 1) * w + x) * 3 + 1] = qGreen(pixel); dstPtr[((h - y - 1) * w + x) * 3 + 2] = qBlue(pixel); break; case 4: dstPtr[((h - y - 1) * w + x) * 4 + 0] = qRed(pixel); dstPtr[((h - y - 1) * w + x) * 4 + 1] = qGreen(pixel); dstPtr[((h - y - 1) * w + x) * 4 + 2] = qBlue(pixel); dstPtr[((h - y - 1) * w + x) * 4 + 3] = qAlpha(pixel); break; } } } return dst; }
完整的代码:
#include <QApplication>
#include <QVTKOpenGLNativeWidget.h>
#include <QFile>
#include <QStandardPaths>
#include <QImage>
#include <QString>
#include <QColorSpace>
#include <QDebug>
#include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkSmartPointer.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkButtonWidget.h>
#include <vtkTexturedButtonRepresentation.h>
#include <vtkTexturedButtonRepresentation2D.h>
#include <vtkImageData.h>
#include <vtkPNGReader.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCommand.h>
#include <vtkNew.h>
#include <vtkInformation.h>
// 创建一个命令类来处理按钮点击事件
class ButtonCallback : public vtkCommand {
public:
static ButtonCallback* New()
{
auto result = new ButtonCallback();
result->InitializeObjectBase();
return result;
}
virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData) override
{
// 检查事件类型
if (eventId == vtkCommand::StateChangedEvent)
{
// 处理按钮状态变化事件
vtkButtonWidget* buttonWidget = reinterpret_cast<vtkButtonWidget*>(caller);
qDebug() << vtkCommand::GetStringFromEventId(eventId);
vtkButtonRepresentation* representation = reinterpret_cast<vtkButtonRepresentation*>(buttonWidget->GetRepresentation());
qDebug() << "vtkButtonWidget State: " << representation->GetState();
}
}
};
/// @brief 从QImage转换到vtkImageData
/// @param src
/// @return
vtkSmartPointer<vtkImageData> QImage2vtkImageData(const QImage& src)
{
vtkSmartPointer<vtkImageData> dst = vtkSmartPointer<vtkImageData>::New();
int w = src.width();
int h = src.height();
// 设置维度
dst->SetDimensions(w, h, 1);
// 设置数据类型和组件数
vtkNew<vtkInformation> info;
dst->SetScalarType(VTK_UNSIGNED_CHAR, info);
int channels = src.pixelFormat().channelCount();
dst->SetNumberOfScalarComponents(channels, info);
// 分配内存
dst->AllocateScalars(info);
// 获取图像数据的首地址
unsigned char* dstPtr = static_cast<unsigned char*>(dst->GetScalarPointer());
// 遍历图像像素并复制到vtkImageData中
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
// 获取像素值
QRgb pixel = src.pixel(x, y);
// QImage的行顺序是从上到下,vtkImageData的行顺序是从下到上
switch (channels)
{
case 1:
dstPtr[(h - y - 1) * w + x] = qGray(pixel);
break;
case 3:
dstPtr[((h - y - 1) * w + x) * 3 + 0] = qRed(pixel);
dstPtr[((h - y - 1) * w + x) * 3 + 1] = qGreen(pixel);
dstPtr[((h - y - 1) * w + x) * 3 + 2] = qBlue(pixel);
break;
case 4:
dstPtr[((h - y - 1) * w + x) * 4 + 0] = qRed(pixel);
dstPtr[((h - y - 1) * w + x) * 4 + 1] = qGreen(pixel);
dstPtr[((h - y - 1) * w + x) * 4 + 2] = qBlue(pixel);
dstPtr[((h - y - 1) * w + x) * 4 + 3] = qAlpha(pixel);
break;
}
}
}
return dst;
}
int main(int argc, char** argv)
{
QApplication a(argc, argv);
QVTKOpenGLNativeWidget w;
// 创建渲染器
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
auto reWin = w.renderWindow();
reWin->AddRenderer(renderer);
// 创建按钮的2D表示
vtkSmartPointer<vtkTexturedButtonRepresentation2D> btnRepresentation2D = vtkSmartPointer<vtkTexturedButtonRepresentation2D>::New();
btnRepresentation2D->SetPlaceFactor(1);
btnRepresentation2D->SetNumberOfStates(2);
vtkSmartPointer<vtkPNGReader> reader0 = vtkSmartPointer<vtkPNGReader>::New();
QString tempFilePath0 = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/poweroff.png";
QFile::copy(":/resources/poweroff.png", tempFilePath0);
reader0->SetFileName(tempFilePath0.toStdString().c_str());
reader0->Update();
btnRepresentation2D->SetButtonTexture(0, reader0->GetOutput());
QImage poweron = QImage(":/resources/poweron.png");
btnRepresentation2D->SetButtonTexture(1, QImage2vtkImageData(poweron));
// 创建按钮并设置表示
vtkSmartPointer<vtkButtonWidget> btnWidget = vtkSmartPointer<vtkButtonWidget>::New();
btnWidget->SetInteractor(w.interactor());
btnWidget->SetRepresentation(btnRepresentation2D);
//btnWidget->SetEnabled(1);
btnWidget->On();
// 创建按钮点击事件处理命令对象
vtkSmartPointer<ButtonCallback> callback = vtkSmartPointer<ButtonCallback>::New();
btnWidget->AddObserver(vtkCommand::StateChangedEvent, callback);
w.show();
int code = a.exec();
QFile::remove(tempFilePath0);
//QFile::remove(tempFilePath1);
return code;
}
通过本文的介绍,读者可以了解到如何结合Qt和VTK,开发出功能丰富、交互性强的图形用户界面。我们详细介绍了如何在Qt应用程序中嵌入VTK渲染器,并创建交互式的按钮。同时,我们还深入剖析了如何处理按钮点击事件,以及如何进行图像数据的格式转换。希望本文对读者能够有所帮助,让他们能够更加灵活地利用Qt和VTK进行图形用户界面开发和数据可视化。