I need to output the i progress bars and update them all. But only the last one updates i times. This is the code:
static void calculaPi (GtkButton * boton, Datos * dDatos){
const char * threads;
GtkWidget * barra, *bot2, *button, *progress, *vbox;
threads = gtk_entry_get_text(GTK_ENTRY(dDatos->dthreads ));
gint ithreads = 1;
ithreads = atoi(threads);
barra = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title((GtkWindow *) barra, "Loteria de Threads");
gtk_window_set_default_size(GTK_WINDOW(barra), 300, ithreads*30);
gtk_window_set_position(GTK_WINDOW(barra), GTK_WIN_POS_CENTER);
button = gtk_button_new_with_label ("Click me!");
vbox = gtk_vbox_new (FALSE, 5);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 5);
gtk_container_add (GTK_CONTAINER (barra), vbox);
for (gint i = 1 ; i <= ithreads; i++) {
progress = gtk_progress_bar_new ();
gtk_box_pack_start (GTK_BOX (vbox), progress, FALSE, FALSE, 5);
g_object_set_data (G_OBJECT (barra), "pbar", (gpointer) progress);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (button_clicked), (gpointer) barra);
}
bot2 = gtk_button_new_with_label("Salir");
gtk_box_pack_start (GTK_BOX (vbox), bot2, FALSE, FALSE, 5);
gtk_widget_set_size_request(bot2, 100, 35);
g_signal_connect (G_OBJECT (bot2), "clicked",
G_CALLBACK (destroy),
G_OBJECT (barra));
gtk_widget_show_all(barra);
gtk_main();
}
static void
button_clicked (GtkButton *button,
GtkWidget *barra)
{
GtkProgressBar *progress;
gdouble percent = 0.0;
gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
progress = GTK_PROGRESS_BAR (g_object_get_data (G_OBJECT (barra), "pbar"));
while (percent <= 100.0)
{
gchar *message = g_strdup_printf ("%.0f%% Complete", percent);
gtk_progress_bar_set_fraction (progress, percent / 100.0);
gtk_progress_bar_set_text (progress, message);
while (gtk_events_pending ())
gtk_main_iteration ();
g_usleep (500000);
percent += 5.0;
}
}
With this line:
g_object_set_data (G_OBJECT (barra), "pbar", (gpointer) progress);
you override previous value of "pbar" data entry on each loop iteration. When you later retrive it in button_clicked() you get the last set value, i.e. the last progress bar.
In this particular case you can just pass progress widget as user data (and drop g_object_[gs]et_data() calls) for button_clicked() callback: the function doesn't use current barra window for anything else anyway.
In a more general way, you should learn how to use your own structures for user data parameters. A common way is to declare and use one structure for given toplevel type and store pointers to widgets you need to access from callbacks in it.
Related
edit: The problem described here seems to be theme related. I'm using Greybird from XUbuntu here.
The program
In my program you can click a button to create more buttons in a vertical GtkBox. If the buttons exceed the visible space a GtkScrollbar will be shown:
Problem
When the GtkScrollbar is shown for the first time, it dos not show on the side, as it should be; It's shown below the buttons:
This is weird, because the scrollbar is actually inside a horizontal GtkBox in a cell next to those buttons.
In this state, the buttons are not clickable anymore until either of of the following happens:
click the scroll bar
scroll via mouse wheel
click the window border
focus another application
UI Architecture
This is the architecture of my UI:
Whether the scrollbar is shown or not, is decided when the "page-size" property the GtkAdjustment is changed.
Code
#include <gtk/gtk.h>
gboolean
adj_page_sizechanged (GtkAdjustment *adjustment,
GdkEvent *unused,
GtkScrollbar *scrollbar)
{
gdouble maxValue = gtk_adjustment_get_upper (adjustment);
gdouble maxSize = gtk_adjustment_get_page_size (adjustment);
gboolean show = (maxValue - maxSize >= 0.000001);
g_object_set (scrollbar, "visible", show, NULL);
return FALSE;
}
/**
* Creates a `automatic` scollbar for `widget`, that will never
* overlay (hide) the content of `widget`.
**/
GtkWidget *
gtkx_scrollable_widget_vertical_new (GtkWidget *widget,
GtkAdjustment *adjustment_nullable)
{
GtkWidget *root = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
// setup adjustment
GtkAdjustment * adj_v = adjustment_nullable;
if (NULL == adj_v)
adj_v = gtk_adjustment_new (0,0,100,1,10,10);
// setup scrollbar
{
GtkWidget * scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, adj_v);
g_signal_connect(adj_v, "notify::page-size", G_CALLBACK (adj_page_sizechanged), scrollbar);
gtk_box_pack_end (GTK_BOX (root), scrollbar, FALSE, FALSE, 0);
}
// setup scrollable area
{
GtkWidget * scrolled_window = gtk_scrolled_window_new (NULL, adj_v);
gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_NEVER, GTK_POLICY_EXTERNAL);
gtk_box_pack_start (GTK_BOX (root), scrolled_window, TRUE, TRUE, 0);
}
return root;
}
void
add_button (GtkButton *button,
GtkBox *box)
{
g_print("add button\n");
gtk_container_add (GTK_CONTAINER (box), gtk_button_new_with_label ("b 2"));
gtk_widget_show_all (GTK_WIDGET (box));
}
GtkWidget *
create_ui()
{
GtkWidget * action_button = NULL;
GtkWidget * box_outer = NULL;
GtkWidget * box_inner = NULL;
box_inner = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
action_button = gtk_button_new_with_label ("click me");
gtk_container_add (GTK_CONTAINER (box_inner), action_button);
g_signal_connect (action_button, "clicked", G_CALLBACK (add_button), box_inner);
box_outer = gtkx_scrollable_widget_vertical_new (box_inner, NULL);
return box_outer;
}
/**
* standard stuff. See `create_ui()`
**/
int main(int argc, char *argv[])
{
gtk_init (&argc, &argv);
GtkWidget *window;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), -1, 60);
GtkWidget * ui = create_ui ();
gtk_container_add (GTK_CONTAINER (window), ui);
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (gtk_main_quit), G_OBJECT (window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Notes
I don't use GtkScrolledWindow's internal scrollbars, because they will overlay the content. I don't want that. Therefore I'm making this implementation.
I could imagine this to be a bug within GTK. Even if it is, is there a way to work around it?
I'm running Gtk3.24 under XOrg, XUb 21.04, Theme: Greybird.
edit
It looks like this behavior is triggered depending on the theme.
I have an App which as some point Opens a new Window and works fine.
Then After I'm done with it, I need to switch back to the main window.
I understood that I need the function:
gtk_widget_hide();
but I cant figure out how to hide the main Window, to print only the second one and again after I click the button in the second window to go back to the first one.
This is what I have so far:
#include <stdio.h>
#include <gtk/gtk.h>
static void crete_new_wind (GtkWidget *widget);
gboolean destroy (GtkWidget *window);
int main (int argc, char *argv[]){
GtkWidget *window, *button, *box;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "First Window");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, 300, 300);
gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
button = gtk_button_new_with_label ("Go to Window B");
g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (crete_new_wind), (gpointer) window);
box = gtk_box_new (TRUE, 1);
gtk_box_pack_end (GTK_BOX (box), button, TRUE, TRUE, 1);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
void crete_new_wind (GtkWidget *widget){
GtkWidget *window, *button, *box;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Second Window");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, 300, 300);
gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);;
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), widget);
button = gtk_button_new_with_label ("Go back to Window A");
g_signal_connect (G_OBJECT (button), "destroy", G_CALLBACK (destroy), NULL);
gtk_widget_hide(widget);
box = gtk_box_new (TRUE, 1);
gtk_box_pack_end (GTK_BOX (box), button, TRUE, TRUE, 1);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_widget_show_all (window);
}
gboolean destroy (GtkWidget *widget){
gtk_widget_destroy (widget);
return TRUE;
}
If I click on the button(Go to Window B) I have this:
But the main window is still there, avaible to the user which is not what I need.
Thats because the clicked callback wants two parameters
Change the prototype and the function to
static void crete_new_wind(GtkButton *dummy, gpointer widget);
static void crete_new_wind(GtkButton *dummy, gpointer widget) {
in order to use the second parameter in your function.
I am trying to create a GUI in GTK using C. I want to have radio buttons but I don't know how to determine which one is selected.
#include <glib.h>
#include <gtk/gtk.h>
static gboolean close_application( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
gtk_main_quit ();
return FALSE;
}
static void btn_selected()
{
//pop-up message; left empty for now
}
int main( int argc,
char *argv[] )
{
GtkWidget *window = NULL;
GtkWidget *box1;
GtkWidget *box2;
GtkWidget *button;
GtkWidget *separator;
GSList *group;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "delete-event",
G_CALLBACK (close_application),
NULL);
gtk_window_set_title (GTK_WINDOW (window), "radio buttons");
gtk_container_set_border_width (GTK_CONTAINER (window), 0);
box1 = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (window), box1);
gtk_widget_show (box1);
box2 = gtk_vbox_new (FALSE, 10);
gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
gtk_widget_show (box2);
button = gtk_radio_button_new_with_label (NULL, "button1");
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_show (button);
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
button = gtk_radio_button_new_with_label (group, "button2");
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_show (button);
button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (button),
"button3");
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_show (button);
separator = gtk_hseparator_new ();
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
gtk_widget_show (separator);
box2 = gtk_vbox_new (FALSE, 10);
gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
gtk_widget_show (box2);
button = gtk_button_new_with_label ("close");
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (close_application),
window);
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_set_can_default (button, TRUE);
gtk_widget_grab_default (button);
gtk_widget_show (button);
//This button will call the btn_selected method when clicked
button = gtk_button_new_with_label("selected");
g_signal_connect(button,"clicked",G_CALLBACK (btn_selected),NULL);
gtk_box_pack_start(GTK_BOX(box2),button,TRUE,TRUE,0);
gtk_widget_show(button);
gtk_widget_show (window);
gtk_main ();
return 0;
}
The GUI comes up without any errors, I just don't know to check which radio button was selected so I can perform an action. Since all the buttons are named button I don't know how to check for a specific one.I got this code from a gtk tutorial online.
Any ideas would be greatly appreciated.
GtkRadioButton is descended from GtkToggleButton so you can connect to the 'toggled' signal or use the 'active' property to the the state of the button.
I want to set the position of the window using gtk_window_set_position
but it seems that after the window is created, the gtk_window_set_position will not take effect.
I'm wondering how to call gtk_window_set_position after the window shows up, i.e. a button event?
I just wrote a minimal working example that implements two approaches. One approach uses gtk_window_move and the other gtk_window_set_position.
If you have the gtk+ reference at hand the code should be self explanatory.
#include <gtk/gtk.h>
#include <glib.h>
void
button1_clicked_cb (GtkWidget * widget, GtkWindow * window)
{
GdkWindow *root;
gint width, height, rwidth, rheight;
gtk_window_get_size (window, &width, &height);
root = gtk_widget_get_root_window (GTK_WIDGET (window));
gdk_window_get_geometry (root, NULL, NULL, &rwidth,
&rheight);
gtk_window_move (window, (rwidth - width) / 2,
(rheight - height) / 2);
}
void
button2_clicked_cb (GtkWidget * widget, GtkWindow * window)
{
gtk_widget_hide (GTK_WIDGET (window));
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show_all (GTK_WIDGET (window));
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *button1;
GtkWidget *button2;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
button1 = gtk_button_new_with_label ("approach 1");
button2 = gtk_button_new_with_label ("approach 2");
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
gtk_box_pack_start (GTK_BOX (box), button1, TRUE, TRUE,
10);
gtk_box_pack_start (GTK_BOX (box), button2, TRUE, TRUE,
10);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_widget_show_all (window);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (button1, "clicked",
G_CALLBACK (button1_clicked_cb),
window);
g_signal_connect (button2, "clicked",
G_CALLBACK (button2_clicked_cb),
window);
gtk_main ();
return 0;
}
// gcc -o 0 $(pkg-config --cflags --libs gtk+-2.0) 1.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
struct tst
{
GtkWidget *win, *w2, *hb, *vb, *ent, *btn, *b2, *pbar;
GtkAccelGroup *acc;
};
GCancellable *can1;
GError *err1;
GThread *t1;
static void t1_stop (struct tst *prg)
{
g_cancellable_cancel (can1);
can1 = NULL;
}
gpointer t1_do (gpointer ptr1)
{
struct tst *prg = (gpointer)ptr1;
g_file_copy (g_file_new_for_path ("/1.avi"), g_file_new_for_path ("/2.avi"), G_FILE_COPY_NOFOLLOW_SYMLINKS, can1, NULL, NULL, &err1);
if (err1 != NULL) g_error_free (err1);
gtk_widget_destroy (prg->w2);
}
static void window_pbar (struct tst *prg)
{
prg->w2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
prg->hb = gtk_hbox_new (FALSE, 0);
prg->pbar = gtk_progress_bar_new ();
prg->b2 = gtk_button_new_with_label ("Cancel");
gtk_container_add (GTK_CONTAINER (prg->w2), GTK_WIDGET (prg->hb));
gtk_window_set_position (GTK_WINDOW (prg->w2), GTK_WIN_POS_CENTER);
gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->pbar), FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->b2), FALSE, FALSE, 0);
g_signal_connect_swapped (prg->w2, "delete_event", G_CALLBACK (t1_stop), prg);
g_signal_connect_swapped (prg->b2, "clicked", G_CALLBACK (t1_stop), prg);
gtk_widget_show_all (GTK_WIDGET (prg->w2));
can1 = g_cancellable_new ();
err1 = NULL;
t1 = g_thread_create (t1_do, (gpointer)prg, TRUE, NULL);
}
static void window_new ()
{
struct tst *prg = g_new0 (struct tst, 1);
prg->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
prg->vb = gtk_vbox_new (FALSE, 0);
prg->btn = gtk_button_new_with_label ("start");
gtk_container_add (GTK_CONTAINER (prg->win), GTK_WIDGET (prg->vb));
gtk_box_pack_start (GTK_BOX (prg->vb), GTK_WIDGET (prg->btn), FALSE, FALSE, 0);
g_signal_connect (prg->win, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect_swapped (prg->btn, "clicked", G_CALLBACK (window_pbar), prg);
gtk_window_set_title (GTK_WINDOW (prg->win), "Test program");
gtk_window_set_position (GTK_WINDOW (prg->win), GTK_WIN_POS_CENTER);
gtk_widget_show_all (GTK_WIDGET (prg->win));
gtk_main ();
}
int main (int argc, char *argv[])
{
gtk_init (&argc, &argv);
window_new ();
return 0;
}
This program is an example. When 'start' button is clicked the program creates a window with progress bar and 'cancel' button, and make a thread to copy /1.avi to /2.avi, but because of /1.avi doesn't exist the program will write 'Error!' on terminal and close the progress bar window.
But there is one problem in this program. While I click 'start' button many times somewhen the program writes another error messages on terminal. Sometimes the messages are about GDK, sometimes about GObject, and sometimes about GTK+. And sometimes program itself is freezed or crashed.
// gcc -o 0 $(pkg-config --cflags --libs gtk+-2.0) 1.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
struct tst
{
GtkWidget *win, *w2, *hb, *vb, *ent, *btn, *b2, *pbar;
GtkAccelGroup *acc;
};
GCancellable *can1;
GError *err1;
GThread *t1;
static void t1_stop (struct tst *prg)
{
g_cancellable_cancel (can1);
can1 = NULL;
}
gpointer t1_do (gpointer ptr1)
{
struct tst *prg = (gpointer)ptr1;
g_file_copy (g_file_new_for_path ("/1.avi"), g_file_new_for_path ("/2.avi"), G_FILE_COPY_NOFOLLOW_SYMLINKS, can1, NULL, NULL, &err1);
if (err1 != NULL) g_error_free (err1);
gtk_widget_destroy (prg->w2);
}
static void window_pbar (struct tst *prg)
{
prg->w2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
prg->hb = gtk_hbox_new (FALSE, 0);
prg->pbar = gtk_progress_bar_new ();
prg->b2 = gtk_button_new_with_label ("Cancel");
gtk_container_add (GTK_CONTAINER (prg->w2), GTK_WIDGET (prg->hb));
gtk_window_set_position (GTK_WINDOW (prg->w2), GTK_WIN_POS_CENTER);
gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->pbar), FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->b2), FALSE, FALSE, 0);
g_signal_connect_swapped (prg->w2, "delete_event", G_CALLBACK (t1_stop), prg);
g_signal_connect_swapped (prg->b2, "clicked", G_CALLBACK (t1_stop), prg);
gtk_widget_show_all (GTK_WIDGET (prg->w2));
can1 = g_cancellable_new ();
err1 = NULL;
t1 = g_thread_create (t1_do, (gpointer)prg, TRUE, NULL);
g_thread_join (t1);
}
static void window_new ()
{
struct tst *prg = g_new0 (struct tst, 1);
prg->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
prg->vb = gtk_vbox_new (FALSE, 0);
prg->btn = gtk_button_new_with_label ("start");
gtk_container_add (GTK_CONTAINER (prg->win), GTK_WIDGET (prg->vb));
gtk_box_pack_start (GTK_BOX (prg->vb), GTK_WIDGET (prg->btn), FALSE, FALSE, 0);
g_signal_connect (prg->win, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect_swapped (prg->btn, "clicked", G_CALLBACK (window_pbar), prg);
gtk_window_set_title (GTK_WINDOW (prg->win), "Test program");
gtk_window_set_position (GTK_WINDOW (prg->win), GTK_WIN_POS_CENTER);
gtk_widget_show_all (GTK_WIDGET (prg->win));
gtk_main ();
}
int main (int argc, char *argv[])
{
gtk_init (&argc, &argv);
window_new ();
return 0;
}
So I modified some parts of program. Now the problem not occurs though I click 'start' button many times, but I met another problem. When I can copy /1.avi to /2.avi, if I click 'start' button then program copies /1.avi to /2.avi but during this process the program window freezes, and progress bar window doesn't appear(On first example this problem isn't occured).
What should I do to make this program to meet neither of two problems?
The immediate problem I can see with your code is that you are calling a Gtk function in t1_do which is run from the secondary thread without enclosing it in gtk_thread_enter (); gtk_thread_leave ();
The general rule is that you should never call a UI function from a thread that isn't the main UI thread. Until you fix that problem you could just get completely random errors and behaviour