Gtk+ not updating the widgets correctly - gtk

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.

Related

Gtk3: GtkImage prevents user from shrinking the window

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.

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.

How to avoid blocking function g_application_run

I have an application with the following pattern (it's an embedded application) that I don't want to change:
void
main(int argc, char *argv[])
{
some_init_functions();
while(1) {
some_functions();
}
}
I'd like to simulate the application on Windows so I'd like to use Gtk+3 to show a window that simulate the display used in the original application.
The problem is the function g_application_run() that is blocking.
Is it possible to manually call a Gtk+/GLib function to process pending events only and returns immediately? I would call this function in the while(1) loop.
I read about gtk_main_iteration_do(), but I didn't understand how to use it without calling g_application_run().
It's your job to integrate your application in the GUI, not the other way around. What you should do is call g_application_run, and add an event source with g_timeout_add or g_idle_add that will call your callback. In that callback, just call some_functions() once. The GTK main loop will take care of calling it again and again.
It is possible to get out of the g_application_run loop. Events can be handled by g_main_context_iteration(GMainContext *context, gboolean may_block). With may_block = TRUE, it will only return to the main loop after an event has been handled. With may_block = FALSE, only the highest priority events will be handled after which control returns to the loop.
The code below is based upon the example-0.c code from the GTK Reference Manual, yet making the process flow from g_application_run explicit. Note that command line arguments are not processed, and several checks are omitted.
A timeout event is added such that, in combination with may_block = TRUE, the main loop advances at least once per second. Also, a callback is added on the destroy event, such that, in combination with a global variable, the main loop can stop if the window is closed.
As a direct answer to the question asked: you would need to make sure correct set-up and break-down is performed, and add g_main_context_iteration (context, may_block); to your while (1) loop.
#include <gtk/gtk.h>
static bool application_running; // As use_count is private
// Add close_window event watcher to tell our application loop we should stop
static void
close_window (void)
{
printf("Window is closed\n");
application_running = FALSE;
}
// Add timeout to make sure the application loop is unblocked once per second
static gboolean
timeout (gpointer data)
{
printf("timeout\n");
return true;
}
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window;
window = gtk_application_window_new (app);
// Add destroy call-back so we know when window is closed.
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
gtk_widget_show (window);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
g_timeout_add_seconds (1, timeout, NULL);
// **** begin g_application_run alternative ****
// Setup
GMainContext *context;
gboolean acquired_context;
context = g_main_context_default ();
acquired_context = g_main_context_acquire (context);
g_return_val_if_fail (acquired_context, 0);
GError *error = NULL;
if (!g_application_register (G_APPLICATION (app), NULL, &error))
{
g_printerr ("Failed to register: %s\n", error->message);
g_error_free (error);
return 1;
}
g_application_activate (G_APPLICATION (app));
// Main event loop
application_running = true;
int loop = 0;
while (application_running) {
bool may_block = TRUE;
// may_block = TRUE: g_main_context_iteration blocks execution if no events are coming
// may_block = FALSE: g_main_context_iterations processes pending events and continues
g_main_context_iteration (context, may_block);
printf("Loop %i\n", loop++);
}
// Release
g_settings_sync ();
while (g_main_context_iteration (context, FALSE))
;
g_main_context_release (context);
// **** end g_application_run alternative ****
g_object_unref (app);
return status;
}

blank dialogue box without my text nor OK button

I'm trying to popup an OK message box in my gtk browser, but all I'm getting is blank grey square patch without my text in it and even button is not visible.
PFB the function which I'm using to render message box :
Void DisplayOKPopup()
{
dialogue=gtk_message_dialogue_new(GTK_WINDOW(WebtBrowserWindow),
GTK_DIALOGUE_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"text message");
gtk_widget_show(dialogue);
LOGDEBUG(" 1");
gtk_dialogue_run(GTK_DIALOGUE (dialogue));
LOGDEBUG("2");
gtk_widget_destroy (dialogue);
}
As per my debug log, I can see that control is passing till LOGDEBUG("1") and after that it goes to gtk_dialogue_run after that UI is getting crashed, the line next to run i.e LOGDEBUG("2") is not getting executed.
Kindly provide your inputs as I'm working on this since 3 days:!
As concluded, the problem arises from the use of threads. There are several approaches to solve the issue but since there is not code, I'll try with a simple example so that you can recreate it on your code.
Take a global Boolean variable as a flag and on your thread, set it to true so that a "periodic" idle callback can check it and if the flag is TRUE then show the dialog. The logic is that the Gtk UI functions are called from the mainloop/main thread and not from the worker threads.
DisplayOkPopup just has a simple "counter" from 0 to MAX_INT (32 bits) and set's the global flag as TRUE.
check_for_dialog it's a callback that runs on mainloop idle time and check for the flag, if TRUE then runs the dialog.
exit is dirty and will output errors but the goal ain't that, its just a tip/hint for your solution.
Example:
#include <gtk/gtk.h>
gboolean dialog_active;
gboolean show_dialog_popup;
GtkWidget *window;
gpointer DisplayOKPopup (gpointer user_data) {
int i;
while (TRUE) {
for (i = 0; i < G_MAXINT32; i++) {
// nop
}
show_dialog_popup = TRUE;
}
return NULL;
}
gboolean check_for_dialog (gpointer user_data) {
if (show_dialog_popup == TRUE && dialog_active == FALSE) {
dialog_active = TRUE;
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"text message");
gtk_dialog_run(GTK_DIALOG (dialog));
show_dialog_popup = FALSE;
dialog_active = FALSE;
gtk_widget_destroy(dialog);
}
return TRUE;
}
static void app_activate(GtkApplication *app, gpointer user_data) {
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "Window Title Here");
gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
//gtk_container_add(GTK_CONTAINER(window), fixed);
gtk_widget_show_all(window);
g_idle_add(check_for_dialog, NULL);
g_thread_new("my thread", DisplayOKPopup, NULL);
}
int main(int argc, char **argv) {
GtkApplication *app;
int status;
show_dialog_popup = FALSE;
dialog_active = FALSE;
app = gtk_application_new("your.application.id", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(app_activate), NULL);
status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
Build and run:
$ gcc -o test test.c `pkg-config --cflags --libs gtk+-3.0`
$ ./test
Result:
EDIT:
Answer to your comment is:
To have custom buttons on your dialog then use:
gtk_dialog_new_with_buttons
gtk_dialog_add_button (if dialog exists, will add 1 button)
gtk_dialog_add_buttons (same as above but can add many buttons)
Example for 1):
dialog = gtk_dialog_new_with_buttons ("My dialog",
main_app_window,
flags,
"_OK",
GTK_RESPONSE_ACCEPT,
"_Cancel",
GTK_RESPONSE_REJECT,
NULL);
Example for 2):
gtk_dialog_add_button (GTK_DIALOG(dialog),
"This is my button",
GTK_RESPONSE_ACCEPT);
Example for 3) is same as 2) but can handle many buttons and terminates with NULL.

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.