QVTKOpenGLNativeWidget中绘制vtkButtonWidget

本文探讨了如何结合Qt和VTK(Visualization Toolkit)开发图形用户界面(GUI),通过一个具体的案例,详细介绍了如何在Qt应用程序中嵌入VTK渲染器,并创建交互式的图形元素,如按钮,以实现更丰富的用户体验。文章还深入剖析了如何处理按钮点击事件,以及如何将Qt的图像数据转换为VTK格式。

介绍: 在现代软件开发中,图形用户界面(GUI)的设计和开发是至关重要的一部分。Qt作为一种流行的跨平台应用程序框架,提供了丰富的GUI开发工具和功能。而VTK作为一个强大的可视化工具包,用于创建复杂的3D图形和数据可视化。本文将介绍如何结合Qt和VTK,利用它们各自的优势,开发出功能丰富、交互性强的图形用户界面。

  1. 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();
    ...
    }
  2. 创建交互式按钮 在我们的示例中,我们使用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);
  3. 处理按钮点击事件为了处理按钮的点击事件,我们创建了一个自定义的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。

  4. 图像数据转换 在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进行图形用户界面开发和数据可视化。

posted @   非法关键字  阅读(881)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示