首页 > 其他分享 >QVTKOpenGLNativeWidget中绘制vtkButtonWidget

QVTKOpenGLNativeWidget中绘制vtkButtonWidget

时间:2024-03-01 16:25:36浏览次数:27  
标签:QVTKOpenGLNativeWidget VTK dstPtr vtkSmartPointer 按钮 include 绘制 pixel vtkButtonW

本文探讨了如何结合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进行图形用户界面开发和数据可视化。

标签:QVTKOpenGLNativeWidget,VTK,dstPtr,vtkSmartPointer,按钮,include,绘制,pixel,vtkButtonW
From: https://www.cnblogs.com/linxmouse/p/18047328

相关文章

  • C# GDI+绘制网络获取指定QQ圆形头像框
    某论坛的评论区模块,发现这功能很不错,琢磨了一晚上做了大致一样的,用来当做注册模块的头像绑定功能,下面通过实例代码给大家介绍下C#获取指定QQ头像绘制圆形头像框GDI(Graphics)的方法,感兴趣的朋友一起看看吧。效果图:完全代码(下方有详细解读)1234567891011......
  • turtle绘制太极图
    代码演示importturtle#导入turtle库t=turtle.Pen()#turtle.circle(radius,extent,step)#radius是必需的,表示半径,正值时逆时针旋转;#extent表示度数,用于绘制圆弧,正值时向上,负值时向下;#step表示边数,可用于绘制正多边形;#extent和step参数可有可无。#......
  • 基于Python GDAL为长时间序列遥感图像绘制时相变化曲线图
      本文介绍基于Python中gdal模块,对大量多时相栅格图像,批量绘制像元时间序列折线图的方法。  首先,明确一下本文需要实现的需求:现有三个文件夹,其中第一个文件夹存放了某一研究区域原始的多时相栅格遥感影像数据(每一景遥感影像对应一个时相,文件夹中有多景遥感影像),每一景遥感影像......
  • 【Filament】绘制矩形
    1前言​Filament环境搭建中介绍了Filament的Windows和Android环境搭建,绘制三角形中介绍了绘制纯色和彩色三角形,本文将使用Filament绘制纯色和彩色矩形。2绘制矩形​本文项目结构如下,完整代码资源→Filament绘制矩形。2.1自定义基类​为方便读者将......
  • 【Filament】绘制圆形
    1前言​Filament环境搭建中介绍了Filament的Windows和Android环境搭,绘制三角形中介绍了绘制纯色和彩色三角形,绘制矩形中介绍了绘制纯色和彩色矩形,本文将使用Filament绘制圆形。2绘制圆形​本文项目结构如下,完整代码资源→Filament绘制圆形。2.1自定义基......
  • 【Filament】绘制三角形
    1前言​Filament环境搭建中介绍了Filament的Windows和Android环境搭建,本文将使用Filament绘制纯色和彩色三角形。1.1Filament类图1.2图元​Filament中图形的绘制都是基于三角形实现,三角形是构成复杂图形的最小基本单元。Filament中输入模型信息的代码......
  • C#:winform使用chart控件绘制折线图,时间轴可缩放
    C#:winform使用chart控件绘制折线图,时间轴可缩放Chart坐标轴横轴为时间,纵轴是数值如果只是一次性绘图,那么遍历一遍数据即可如果想连续绘制(比如按照时间更新绘制),就需要一个Timer控件来更新绘图的数据。以下为项目代码:GUI界面添加一个Chart和一个timer即可usingSystem;using......
  • 基于Python的地图绘制教程
    本文将介绍通过Python绘制地形图的方法,所需第三方Python相关模块包括rasterio、geopandas、cartopy等,可通过pip等方式安装。1示例代码1.1导入相关模块importrasterioimportgeopandasasgpdimportnumpyasnpimportcartopy.crsasccrsimportmatplotlib.pyplot......
  • 【libGDX】使用Mesh绘制圆形
    1前言​使用Mesh绘制三角形中介绍了绘制三角形的方法,使用Mesh绘制矩形中介绍了绘制矩形的方法,本文将介绍绘制圆形的方法。​libGDX以点、线段、三角形为图元,没有提供绘制圆形的接口。要绘制圆形边框,必须通过割圆法逼近圆形;要绘制圆形的内部,必须通过三角形逼近圆形,如......
  • 【libGDX】使用Mesh绘制立方体
    1前言​本文主要介绍使用Mesh绘制立方体,读者如果对Mesh不太熟悉,请回顾以下内容:使用Mesh绘制三角形使用Mesh绘制矩形使用Mesh绘制圆形​在绘制立方体的过程中,主要用到了MVP(ModelViewProjection)矩阵变换。Model:模型变换,施加在模型上的空间变换,包含平移变......