如何在GTK Image组件中显示pbPlots库生成的图表?
解决GTK4中pbPlots图表显示乱码的问题
你遇到的乱码问题核心是错误地将PNG压缩字节流直接作为原始像素数据传给GdkPixbuf,同时存在内存管理时机错误。以下是修复方案:
问题分析
ConvertToPNG生成的是PNG格式的压缩二进制数据,而gdk_pixbuf_new_from_data需要的是未压缩的原始像素数据,直接传入会导致解析混乱,出现乱码。FreeAllocations()在窗口显示前就调用,会提前释放pbPlots分配的图像内存,导致GdkPixbuf访问无效内存。- pbPlots生成的是RGBA格式(带Alpha通道)的图像,你创建pixbuf时错误指定为RGB格式,通道数不匹配。
修复步骤
- 直接使用pbPlots生成的
RGBABitmapImage原始数据创建GdkPixbuf,跳过PNG转换步骤。 - 为GdkPixbuf设置内存销毁回调,确保像素数据在pixbuf被销毁时自动释放,避免内存泄漏。
- 调整
FreeAllocations()的调用时机,通过回调管理pbPlots的内存。 - 修正GdkPixbuf的参数,适配RGBA格式:
- 启用Alpha通道(第三个参数设为
TRUE) - 行跨度设为
width * 4(RGBA每个像素占4字节)
- 启用Alpha通道(第三个参数设为
修复后的完整代码
#include "pbPlots/pbPlots.hpp" #include "pbPlots/supportLib.hpp" #include <gtk/gtk.h> #include <algorithm> // 回调函数:当pixbuf销毁时释放pbPlots分配的图像内存 static void free_pixel_data(guchar *data, gpointer user_data) { RGBABitmapImage *image = static_cast<RGBABitmapImage*>(user_data); DeleteImage(image); FreeAllocations(); // 统一释放pbPlots的所有分配 } static void activate(GtkApplication* app, gpointer user_data) { GtkWidget *window = gtk_application_window_new(app); GtkWidget *grid = gtk_grid_new(); bool success; StringReference *errorMessage = CreateStringReferenceLengthValue(0, L' '); RGBABitmapImageReference *imageReference = CreateRGBABitmapImageReference(); std::vector<double> xs{-2, -1, 0, 1, 2}; std::vector<double> ys{2, -1, -2, -1, 2}; ScatterPlotSeries *series = GetDefaultScatterPlotSeriesSettings(); series->xs = &xs; series->ys = &ys; series->linearInterpolation = true; series->lineType = toVector(L"dashed"); series->lineThickness = 2; series->color = GetGray(0.3); ScatterPlotSettings *settings = GetDefaultScatterPlotSettings(); settings->width = 600; settings->height = 400; settings->autoBoundaries = true; settings->autoPadding = true; settings->title = toVector(L"x^2 - 2"); settings->xLabel = toVector(L"X axis"); settings->yLabel = toVector(L"Y axis"); settings->scatterPlotSeries->push_back(series); success = DrawScatterPlotFromSettings(imageReference, settings, errorMessage); if (!success) { std::cerr << "Error: "; for(wchar_t c : *errorMessage->string){ std::wcerr << c; } std::cerr << std::endl; FreeAllocations(); return; } RGBABitmapImage *image = imageReference->image; // 将pbPlots的0-1范围double像素值转换为0-255的unsigned char std::vector<unsigned char> pixel_data; pixel_data.reserve(image->width * image->height * 4); for (double channel : image->values) { pixel_data.push_back(static_cast<unsigned char>(std::clamp(channel * 255.0, 0.0, 255.0))); } // 创建带Alpha通道的Pixbuf,指定销毁回调 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( pixel_data.data(), GDK_COLORSPACE_RGB, TRUE, // 启用Alpha通道 8, // 每个通道8位 image->width, image->height, image->width * 4, // RGBA行跨度:宽度*4字节 free_pixel_data, image // 传递给回调的用户数据(要释放的图像) ); GtkWidget* gtk_image = gtk_image_new_from_pixbuf(pixbuf); gtk_widget_set_size_request(gtk_image, image->width, image->height); gtk_widget_set_hexpand(gtk_image, TRUE); gtk_grid_attach(GTK_GRID(grid), gtk_image, 0, 0, 1, 1); gtk_window_set_child(GTK_WINDOW(window), grid); gtk_window_set_title(GTK_WINDOW(window), "GTK4 pbPlots Chart"); gtk_window_set_default_size(GTK_WINDOW(window), image->width + 10, image->height + 10); gtk_widget_show(window); // 释放pixbuf的引用(窗口会持有它,销毁时触发回调) g_object_unref(pixbuf); } int main(int argc, char **argv) { GtkApplication *app = gtk_application_new("org.gtk.chart", G_APPLICATION_DEFAULT_FLAGS); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); int status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); return status; }
编译命令
保持原命令不变:
g++ $( pkg-config --cflags gtk4 ) -o chart chart.cpp pbPlots/pbPlots.cpp pbPlots/supportLib.cpp $( pkg-config --libs gtk4 )
关键说明
- 直接使用pbPlots的原始RGBA数据,避免了PNG压缩/解压缩的额外开销,同时解决了格式不匹配的问题。
- 通过
gdk_pixbuf_new_from_data的销毁回调,确保pbPlots分配的内存能在正确时机释放,避免内存泄漏和悬空指针。 - 手动将pbPlots的double类型像素值(0.0-1.0范围)转换为unsigned char(0-255),匹配GdkPixbuf的像素格式要求。
内容的提问来源于stack exchange,提问作者Radosław Ziomber
相关产品推荐
相关产品推荐

