gtk displaying a dialog from within a dialog - c

I'm writing a gtk program,
at some point in my application, I call a gtkdialog to obtain input from the user.
what I want to do is close the current dialog and open another dialog when the OK button is clicked, I already have a function doing some other work, starting the dialog in that function would be great.
here's the parent dialog code:
GtkWidget * new_button_dialog = gtk_dialog_new_with_buttons("new button",(GtkWindow *)container,GTK_DIALOG_DESTROY_WITH_PARENT,"OK", GTK_RESPONSE_OK,NULL);
GtkWidget * content_area = gtk_dialog_get_content_area (GTK_DIALOG (new_button_dialog));
GtkWidget * button_name_label = gtk_label_new ("Press escape at any time to cancel.\ntype in the button name");
gtk_container_add (GTK_CONTAINER (content_area) , button_name_label);
GtkWidget * button_name_entry = gtk_entry_new();
gtk_entry_set_activates_default((GtkEntry*)button_name_entry,TRUE);
gtk_container_add(GTK_CONTAINER(content_area), button_name_entry);
GtkWidget * button_text_dialog = gtk_dialog_new_with_buttons("new button",(GtkWindow *)container,GTK_DIALOG_DESTROY_WITH_PARENT,"OK", GTK_RESPONSE_OK,NULL);
gtk_container_add (GTK_CONTAINER (container), button);
g_signal_connect(new_button_dialog,"response",G_CALLBACK (add_new_button),NULL);
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",GTK_SIGNAL_FUNC (gtk_widget_show_all),(GtkWidget*)new_button_dialog);
gtk_widget_show_all((GtkWidget *)container);

The response of a dialog cannot be handled in the same function that the dialog is created. It means that in one function you cannot create a dialog, handle its input, close that dialog and open a new dialog. It cannot be done since when that function exits the dialog entry is empty and the first dialog is just shown.
It should be done as a chain of separate event handlers. All needed variables (data context) should be transferred between those separate handlers using a pointer to structure that holds that context.
The below full runnable example demonstrates such flow.
In the fist dialog handler we want to do something with the main window and some container object and maybe we want some other data that is shared between widgets of that main window. The structure window_ctx_t is declared for such context. There is a boolean flag in that context to lock creation on new dialog chains before closing active dialogs.
The function first_dialog_create creates the first dialog with text entry field. It receives window_ctx_t, where the main window pointer is stored that is used for the dialog creation. Now we need to attach to that dialog response handler and provide to the handler enough data for its functionality. In that example we provide pointer to text entry that is used to read text.
Using provided data the response handler add_new_button is able to read text entry, to create a new dialog attached to the main window and also to do something with provided container.
When the first dialog data context is not needed it is freed by calling g_free() in the dialog destroyed handler.
Build the example:
gcc -Wall -g dialogs.c -o dialogs `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0`
Example "dialogs.c"
#include <gtk/gtk.h>
typedef struct {
GtkWidget *container;
GtkWindow *window;
gboolean dialogs_created;
} window_ctx_t;
typedef struct {
GtkEntry *entry;
window_ctx_t *window_ctx;
} first_dialog_data_t;
static void first_dialog_create(gpointer context);
static void another_dialog_response(GtkWidget *dialog, gint response_id,
gpointer ctx)
{
window_ctx_t *win_ctx = ctx;
gtk_widget_destroy(dialog);
win_ctx->dialogs_created = FALSE;
}
static void cleanup_first_dialog(GtkWidget *widget, gpointer data)
{
g_free(data);
}
/* response callback of the first dialog */
static void add_new_button(GtkWidget *dialog, gint response_id,
gpointer ctx)
{
GtkWidget *button;
GtkWidget *button_text_dialog;
first_dialog_data_t *data = ctx;
if (response_id != GTK_RESPONSE_OK) {
data->window_ctx->dialogs_created = FALSE;
goto Exit;
}
/* Create a new button with the label from text entry */
button = gtk_button_new_with_label(gtk_entry_get_text(data->entry));
/* When the button receives the "clicked" signal, it will again open
* "new button" dialog */
g_signal_connect_swapped(button, "clicked", G_CALLBACK(first_dialog_create),
data->window_ctx);
/* add button to the container */
gtk_container_add(GTK_CONTAINER(data->window_ctx->container), button);
/* show button */
gtk_widget_show(button);
/* create another dialog */
button_text_dialog = gtk_dialog_new_with_buttons("new button",
data->window_ctx->window, GTK_DIALOG_DESTROY_WITH_PARENT, "OK",
GTK_RESPONSE_OK, NULL);
g_signal_connect(button_text_dialog, "response",
G_CALLBACK(another_dialog_response), (gpointer)data->window_ctx);
gtk_widget_show_all(button_text_dialog);
Exit:
gtk_widget_destroy(dialog);
}
static void first_dialog_create(gpointer context)
{
first_dialog_data_t *data;
window_ctx_t *win_ctx = context;
if (win_ctx->dialogs_created)
return;
/* lock buttons handler */
win_ctx->dialogs_created = TRUE;
/* allocate structure for dialog context */
data = g_new0(first_dialog_data_t, 1);
/* create first dialog, its label and text entry */
GtkWidget *new_button_dialog = gtk_dialog_new_with_buttons("new button",
win_ctx->window, GTK_DIALOG_DESTROY_WITH_PARENT,"OK",
GTK_RESPONSE_OK, NULL);
GtkWidget *content_area = gtk_dialog_get_content_area(
GTK_DIALOG(new_button_dialog));
GtkWidget *button_name_label = gtk_label_new("Press escape at any time to "
"cancel.\ntype in the button name");
gtk_container_add(GTK_CONTAINER(content_area), button_name_label);
GtkWidget * button_name_entry = gtk_entry_new();
gtk_entry_set_activates_default((GtkEntry*)button_name_entry,TRUE);
gtk_container_add(GTK_CONTAINER(content_area), button_name_entry);
/* provide data to response handler */
data->entry = (GtkEntry *)button_name_entry;
data->window_ctx = win_ctx;
g_signal_connect(new_button_dialog, "response", G_CALLBACK(add_new_button),
data);
/* handler to free dialog context 'data' when destroed */
g_signal_connect(new_button_dialog, "destroy",
G_CALLBACK(cleanup_first_dialog), data);
/* show first dialog */
gtk_widget_show_all(new_button_dialog);
}
static void destroy(GtkWidget *widget, gpointer data)
{
gtk_main_quit();
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *box1;
GtkWidget *button;
window_ctx_t win_ctx;
gtk_init (&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
box1 = gtk_hbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), box1);
/* set window context */
win_ctx.window = (GtkWindow *)window;
win_ctx.container = box1;
win_ctx.dialogs_created = FALSE;
g_signal_connect (window, "destroy", G_CALLBACK(destroy), NULL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
button = gtk_button_new_with_label("Start");
g_signal_connect_swapped(button, "clicked", G_CALLBACK(first_dialog_create),
&win_ctx);
gtk_container_add(GTK_CONTAINER(box1), button);
gtk_widget_show_all(window);
gtk_main();
return 0;
}

Related

how to change multiple widgets property on button click GTK c

I am trying to make UI using GTK in c for raspberry pi 4. I want to change the visibility of different widgets based on button click just to simulate a new page. I have tried everything available on the internet but as I am not that good at coding I cant figure out what is wrong.
can someone please help ?
This program compiles but when I press the button it gives error " assertion failed on gtk_widget_show " and also on widget hide. Also a segmentation fault occurs and the program crashes.
I am using cmake to compile my code. I have attached the error screen shot.
#include <gtk/gtk.h>
typedef struct AppData
{
GtkWidget *label1;
GtkWidget *label2;
} AppData;
static void button1 (gpointer data)
{
AppData *data2 = (AppData*)data;
gtk_widget_hide(data2->label1);
gtk_widget_show(data2->label2);
}
static void button2 ( gpointer data)
{
AppData *data2 = (AppData*)data;
gtk_widget_show(data2->label1);
gtk_widget_hide(data2->label2);
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *btn1;
GtkWidget *btn2;
GtkWidget *box1;
GtkWidget *box2;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "ethercat test 1");
gtk_window_set_default_size(GTK_WINDOW(window), 1000,500);
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);
box1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
gtk_fixed_put(GTK_FIXED(fixed), box1, 0,0);
box2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
gtk_fixed_put(GTK_FIXED(fixed), box2, 100,100);
AppData *app_data = g_new0 (AppData, 2);
app_data->label1 = gtk_label_new("label1");
gtk_box_pack_start(GTK_BOX(box1),app_data->label1, TRUE,TRUE,0);
app_data->label2 = gtk_label_new("label2");
gtk_box_pack_start(GTK_BOX(box2),app_data->label2, TRUE,TRUE,0);
btn1 = gtk_button_new_with_label("ethercat 1");
gtk_fixed_put(GTK_FIXED(fixed), btn1, 10, 450);
gtk_widget_set_size_request(btn1, 80,30);
btn2 = gtk_button_new_with_label("ethercat 2");
gtk_fixed_put(GTK_FIXED(fixed), btn2, 110, 450);
gtk_widget_set_size_request(btn2, 80,30);
gtk_widget_show_all(window);
g_signal_connect(G_OBJECT(btn1), "clicked", G_CALLBACK(button1), app_data);
g_signal_connect(G_OBJECT(btn2), "clicked", G_CALLBACK(button2), app_data);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
printf("program end\n");
return (0);
}
enter image description here
The function signature of your "clicked" callbacks is wrong. It should be of the form as described in the documentation:
void on_clicked(
GtkButton* self,
gpointer user_data
)
So for example, your button2() function becomes
static void button2 (GtkButton *btn2, gpointer data)
{
AppData *data2 = (AppData*)data;
gtk_widget_show(data2->label1);
gtk_widget_hide(data2->label2);
}

C Update GUI element of GTK3

#include <gtk/gtk.h>
GtkBuilder *builder;
GtkWidget *window;
GtkImage *image;
GtkButton *but;
char s[1000];
void on_button1_button_press_event(GtkWidget *but, gpointer data)
{
strcpy(s, "/home/linux/testing2.png");
gtk_widget_queue_draw (image);
}
int main(void)
{
gtk_init(NULL, NULL);
builder = gtk_builder_new();
gtk_builder_add_from_file(builder, "/home/linux/gui.xml", NULL);
window = (GtkWidget *) gtk_builder_get_object(builder, "window1");
image = (GtkImage *) gtk_builder_get_object(builder, "image1");
but = (GtkButton *) gtk_builder_get_object(builder, "button1");
gtk_image_set_from_file(image, strcpy(s, "/home/linux/testing1.png"));
g_signal_connect(but, "clicked", G_CALLBACK(on_button1_button_press_event), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
When I click on "button1", "callback" function called, and it updates string s then I want gtk refresh "image1" with gtk_widget_queue_draw. But "image1" have not changed.
gtk_widget_queue_draw() takes a string with the filename of the image. But it does not keep the string you pass. Instead the widget stores the whole loaded image.
So your s variable is useless, and changing it does nothing. You should do instead, in main():
gtk_image_set_from_file(image, "/home/linux/testing1.png");
and in the event callback:
gtk_image_set_from_file(image, "/home/linux/testing2.png");
There is no need to call gtk_widget_queue_draw() because changing the image does that automatically.

How to pass an object to a GTK3 callback?

I'm trying to pass an object to the callback of a "clicked" event in order to set the text of the target label.
Here's my code so far:
#include <gtk/gtk.h>
typedef struct {
int i;
GtkWidget *target;
} Data;
void change( GtkWidget *widget,
Data *data )
{
gtk_label_set_text(GTK_LABEL(data->target), "it did!");
}
int main( int argc,
char* argv[] )
{
gtk_init(&argc, &argv);
GtkWidget *window, *label, *button;
Data data;
data.i = 0;
data.target = label;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GTKdemo");
g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
GtkGrid *grid = gtk_grid_new();
button = gtk_button_new_with_label("Click here");
g_signal_connect(button, "clicked", G_CALLBACK(change), &data);
gtk_grid_attach(grid, button, 0,0,1,1);
label = gtk_label_new("this will change");
gtk_grid_attach(grid, label, 0,1,1,1);
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(grid));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
I'm trying to set the text of label when button is clicked.
I tried gtk callback multiple arguments but no dice. Help?
Okay, I fixed it by moving data.target = label to execute after the g_signal_connect of the button.
Perhaps the problem arose from using the pointer address before the object variables were defined.

set dialog not resizable or create another gtk window

I have a gtk_window which contain 4 buttons.
One of these buttons will open a file selection dialog (another function) which - when the file has been choosed - will show up a dialog with 3 gtk_entry (this function).
static void function_with_3_gtk_entry (gchar *message, gpointer mainwin){
GtkWidget *dialog, *label, *content_area, *entry1, *entry2, *entry3;
/* Create the widgets */
dialog = gtk_dialog_new_with_buttons ("Nome File", mainwin, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_NONE, NULL);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
entry1 = gtk_entry_new();
entry2 = gtk_entry_new();
entry3 = gtk_entry_new();
gtk_widget_set_size_request(dialog, 250, 200);
/* Ensure that the dialog box is destroyed when the user responds */
g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
/* Add the label, and show everything we've added to the dialog */
gtk_container_add (GTK_CONTAINER (content_area), entry1);
gtk_container_add (GTK_CONTAINER (content_area), entry2);
gtk_container_add (GTK_CONTAINER (content_area), entry3);
gtk_widget_show_all (dialog);
}
My questions are:
can i use another gtk_window into this function instead of a gtk_dialog?
how can i set the dialog not resizable?
The code snippet below has two functions:
create_dialog() creates a dialog which cannot be resized, as requested.
create_window() creates a window, within a function, as requested.
Hope this helps.
#include <gtk/gtk.h>
/* Create a dialog, which cannot be resized by the user. */
void create_dialog(GtkWidget *button, gpointer window) {
GtkWidget *dialog, *label, *content_area;
/* New label for dialog content */
label = gtk_label_new("This is a dialog!");
/* Make a new dialog with an 'OK' button */
dialog = gtk_dialog_new_with_buttons("This is a dialog, which (shouldn't | can't) be resized!", window, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_NONE, NULL);
/* Add label to dialog */
content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
gtk_container_add(GTK_CONTAINER(content_area), label);
/* Destroy dialog properly */
g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog);
/* Set dialog to not resize. */
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
gtk_widget_show_all(dialog);
}
/* Create a window, while in a function! */
void create_window(GtkWidget *button, gpointer window) {
GtkWidget *new_window, *label;
/*New label for dialog content */
label = gtk_label_new("This is a window, created in a function!");
/* Create new window */
new_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
/* Add label to window */
gtk_container_add(GTK_CONTAINER(new_window), label);
gtk_widget_show_all(new_window);
}
/* Boring implementation bits */
gint main(gint argc, char **argv) {
/* Initialise GTK+ */
gtk_init(&argc, &argv);
GtkWidget *main_win, *dialog_button, *window_button, *button_container;
/* Create a button, one for each function. */
dialog_button = gtk_button_new_with_label("Create dialog!");
window_button = gtk_button_new_with_label("Create window!");
/* Pack buttons into a box. */
button_container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_box_pack_start(GTK_BOX(button_container), dialog_button, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(button_container), window_button, FALSE, FALSE, 0);
/* Connect signals to button callback functions */
g_signal_connect(dialog_button, "clicked", G_CALLBACK(create_dialog), main_win);
g_signal_connect(window_button, "clicked", G_CALLBACK(create_window), main_win);
/* Create a new window, show it, and run GTK+ */
main_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(main_win, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(main_win), button_container);
gtk_widget_show_all(main_win);
gtk_main();
return 0;
}

How to get the text of a button in GTK?

I am developing an application that has a numeric keypad and a text box when clicked on a button, text box shows the number.
I need to write a function to each button? Or you can pass a text and a widget as parameter?
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
void callback( GtkWidget *widget,
gpointer data )
{
gtk_entry_append_text(entry, text);
}
void create_button(GtkWidget* table,GtkWidget* entry,
int start_r,int end_r,
int start_c,int end_c,
char* label)
{
GtkWidget *button;
button = gtk_button_new_with_label (label);
g_object_set_data( G_OBJECT( button ),
"char", (gpointer)label );
g_signal_connect (button, "clicked",
G_CALLBACK (callback), entry);
gtk_table_attach_defaults (GTK_TABLE(table), button, start_c, end_c, start_r, end_r);
gtk_widget_show (button);
}
gint delete_event( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
gtk_main_quit ();
return(FALSE);
}
int main(int argc,char* argv[]){
GtkWidget *window;
//GtkWidget *button;
GtkWidget *table;
GtkWidget *entry;
//GtkWidget *label;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Armario");
g_signal_connect (GTK_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), NULL);
gtk_container_set_border_width (GTK_CONTAINER (window), 20);
table = gtk_table_new (2, 2, TRUE);
gtk_container_add (GTK_CONTAINER (window), table);
entry = gtk_entry_new();
gtk_entry_set_max_length(GTK_ENTRY(entry),10);
gtk_entry_set_placeholder_text(GTK_ENTRY(entry),"Teste");
gtk_table_attach_defaults (GTK_TABLE(table), entry, 0, 2, 1, 2);
gtk_widget_show(entry);
create_button(table,entry,0,1,0,1,"Botao");
gtk_widget_show (table);
gtk_widget_show (window);
gtk_main ();
return 0;
}
The answer to your question is yes, you can pass a widget in Gtk+ to a callback. Actually, the first parameter of the callback for the clicked signal is the button which received the signal (ie. usually the button that was clicked). As you can see in the sample code below, you can extract from the button its label and use it as text.
[...] /* In create_button... */
/* Make your buttons be notified when they are clicked */
g_signal_connect (button, "clicked", G_CALLBACK (on_button_clicked), entry);
[...]
/* Append the text in the button to the text entry */
void on_button_clicked (GtkButton *button, gpointer user_data)
{
GtkEntry *entry = user_data;
const gchar *text = gtk_button_get_label (button);
gint position = 0;
gtk_editable_insert_text (GTK_EDITABLE (entry), text, -1, &position);
}
I'm using gtk_editable_insert_text because gtk_entry_append_text has been deprecated for a long time. Passing the "entry" parameter to the callback is possible by using the last parameter of g_signal_connect which allows you to specify some data that you need to access in your callback. This info is then made available to the callback in the "user_data" parameter.
Your exemple could also be improved by using gtk_widget_show_all, and I also don't see the point in calling g_object_set_data on the "char" property, as the text is already set in the label property (and retrieved with gtk_button_get_label).
Two options, that I can think of:
GTK widgets are really GObjects, so you can attach to them arbitrary pieces of data. See the functions g_object_set_data/g_object_set_data_full/g_object_get_data. So you can just add the text to the button as an attached data and retrieve it when needed.
You can pass any data you need to a callback by defining a struct with all the fields, and passing a pointer to it. If the struct cannot be declared statically, you can malloc it and use g_signal_connect_data to specify the function to release the data:
For example:
struct entry_and_text;
{
GtkWidget *w;
char *text;
};
void free_data(gpointer data, GClosure *closure)
{
free(data);
}
entry_and_text *data = (entry_and_text *)malloc(sizeof(entry_and_text));
data->w = entry;
data->text = label;
g_signal_connect_data (button, "clicked",
G_CALLBACK (callback), data, free_data, 0);

Resources