You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在GTK Image组件中显示pbPlots库生成的图表?

解决GTK4中pbPlots图表显示乱码的问题

你遇到的乱码问题核心是错误地将PNG压缩字节流直接作为原始像素数据传给GdkPixbuf,同时存在内存管理时机错误。以下是修复方案:

问题分析

  1. ConvertToPNG生成的是PNG格式的压缩二进制数据,而gdk_pixbuf_new_from_data需要的是未压缩的原始像素数据,直接传入会导致解析混乱,出现乱码。
  2. FreeAllocations()在窗口显示前就调用,会提前释放pbPlots分配的图像内存,导致GdkPixbuf访问无效内存。
  3. pbPlots生成的是RGBA格式(带Alpha通道)的图像,你创建pixbuf时错误指定为RGB格式,通道数不匹配。

修复步骤

  1. 直接使用pbPlots生成的RGBABitmapImage原始数据创建GdkPixbuf,跳过PNG转换步骤。
  2. 为GdkPixbuf设置内存销毁回调,确保像素数据在pixbuf被销毁时自动释放,避免内存泄漏。
  3. 调整FreeAllocations()的调用时机,通过回调管理pbPlots的内存。
  4. 修正GdkPixbuf的参数,适配RGBA格式:
    • 启用Alpha通道(第三个参数设为TRUE)
    • 行跨度设为width * 4(RGBA每个像素占4字节)

修复后的完整代码

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 00:55:16