GTK3: изменить размер изображения, сохранив соотношение сторон - PullRequest
0 голосов
/ 14 мая 2018

Когда я изменяю размер GtkWindow, я хочу, чтобы размер GtkImage также менялся, сохраняя то же соотношение сторон, что и раньше. Я не могу найти хороших примеров того, как настроить это с GTK3

Это то, что я пробовал до сих пор:

#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

GtkAllocation *allocation = g_new0 (GtkAllocation, 1); 

gboolean resize_image(GtkWidget *widget, GdkEvent *event, GtkWidget *window) {
    GdkPixbuf *pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(widget));
    if (pixbuf == NULL) {
        g_printerr("Failed to resize image\n");
        return 1;
    }
    gtk_widget_get_allocation(GTK_WIDGET(widget), allocation);
    pixbuf = gdk_pixbuf_scale_simple(pixbuf, widget->allocation.width, widget->allocation.height, GDK_INTERP_BILINEAR);
    gtk_image_set_from_pixbuf(GTK_IMAGE(widget), pixbuf);
    return FALSE;
}

int main(int argc, char **argv) {
    GtkWidget *window = NULL;
    GtkWidget *image = NULL;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    image = gtk_image_new_from_file("image.jpg");
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect(image, "draw", G_CALLBACK(resize_image), (gpointer)window);
    gtk_container_add(GTK_CONTAINER(window), image);
    gtk_widget_show_all(GTK_WIDGET(window));
    gtk_main();
    return 0;
}

Этот код должен просто изменить размер Pixbuf до размера родительского, но он не работает, я получаю следующие ошибки:

GdkPixbuf-CRITICAL **: gdk_pixbuf_get_width: утверждение 'GDK_IS_PIXBUF (pixbuf) 'не удалось

GLib-GObject-CRITICAL **: g_object_unref: утверждение 'G_IS_OBJECT (объект) 'не удалось

Даже если этот код будет работать, я не смогу сохранить такое же соотношение сторон, как этого добиться?

Заранее спасибо

1 Ответ

0 голосов
/ 16 мая 2018

Хорошо, хитрость для этого заключается в использовании GtkLayout между изображением и окном.Кроме того, вместо использования сигнала draw используйте сигнал size-allocate.

Мы загрузим GdkPixbuf и используем его в качестве эталона для масштабирования, в противном случае качество ухудшится при накопительном «изменении размера».

Простым подходом будет:

#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

struct _resize_widgets {
   GtkWidget *image;
   GdkPixbuf *pixbuf;
};

typedef struct _resize_widgets ResizeWidgets;

gboolean resize_image(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data) {
   int x,y,w,h;
   GdkPixbuf *pxbscaled;
   GtkWidget *image = (GtkWidget *) ((ResizeWidgets *) user_data)->image; 
   GdkPixbuf *pixbuf= (GdkPixbuf *) ((ResizeWidgets *) user_data)->pixbuf; 

   x = 0;
   y = 0;

   h = allocation->height;
   w = (gdk_pixbuf_get_width(pixbuf) * h) / gdk_pixbuf_get_height(pixbuf);

   pxbscaled = gdk_pixbuf_scale_simple(pixbuf, w, h, GDK_INTERP_BILINEAR);

   if (w < allocation->width) {
      x = (allocation->width - w) / 2;
      gtk_layout_move(GTK_LAYOUT(widget), image, x, y);
   }

   gtk_image_set_from_pixbuf(GTK_IMAGE(image), pxbscaled);

   g_object_unref (pxbscaled);

   return FALSE;
}

int main(int argc, char **argv) {
   GtkWidget *window = NULL;
   GtkWidget *image = NULL;
   GtkWidget *container = NULL;
   GdkPixbuf *pixbuf = NULL;
   ResizeWidgets *widgets;

   gtk_init(&argc, &argv);

   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   container = gtk_layout_new(NULL, NULL);
   image = gtk_image_new();

   pixbuf = gdk_pixbuf_new_from_file ("image.png", NULL);
   if (pixbuf == NULL) {
      g_printerr("Failed to resize image\n");
      return 1;
   }

   widgets = g_new0(ResizeWidgets, 1);
   widgets->image = image;
   widgets->pixbuf = pixbuf;

   gtk_container_add(GTK_CONTAINER(window), container);
   gtk_layout_put(GTK_LAYOUT(container), image, 0, 0);

   gtk_widget_set_size_request (GTK_WIDGET(window), 20, 20);

   g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
   g_signal_connect(container, "size-allocate", G_CALLBACK(resize_image), widgets);

   gtk_widget_show_all(GTK_WIDGET(window));

   gtk_main();

   g_object_unref (pixbuf);
   g_free(widgets);

   return 0;
}

Компилировать с gcc -o main main.c pkg-config --cflags --libs gtk + -3.0`

Это сохранит соотношение сторон.Если это нежелательно, то обработчик обратного вызова resize_image будет выглядеть следующим образом:

gboolean resize_image(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data) {
   int x,y,w,h;
   GdkPixbuf *pxbscaled;
   GtkWidget *image = (GtkWidget *) ((ResizeWidgets *) user_data)->image; 
   GdkPixbuf *pixbuf= (GdkPixbuf *) ((ResizeWidgets *) user_data)->pixbuf; 

   pxbscaled = gdk_pixbuf_scale_simple(pixbuf, allocation->width, allocation->height, GDK_INTERP_BILINEAR);

   gtk_image_set_from_pixbuf(GTK_IMAGE(image), pxbscaled);

   g_object_unref (pxbscaled);

   return FALSE;
}

Это простой хак, очевидно, есть другие варианты, такие как cairo для рисования или просто проверка Eye of Gnome(eog) реализация, которая является намного более полной.

...