3

I want to display chart inside my GTK4 application. I'm mixing C GTK with C++ library pbPlots and rest of the code. Unfortunately I get some garbage image.

Some bad results

Vast majority of chart generation example codes is not compatible with GTK4. Writing image to file and reading it to GTK Image widget is quite silly.

g++ $( pkg-config --cflags gtk4 ) -o chart chart.cpp pbPlots/pbPlots.cpp pbPlots/supportLib.cpp $( pkg-config --libs gtk4 )
#include "pbPlots/pbPlots.hpp"
#include "pbPlots/supportLib.hpp"

#include <gtk/gtk.h>

static void activate (GtkApplication* app, gpointer        user_data)
{
  GtkWidget *window;

  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);
  std::vector<double> *pngdata;
  if(success){
    pngdata = ConvertToPNG(imageReference->image);
    //WriteToFile(pngdata, "example2.png");
    //DeleteImage(imageReference->image);
  }else{
    std::cerr << "Error: ";
    for(wchar_t c : *errorMessage->string){
      std::wcerr << c;
    }
    std::cerr << std::endl;
  }

  unsigned char* buffer = DoubleArrayToByteArray(pngdata);
  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(buffer, GDK_COLORSPACE_RGB, FALSE, 8, settings->width, settings->height, settings->width * 3, NULL, NULL);

  // GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file("test.png", nullptr);
  GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
  gtk_widget_set_size_request (image, settings->width, settings->height);
  gtk_widget_set_hexpand(image, TRUE);

  gtk_grid_attach(GTK_GRID(grid), image, 0, 0, 1, 1);
  gtk_window_set_child (GTK_WINDOW (window), grid);
  gtk_window_set_title (GTK_WINDOW (window), "Window");
  gtk_window_set_default_size (GTK_WINDOW (window), settings->width+10, settings->height+10);
  gtk_widget_show (window);

  FreeAllocations();
}

int main (int argc, char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}
kiner_shah
  • 3,939
  • 7
  • 23
  • 37
  • 1
    Maybe its the conversion of double array to byte array. What is there in your buffer is somewhat different that what GTK expects. I recommend debugging that area. Load image from a file and see how different is the data (format, value range, etc.). – kiner_shah Mar 13 '23 at 08:02

1 Answers1

1

This is working correctly.

  std::vector<double> *pngdata;
  pngdata = ConvertToPNG(imageReference->image);
  unsigned char* buffer = DoubleArrayToByteArray(pngdata);
  GInputStream* stream = g_memory_input_stream_new_from_data(buffer, pngdata->size(), NULL);
  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
  GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);

enter image description here

#include "pbPlots/pbPlots.hpp"
#include "pbPlots/supportLib.hpp"
#include <gtk/gtk.h>

static void activate (GtkApplication* app,
                      gpointer        user_data)
{
  GtkWidget *window;

  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);
  std::vector<double> *pngdata;
  if(success){
    pngdata = ConvertToPNG(imageReference->image);
    //WriteToFile(pngdata, "example2.png");
    //DeleteImage(imageReference->image);
  }else{
    std::cerr << "Error: ";
    for(wchar_t c : *errorMessage->string){
      std::wcerr << c;
    }
    std::cerr << std::endl;
  }

  unsigned char* buffer = DoubleArrayToByteArray(pngdata);
  GInputStream* stream = g_memory_input_stream_new_from_data(buffer, pngdata->size(), NULL);
  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
  GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
  gtk_widget_set_size_request (image, settings->width, settings->height);
  gtk_widget_set_hexpand(image, TRUE);

  gtk_grid_attach(GTK_GRID(grid), image, 0, 0, 1, 1);
  gtk_window_set_child (GTK_WINDOW (window), grid);
  gtk_window_set_title (GTK_WINDOW (window), "Window");
  gtk_window_set_default_size (GTK_WINDOW (window), settings->width+10, settings->height+10);
  gtk_widget_show (window);

  FreeAllocations();
}

int main (int argc, char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}

But:

  1. Potentially is not optimal.
  2. There is still need to take care of memory management. I hope g_autoptr() is adequate for that use case (unsigned char* buffer, GInputStream* stream, GdkPixbuf *pixbuf). I need to plot live chart from serial port, so picture will be updated during work.

Serial monitor in GTK C++