GTK3 - radiobutton: callback is called twice every time toggle the button - c

I'm trying to use gtk3 radiobutton following the turorial bellow. The problem is I noticed the callback funcion is being called twice everytime I click in the button.
My question is why and how can I change this for the callback to be called one time when I click in a radio button?
https://developer.gnome.org/gnome-devel-demos/stable/radiobutton.c.html.en
#include <gtk/gtk.h>
static void foo(GtkWidget *widget, gpointer data)
{
char *id_radio = (char*)data;
g_print("%s\n", id_radio);
}
int main(int argc, char *argv[])
{
gtk_init(&argc, & argv);
GtkWidget *radio_1, *radio_2, *radio_3, *vbox, *window;
radio_1 = gtk_radio_button_new_with_label(NULL, "Radio 01");
radio_2 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radio_1), "Radio 02");
radio_3 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radio_1), "Radio 03");
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(GTK_TOGGLE_BUTTON(radio_1), "toggled", G_CALLBACK(foo), (gpointer)"1");
g_signal_connect(GTK_TOGGLE_BUTTON(radio_2), "toggled", G_CALLBACK(foo), (gpointer)"2");
g_signal_connect(GTK_TOGGLE_BUTTON(radio_3), "toggled", G_CALLBACK(foo), (gpointer)"3");
gtk_box_pack_start(GTK_BOX(vbox), radio_1, 1, 1, 0);
gtk_box_pack_start(GTK_BOX(vbox), radio_2, 1, 1, 0);
gtk_box_pack_start(GTK_BOX(vbox), radio_3, 1, 1, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show_all(window);
gtk_main();
return 0;
}

The "toggled" signal which is inherited from a toggle button, triggers both on deactivation and activation. when you select a radio button, the previously selected button gets deactivated (the first callback), and the newly pressed one activates (the second callback).
If you want to filter out just the activations, get the toggle button's state inside the callback with gtk_toggle_button_get_active ().
As per documentation from:
https://developer.gnome.org/gtk3/stable/GtkToggleButton.html#gtk-toggle-button-get-active

Related

How can I configure a GtkWidget signal that runs every time a widget is shown?

The show signal seems to only be called the first time a widget is shown. If I call gtk_widget_show on the widget in question, if it has already been shown, any functions passed to g_signal_connect to the widget in question with the show signal will not be called. I attempted to use the show signal for a button whose text is determined by an external state that the button changes, so when the button first becomes visible it successfully shows the initial state, but even though I change the state in the button handler then call gtk_widget_show, the text is not updated when the button is clicked. How can I configure a widget to run on EVERY show event, not just the first.
Requirement:
update button label based on state
state change happen on button click
One solution could be to update the status when the button is clicked, and then set the button's label, e.g. like this:
update_state();
gtk_button_set_label(button, state);
If more parts of the user interface than just the button should be updated, one could think about introducing a separate function updateUI.
A small demo could look like this:
#include <gtk/gtk.h>
static int cnt;
static char state[16];
static void update_state() {
snprintf(state, sizeof(state), "clicked %d", cnt++);
}
static void button_show(__unused GtkWidget *widget, __unused gpointer data) {
g_print("show\n");
}
static void button_clicked(GtkButton *button, __unused gpointer data) {
g_print("clicked\n");
update_state();
gtk_button_set_label(button, state);
}
static void buildUI(GApplication *app, __unused gpointer data) {
GtkWidget *window = gtk_application_window_new(GTK_APPLICATION(app));
gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
GtkWidget *button = gtk_button_new();
gtk_button_set_label(GTK_BUTTON(button), state);
gtk_widget_set_size_request(button, 80, 32);
gtk_widget_set_halign(button, GTK_ALIGN_START);
gtk_widget_set_valign(button, GTK_ALIGN_START);
gtk_container_add(GTK_CONTAINER(window), button);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(button_clicked), NULL);
g_signal_connect(G_OBJECT(button), "show",
G_CALLBACK(button_show), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(GTK_WIDGET(window));
}
int main(int argc, char *argv[]) {
GtkApplication *app = gtk_application_new("com.example.MyApp", G_APPLICATION_FLAGS_NONE);
update_state();
g_signal_connect(app, "activate", G_CALLBACK(buildUI), NULL);
g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return 0;
}
It updates the label whenever the button is clicked.

Should I use a global variable in gtk callback functions? [duplicate]

This question already has answers here:
Passing additional arguments to gtk function
(3 answers)
GTK passing structure to callback function in C
(1 answer)
Closed 3 years ago.
Hi I'm trying to program with GTK+ 2 and when I need to create a popup window after the "clicked' signal of a button, I use " gtk_widget_set_sensitive(button, FALSE) " so as not to be able to launch another popoup window from the same button when one is already launched. Yet the only way to close the popup with another callback function containing " gtk_widget_destroy(popup) " also means this callback can't "see" the first button to turn it sensitive again. Should I use a global variable and asign the popup window to it so my second callback function will see it? Or do I define more global widgets ?
What I'm really getting at is using global variables in something like GTK a common thing to do?, such as in header files, or is it bad practice at all?
Here is a sample program (with less stuff in the first callback than normal!)
#include <gtk/gtk.h>
// Is it good to set a temporary global GtkWidget * so I can asign it to
// button1 later and then use this temporary variable to set the widget sensitive again.
// GtkWidget * temp;
static void popup_win(GtkWidget *, gpointer data);
static void close_up(GtkWidget *, GtkWidget *);
int main(int argc, char * argv[])
{
GtkWidget * window, * button1, * vbox;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "EXAMPLE PROGRAM");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_widget_set_size_request(window, 800, 300);
button1 = gtk_button_new_with_label("Click here");
g_signal_connect(G_OBJECT(button1), "clicked",
G_CALLBACK(popup_win), NULL);
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), button1, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
static void popup_win(GtkWidget * btn, gpointer data)
{
gtk_widget_set_sensitive(btn, FALSE);
// I could use temp here. Then get to it in close_up function.
// temp = btn
GtkWidget * window2, * button2, * hbox;
window2 = gtk_window_new(GTK_WINDOW_POPUP);
gtk_window_set_title(GTK_WINDOW(window2), "POPUP WINDOW");
gtk_container_set_border_width(GTK_CONTAINER(window2), 10);
gtk_widget_set_size_request(window2, 100, 200);
button2 = gtk_button_new_with_label("Click me too");
g_signal_connect(G_OBJECT(button2), "clicked",
G_CALLBACK(close_up), (gpointer) window2);
hbox = gtk_hbox_new(TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox), button2, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window2), hbox);
gtk_widget_show_all(window2);
}
static void close_up(GtkWidget * btn, GtkWidget * win)
{
// can't get at button1 so I can't set widget sensitve again.
// gtk_widget_set_sensitive(button1, TRUE);
// Unless I'm using temp variable.
// gtk_widget_set_sensitive(temp, TRUE);
gtk_widget_destroy(win);
}```

How do I intercept a gtk window close button click?

On on GTK window there is a red close icon rendered in the title bar. Normally when you click on this, the window is closed and it's resources released.
Is there a way of intercepting the normal flow to prevent the window from being destroyed so that I can show it again later? i.e. I want to hide the window not close/destroy it.
This is what I have so far.
void destroy_window_callback(GtkWidget* widget, WebWindow_Linux* source)
{
printf("Don't destroy the window, just hide it.\n");
}
g_signal_connect(web_window, "destroy", G_CALLBACK(destroy_window_callback), this);
This is probably what you need
#include <gtk/gtk.h>
void
on_button_clicked(GtkButton *button, gpointer data)
{
GtkWidget *widget;
widget = (GtkWidget *) data;
if (widget == NULL)
return;
gtk_widget_show(widget);
return;
}
gboolean
on_widget_deleted(GtkWidget *widget, GdkEvent *event, gpointer data)
{
gtk_widget_hide(widget);
return TRUE;
}
int
main(int argc, char **argv)
{
GtkWidget *window1;
GtkWidget *window2;
GtkWidget *button;
gtk_init(&argc, &argv);
window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
window2 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("Show again...");
g_signal_connect(G_OBJECT(window1),
"destroy", gtk_main_quit, NULL);
g_signal_connect(G_OBJECT(window2),
"delete-event", G_CALLBACK(on_widget_deleted), NULL);
g_signal_connect(G_OBJECT(button),
"clicked", G_CALLBACK(on_button_clicked), window2);
gtk_container_add(GTK_CONTAINER(window1), button);
gtk_widget_set_size_request(window1, 300, 100);
gtk_widget_set_size_request(window2, 300, 100);
gtk_widget_show_all(window1);
gtk_widget_show(window2);
gtk_main();
return 0;
}
We basically have three widgets, two top level windows and a button. The first window has it's "destroy" event connected to gtk_main_quit() quitting the application when the window's close button is pressed. The second window has it's "delete-event" connected to a custom function. This is the important one. As you see it returns TRUE indicating that the signal was handled and thus preventing to call the default handler and hence preventing the call to gtk_widget_destroy(). Also in it we can hide the widget if we want.

How to center a dialog window on the main window in GTK?

I want to print small warning messages. To achieve that, I'm creating a new window and I want it to be centered on the main window. So far I've tried setting its parent with gtk_widget_set_parent, using GTK_WINDOW_POPUP and GTK_WINDOW_TOPLEVEL but nothing worked.
How to center the window on the main window?
Here's the code for the function:
static void pop_warning(char *title, char *text)
{
GtkWidget *warning_window;
GtkWidget *box;
GtkWidget *label;
GtkWidget *button;
warning_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(warning_window), title);
g_signal_connect(GTK_WINDOW(warning_window), "destroy", G_CALLBACK(gtk_widget_destroy), NULL);
gtk_container_set_border_width(GTK_CONTAINER(warning_window), 20);
gtk_window_set_resizable(GTK_WINDOW(warning_window), FALSE);
//window is the main window
gtk_widget_set_parent(warning_window, window);
gtk_window_set_position(GTK_WINDOW(warning_window), GTK_WIN_POS_CENTER_ON_PARENT);
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
gtk_container_add(GTK_CONTAINER(warning_window), box);
label = gtk_label_new(text);
gtk_box_pack_start(GTK_BOX(box), label, TRUE, FALSE, 0);
button = gtk_button_new_with_label("Ok");
g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_widget_destroy), warning_window);
gtk_box_pack_start(GTK_BOX(box), button, TRUE, FALSE, 0);
gtk_widget_show_all(warning_window);
}
gtk_window_set_position(GTK_WINDOW(warning_window), GTK_WIN_POS_CENTER_ON_PARENT);
only works if you first call
gtk_window_set_transient_for(GtkWindow *window, GtkWindow *parent);
as mentioned explicitly in the documentation.
You could also try to use GTK_WIN_POS_CENTER_ALWAYS that has not the above mentioned constrain - at least not according the docs.

Auto scrolling GtkScrolledWindow with GtkTextView wrapped in GtkBox

When I have GtkTextView in GtkScrolledWindow, it scrolls automatically when user appends new line at the bottom of the widget. When I put GtkTextView in GtkBox and then in GtkScrolledWindow I doesn't work. I need to put box between GtkTextView and scrollbar and I can't do that without placing whole thing in another box. Is there any way to preserve autoscrolling behaviour when using GtkBox inside GtkScrolledWindow?
There is code that ilustrates my problem:
#include <gtk/gtk.h>
int main(int argc, char *argv[]) {
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(window),
gdk_screen_width()*0.5, gdk_screen_height()*0.5);
GtkWidget *main = gtk_box_new(0, 0);
gtk_container_add(GTK_CONTAINER(window), main);
/*/////////////////////////////////////////////////*/
/* IMPORTANT PART */
/* FIRST CASE (this one works correctly) */
GtkWidget *scrolled_window_first = gtk_scrolled_window_new(NULL, NULL);
GtkWidget *text_view_first = gtk_text_view_new();
gtk_container_add(GTK_CONTAINER(scrolled_window_first), text_view_first);
gtk_box_pack_start(GTK_BOX(main), scrolled_window_first, 1, 1, 0);
/* SECOND CASE (there is no auto scroll which I need) */
GtkWidget *scrolled_window_second = gtk_scrolled_window_new(NULL, NULL);
GtkWidget *text_view_second = gtk_text_view_new();
GtkWidget *box_from_second_example = gtk_box_new(0, 0);
GtkWidget *example_box_before_scroller = gtk_box_new(0,0);
GtkWidget *example_label = gtk_label_new("I need this box badly!");
gtk_box_pack_start(GTK_BOX(box_from_second_example), text_view_second, 1, 1, 0);
gtk_container_add(GTK_CONTAINER(scrolled_window_second), box_from_second_example);
gtk_container_add(GTK_CONTAINER(box_from_second_example), example_box_before_scroller);
gtk_box_pack_start(GTK_BOX(main), scrolled_window_second, 1, 1, 0);
/* END IMPORTANT PART */
/*/////////////////////////////////////////////////*/
gtk_container_add(GTK_CONTAINER(example_box_before_scroller), example_label);
g_signal_connect(GTK_WINDOW(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
The problem is GtkTextView implements GtkScrollable but GtkBox does not. This is not a minor issue: if you want to go the GtkBox way you should put it inside a GtkViewport and add the scrollability stuff... quite some code that involves deep understanding of how the whole thing scrolls.
Anyway if you are lazy enough you could also note GtkTextview is a GtkContainer. In other words you could add widgets around the GtkTextview without the need to incomodate GtkBox. Not exactly what you were looking for but maybe good enough for your purposes:
#include <gtk/gtk.h>
gint main(gint argc, gchar **argv)
{
GtkWidget *window, *scrolled_window, *text_view, *label;
gtk_init(&argc, &argv);
text_view = gtk_text_view_new();
gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)),
"1\n\n\n2\n\n\n3\n\n\n4\n\n\n5\n\n\n6\n\n\n7\n\n\n8", -1);
/* Use this to set the size you want to reserve on the right */
gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(text_view),
GTK_TEXT_WINDOW_RIGHT,
130);
/* Add whatever you want instead of a GtkLabel */
gtk_text_view_add_child_in_window(GTK_TEXT_VIEW(text_view),
gtk_label_new("You badly need this"),
GTK_TEXT_WINDOW_RIGHT,
0, 0);
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(scrolled_window), text_view);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(window), 320, 240);
gtk_container_add(GTK_CONTAINER(window), scrolled_window);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
Did you try to modify the GtkAdjustments the textview creates by default (GtkScrolledWindow implements the GtkScrollable afaik).
GtkAdjustment * gtk_scrollable_get_hadjustment (GtkScrollable *scrollable);
I think the best approach would be to detect insertions to the GtkTextBuffer (get the views buffer via
GtkTextBuffer * gtk_text_view_get_buffer (GtkTextView *text_view);
and hook a callback (which tests for \n) to its insert-text g_signal and use
void gtk_adjustment_set_value (GtkAdjustment *adjustment,
gdouble value);
to modify the scrolling height.
Note that this is untested and a simpler method might be around the corner.

Resources