Gtk3: GtkImage prevents user from shrinking the window - gtk

Gtk3 in C: I am trying to create a window that loads a picture from a png/jpeg file. I want the user to be able to resize the window and automatically scale the picture to fit the window.
So here is what I do:
app = gtk_application_new("foo", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
status = g_application_run(G_APPLICATION(app), argc, argv);
then the activate method does:
GtkWidget *window;
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "foo");
gtk_window_set_default_size(GTK_WINDOW(window), 600, 600);
GError *err = NULL;
// Note: original and image are global variables, because I can't
// get user_data for callbacks working. Will sort this out later.
original = gdk_pixbuf_new_from_file("myfile.png", &err);
if (err != NULL) {
fatalf(scope, "Unable to read file: %s\n", err->message);
exit(1);
}
image = gtk_image_new();
gtk_container_add(GTK_CONTAINER(window), image);
gtk_widget_show_all(window);
g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(resize_picture), NULL);
and the resize_picture:
gint w, h;
gtk_window_get_size(window, &w, &h);
GdkPixbuf *resized = gdk_pixbuf_scale_simple(original, w, h, GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf(image, resized);
Now the problem is that the GtkImage appears to impose the minimum size on the window. I can enlarge the window, which scales up the picture, but I cannot shrink it. Is there some property I can set on GtkWindow or GtkImage to allow me to do that? Or do I need to use something else than GtkImage?

The solution is to switch to GTK4 and use GtkPicture instead of GtkImage.
GtkPicture has can_shrink property, which solves this problem.

Related

GTK+3 in C lang: how to set MAXIMUM length of ProgressBar?

I'm totally in trouble. Wanna set maximum width of progressbar, but the only thing I found is min- properties, that can be set through CSS. What else can I do?
As you probably have found out through research, the CSS "max-width" property is not a property currently handled via the GTK CSS provider. In testing out various scenarios with a progress bar, it seems that the constricting factor is the width allowances of the various GTK containers. And, the only container that appeared to allow for adjusting the width of a progress bar was within a "GtkBox" container. Following is a minimal program I composed to test out various widths for a progress bar (FYI, this is GTK3).
#include <gtk/gtk.h>
int main (int argc, char *argv[])
{
GtkWidget *prog = NULL;
GtkWidget *win = NULL;
GtkWidget *box = NULL;
gtk_init (&argc, &argv);
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
prog = gtk_progress_bar_new();
gtk_window_set_title (GTK_WINDOW (win), "Progress Max");
gtk_container_set_border_width (GTK_CONTAINER (win), 20);
gtk_window_set_default_size(GTK_WINDOW(win), 400, 120);
g_signal_connect (win, "destroy", gtk_main_quit, NULL);
gtk_container_add (GTK_CONTAINER (win), box);
gtk_widget_set_size_request(prog, 200, 20); /* Vary the length to test the effect */
gtk_box_pack_start(GTK_BOX(box), prog, FALSE, TRUE, 40);
gtk_widget_show_all (win);
gtk_main ();
return 0;
}
Make note of the size values in the "gtk_widget_set_size_request" function to determine your progress bar width, and note the boolean values for the "fill" and "padding" parameters in the "gtk_box_pack_start" function. I believe that if you experiment with those statements that you should be able to control the desired width of your progress bar.
Regards.

Why doesn't my GtkBox update until I resize the whole window?

When I attach a menu to a window I detach the existing root control, add a vertical GtkBox to hold the menu and the root control and then attach that box to the GtkWindow, like so:
Gtk::GtkWidget *menubar = GTK_WIDGET(Info.obj);
Wnd->_VBox = Gtk::gtk_box_new(Gtk::GTK_ORIENTATION_VERTICAL, 0);
Gtk::GtkBox *vbox = GTK_BOX(Wnd->_VBox);
Gtk::GtkContainer *wndcontainer = GTK_CONTAINER(Wnd->Wnd);
g_object_ref(Wnd->_Root);
gtk_container_remove(wndcontainer, Wnd->_Root);
gtk_box_pack_start(vbox, menubar, false, false, 0);
gtk_box_pack_end(vbox, Wnd->_Root, true, true, 0);
gtk_container_add(wndcontainer, Wnd->_VBox);
gtk_widget_show_all(GTK_WIDGET(Wnd->Wnd));
g_object_unref(Wnd->_Root);
gtk_window_add_accel_group(Wnd->Wnd, AccelGrp);
In practice it looks like this:
What I'd like is the menu to appear in the correct place automatically without having to resize the window to force a layout update.
I've tried calling gtk_widget_queue_draw on the window but that made no difference. Am I doing something wrong here? Can a call an extra function to invalidate the layout and get it to refresh?
It's difficult to answer without having a minimal reproducible example. Your code in not even plain GTK or gtkmm... it seems to be some exotic variant between the two.
Here is my attempt: I tried to be as close as possible to your code. The issue you are describing is not present though.
/* gcc -o test test.c $(pkg-config --cflags --libs gtk+-3.0) */
#include <gtk/gtk.h>
static GMenu *
menu_model(void)
{
GMenu *menu = g_menu_new();
g_menu_append(menu, "File", NULL);
g_menu_append(menu, "Edit", NULL);
g_menu_append(menu, "Project", NULL);
/* ... */
return menu;
}
int main(int argc, char **argv)
{
GtkWidget *window;
GMenuModel *model;
GtkWidget *menubar;
GtkWidget *content;
GtkWidget *vbox;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
model = G_MENU_MODEL(menu_model());
menubar = gtk_menu_bar_new_from_model(model);
g_object_unref(model);
content = gtk_label_new("Some content here");
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
gtk_box_pack_end(GTK_BOX(vbox), content, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show_all(GTK_WIDGET(window));
gtk_main();
return 0;
}
So while it should "just work" out of the box. And indeed on my Raspberry Pi the code does just do what it's supposed to do, this is still an issue on my Ubuntu 18 VM. I have found somewhat of a work around to kick the GtkBox into reconfiguring the child widget's layout:
GdkRectangle allocation = Wnd->GetClient();
g_signal_emit_by_name(G_OBJECT(vbox), "size-allocate", GTK_WIDGET(vbox), &allocation, NULL, NULL);
The menu now appears in the right location automatically. It seems like a "hack" that may stop working in the future or crash on some systems? IDK. But in terms of right now and Ubuntu 18, I don't have anything better.

Gtk+ not updating the widgets correctly

I hope I can describe this problem well. I ran pacman Syu a couple days ago and the GTK application I'm working on stopped working correctly. I have ran it again after but the problem persists.
Basically, the program is not responding correctly. As an example, I have a button that, when pressed, should print "aqui" to the console and then move the canvas up (as in, navigate up). However the canvas is not updated, and the output is not printed. Only when I close the application, "aqui" is printed as many times as I pressed the button.
The application seems to respond when another window is opened. If I click on a button that opens a different window, the accumulated changes take effect (multiple "aqui" printed, and the canvas moves up as many times as I clicked). When I click on the button that should close the new window, the button disappears, but the window is still there. Example:
This is the code for the up button:
static gboolean moveUp(GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
windowData->moveY(STEP);
gtk_widget_queue_draw((GtkWidget*) user_data);
std::cout << "aqui";
return TRUE;
}
the code for the rotate button, that opens the smaller window seen in the pictures:
static gboolean rotateWindowWindow(GtkWidget *widget, GdkEventButton *event,
gpointer user_data) {
GtkBuilder *builder;
GError *error = NULL;
builder = gtk_builder_new();
if (!gtk_builder_add_from_file(builder, "rotateWindow.glade", &error)) {
g_warning("%s", error->message);
g_free(error);
}
GtkWidget *rotateWindowWindow;
rotateWindowWindow = GTK_WIDGET( gtk_builder_get_object( builder, "rotateWindowWindow" ) );
rotationAngle = (GtkEntry*) GTK_WIDGET( gtk_builder_get_object( builder, "rotationAngle" ) );
GtkWidget* okButton = GTK_WIDGET(gtk_builder_get_object(builder, "okButton"));
g_signal_connect(G_OBJECT(okButton), "clicked", G_CALLBACK(rotateW), rotateWindowWindow);
gtk_builder_connect_signals(builder, NULL);
g_object_unref(G_OBJECT(builder));
gtk_widget_show_all(rotateWindowWindow);
gtk_main();
return TRUE;
}
the rotateW method that is called when clicking the okButton:
static gboolean rotateW(GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
double angle = atof(gtk_entry_get_text(GTK_ENTRY(rotationAngle)));
windowData->rotate(angle);
displayFile->rotateAll(windowData->getAngle(), windowData->getCenter());
gtk_widget_destroy((GtkWidget*) user_data);
return TRUE;
}
and the main method:
int main(int argc, char **argv)
{
GtkWidget *viewport, *buttonUp,
*buttonDown, *buttonLeft, *buttonRight, *buttonZoomIn,
*buttonZoomOut, *newLine, *listWindow, *mainBox, *buttonClose,
*newPolygon, *newPoint, *translateButton, *scaleButton,
*rotateButton, *rotateWindowButton;
GtkDrawingArea *drawingArea;
GError *error = NULL;
origin.x = 0;
origin.y = 0;
viewportData = new Viewport(300.0, 350.0);
windowData = new Window(300.0, 350.0);
sh = new SutherlandHodgeman(windowData);
cs = new CohenSutherland(windowData);
nc = new NoClipping(windowData);
clipper = nc;
displayFile = new DisplayFile();
Polygon* l = new Polygon("line");
l->addPoint(0, 0);
l->addPoint(100, 0);
displayFile->add(l);
//l = new Polygon("line2");
//l->addPoint(5, 5);
//l->addPoint(500, 15);
//displayFile->add(l);
/* Init GTK+ */
gtk_init( &argc, &argv );
/* Create new GtkBuilder object */
mainBuilder = gtk_builder_new();
/* Load UI from file. If error occurs, report it and quit application.
* Replace "tut.glade" with your saved project. */
if( ! gtk_builder_add_from_file( mainBuilder, "interface.glade", &error ) )
{
g_warning( "%s", error->message );
g_free( error );
return 1;
}
/* Get main window pointer from UI */
mainWindow = GTK_WIDGET( gtk_builder_get_object( mainBuilder, "mainWindow" ) );
viewport = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "viewport"));
drawingArea = GTK_DRAWING_AREA(gtk_builder_get_object(mainBuilder, "drawingArea"));
listWindow = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "listWindow"));
buttonClose = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "buttonClose"));
mainBox = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "mainBox"));
buttonUp = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "buttonUp"));
buttonLeft = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "buttonLeft"));
buttonRight = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "buttonRight"));
buttonDown = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "buttonDown"));
buttonZoomIn = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "zoomIn"));
buttonZoomOut = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "zoomOut"));
translateButton = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "translateButton"));
scaleButton = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "scaleButton"));
rotateButton = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "rotateButton"));
newPolygon = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "newPolygon"));
newLine = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "newLine"));
newPoint = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "newPoint"));
cohenButton = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "cohenButton"));
sutherlandButton = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "hodgemanButton"));
noClippingButton = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "noClipping"));
rotateWindowButton = GTK_WIDGET(gtk_builder_get_object(mainBuilder, "rotateWindow"));
g_signal_connect(mainWindow, "delete_event", G_CALLBACK(exit_app), NULL);
g_signal_connect(buttonClose, "clicked", G_CALLBACK(exit_app), NULL);
g_signal_connect(G_OBJECT(drawingArea), "draw", G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(buttonUp), "clicked", G_CALLBACK(moveUp), mainWindow);
g_signal_connect(G_OBJECT(buttonDown), "clicked", G_CALLBACK(moveDown), mainWindow);
g_signal_connect(G_OBJECT(buttonLeft), "clicked", G_CALLBACK(moveLeft), mainWindow);
g_signal_connect(G_OBJECT(buttonRight), "clicked", G_CALLBACK(moveRight), mainWindow);
g_signal_connect(G_OBJECT(buttonZoomIn), "clicked", G_CALLBACK(zoomIn), mainWindow);
g_signal_connect(G_OBJECT(buttonZoomOut), "clicked", G_CALLBACK(zoomOut), mainWindow);
g_signal_connect(G_OBJECT(translateButton), "clicked", G_CALLBACK(translateWindow), mainWindow);
g_signal_connect(G_OBJECT(rotateButton), "clicked", G_CALLBACK(rotateWindow), mainWindow);
g_signal_connect(G_OBJECT(scaleButton), "clicked", G_CALLBACK(scaleWindow), mainWindow);
g_signal_connect(G_OBJECT(newLine), "clicked", G_CALLBACK(newLineWindow), NULL);
g_signal_connect(G_OBJECT(newPolygon), "clicked", G_CALLBACK(newPolygonWindow), NULL);
g_signal_connect(G_OBJECT(newPoint), "clicked", G_CALLBACK(newPointWindow), NULL);
g_signal_connect(G_OBJECT(cohenButton), "clicked", G_CALLBACK(changeClipping), mainWindow);
g_signal_connect(G_OBJECT(sutherlandButton), "clicked", G_CALLBACK(changeClipping), mainWindow);
g_signal_connect(G_OBJECT(noClippingButton), "clicked", G_CALLBACK(changeClipping), mainWindow);
g_signal_connect(G_OBJECT(rotateWindowButton), "clicked", G_CALLBACK(rotateWindowWindow), mainWindow);
/* Connect signals */
gtk_builder_connect_signals( mainBuilder, NULL );
/* Destroy builder, since we don't need it anymore */
//g_object_unref( G_OBJECT( mainBuilder ) );
/* Show window. All other widgets are automatically shown by GtkBuilder */
gtk_widget_show_all( mainWindow );
/* Start main loop */
gtk_main();
return 0;
}
If this is not enough, the full code is on github. I apologize in advance, the code is a mess.
EDIT
Adding std::endl to the end of the std::cout solved the problem of the strings not being printed in real time. Now they are being printed when I press the button, but the rest of the button's funcitonality, that has to do with affecting the interface, still doesn't update until another window opens.
EDIT 2
I made a gif showing how it's behaving right now.
I tried downgrading gtk from 3.20.6 to 3.16.1, which didn't work. I also tried downgrading every package (by editing /etc/pacman.conf and then running pacman -Syyuu) to 03/30/2016, also didn't work.
I also tried removing other instances of gtk main() with no success.
Just tried adding this code:
if(gtk_events_pending())
gtk_main_iteration();
Which force runs the main loop a single time. That also didn't solve the problem.
You may want to force GTK to refresh its pending operations.
I do it in python this way :
while gtk.events_pending(): # this forces GTK to refresh the screen
gtk.main_iteration()
As you do not mention what language you are using, you'll have to find out how to reproduce this, but it's probably just
while gtk.events_pending(): # this forces GTK to refresh the screen
gtk.main_iteration();
Your problem is taht you're callin gtk_main twice. In your main (which is ok), and in your rotateWindowWindow function (whihch is wrong). Doing so will create a new main loop, in which you'll be stuck until you exit.
Just removing that extra call should be enough to fix your issue.

GTK+ and GdkPixbuf

I think I've got an understanding problem of GTK. My simple application has a stream of images and I'd like to display them within my GTK Window. Up to now, it looks like this:
GdkPixbuf *pb = gdk_pixbuf_new_from_data(img2, GDK_COLORSPACE_RGB,
FALSE, 24/3, 320, 240, 320*3,
NULL, NULL);
if(pb == NULL)
fprintf(stderr, "Pixbuf is null!\n");
if(image != NULL)
gtk_container_remove(GTK_CONTAINER(window), image);
image = gtk_image_new_from_pixbuf(pb);
gtk_container_add(GTK_CONTAINER(window), image);
printf("Updated!\n");
img2 is my (rgb) buffer that gets updated from a stream each time. I guess gtk_container_remove and gtk_container_add might be stupid to use for this?
Here's what I've got in addition:
GtkWidget *window;
GtkWidget *image;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window), "destroy",
GTK_SIGNAL_FUNC(destroy), NULL);
/* ... */
start_routine_for_stream_that_calls_the_above(...)
/* ... */
gtk_widget_show_all(window);
gtk_main();
My problem is that it's not working this way... either I see only the last GdkPixbuf image or I see none, which is the correct behaviour ...
But how do I manage it to show an (stream of) updated GdkPixbuf?
Thanks for help
You need to be running the main loop while you change the images. For instance, you can do gtk_main() and use g_timeout_add() to schedule your callback to run say every second and replace images within that callback.

How do I change the colors of an arbitrary widget in GTK+?

If I'm writing an application that wants to communicate some information through the use of color, how can I change the background and foreground colors of a given widget? I would like to know how to do this in glade if it's possible, as well as programmatically (to a computed color).
I want to know how to do this to a complex widget as well, for example, an HBox that contains a VBox that contains some Labels.
Ideally this would also include a solution solution that allows me to tint the widget's existing colors, and identify the average colors of any images in use by the theme, so that I can programmatically compensate for any color choices which might make text unreadable or otherwise clashing - but I would be happy if I could just turn a button red.
Example program:
#include <gtk/gtk.h>
static void on_destroy(GtkWidget* widget, gpointer data)
{
gtk_main_quit ();
}
int main (int argc, char* argv[])
{
GtkWidget* window;
GtkWidget* button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT (window), "destroy",
G_CALLBACK (on_destroy), NULL);
button = gtk_button_new_with_label("Hello world!");
GdkColor red = {0, 0xffff, 0x0000, 0x0000};
GdkColor green = {0, 0x0000, 0xffff, 0x0000};
GdkColor blue = {0, 0x0000, 0x0000, 0xffff};
gtk_widget_modify_bg(button, GTK_STATE_NORMAL, &red);
gtk_widget_modify_bg(button, GTK_STATE_PRELIGHT, &green);
gtk_widget_modify_bg(button, GTK_STATE_ACTIVE, &blue);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The best documentation that I know of is the one available here: http://ometer.com/gtk-colors.html
You can always use gtk_widget_override_color () and gtk_widget_override_background_color (). These two functions allow you to change the color of a widget. But it is better to use CSS classes and regions in your widget/container implementation through gtk_style_context_add_class() and gtk_style_context_add_region().
To modify the color of a widget you can initialize a color and use it to modify the color of the widget:
GdkColor color;
gdk_color_parse("#00FF7F", &color);
gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &color);
To use an image instead of color:
GdkPixbuf *image = NULL;
GdkPixmap *background = NULL;
GtkStyle *style = NULL;
image = gdk_pixbuf_new_from_file ("background.jpg", NULL);
gdk_pixbuf_render_pixmap_and_mask (image, &background, NULL, 0);
style = gtk_style_new ();
style->bg_pixmap [0] = background;
gtk_widget_set_style (GTK_WIDGET(widget), GTK_STYLE (style));