问题
#include <gtk/gtk.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
void display(GtkButton * b, gpointer data)
{
while(1)
{
printf("hurrah!");
}
}
void closeapp(GtkButton * b, gpointer data)
{
gtk_main_quit();
}
int main(int argc,char *argv[])
{
Widgets widg;
printf("hello");
GtkBuilder *gtkBuilder;
GtkWidget *window;
gtk_init(&argc,&argv);
gtkBuilder = gtk_builder_new();
gtk_builder_add_from_file(gtkBuilder,"hello_world.glade",NULL);
window = GTK_WIDGET(gtk_builder_get_object(gtkBuilder,"mywindow"));
widg.e1 = GTK_ENTRY( gtk_builder_get_object(gtkBuilder, "entry1" ) );
gtk_builder_connect_signals(gtkBuilder, &widg);
g_object_unref(G_OBJECT(gtkBuilder));
gtk_widget_show(window);
gtk_main();
return 0;
}
The GUI becomes unresponsive when I click the button. The program should print 'Hurrah' and also it should accept response for the other(closeapp) button.I tried using gdk_thread_enter() and gdk_thread_leave(), but it gave warning that they are deprecated. Please Help!
回答1:
The display
function contains a blocking infinite loop, which prevents the GTK main loop from running, and thus processing events or redrawing the contents of your window. GTK does not run the event handling and UI drawing into a separate thread, so you cannot block the main loop in your code.
You either use a thread, using the GTask API; or you break down your operation into discrete blocks, and use the main loop as well, through API like g_timeout_add or g_idle_add.
Let's show an example of the latter, since it's easier to grasp. If we have an operation that can be broken down into multiple iterations, we can use a simple idle handler, like this:
static GtkWidget *label;
static int counter;
static gboolean
on_idle_count_to_five (gpointer data)
{
/* Increment the global counter */
counter += 1;
char *str = g_strdup_printf ("We are at %d!", counter);
/* Show the current count */
gtk_label_set_text (GTK_LABEL (counter_label), str);
g_free (str);
/* We got past five, so we remove the idle source */
if (counter == 5)
return G_SOURCE_REMOVE;
/* Otherwise we continue */
return G_SOURCE_CONTINUE;
}
The idle handler increments a counter and updates a label each time; if the counter reaches the target value, we remove the handler. In order to set up the handler, we can use:
...
counter_label = gtk_label_new ();
gtk_container_add (GTK_CONTAINER (parent), counter_label);
gtk_widget_show (counter_label);
/* Install the idle handler in the main loop */
g_idle_add (on_idle_count_to_five, NULL);
Using GTask is a bit more complex because while the idle and timeout handlers run inside the same main context as the rest of the UI, when using a separate thread we need to switch to the correct main context in order to update the UI.
For instance, this is a long running operation that periodically updates the UI using a thread wrapped by GTask
:
/* Ancillary data structure that we can use to keep state */
struct TaskData {
GtkWidget *counter_label;
int count;
}
/* Wrapper function for updating the label; this has to be
* called in the main context that initialized GTK and is
* spinning the main loop
*/
static void
update_counter_label (gpointer data_)
{
struct TaskData *data = data_;
char *str = g_strdup_printf ("We are at %d!", data->count);
gtk_label_set_text (GTK_LABEL (data->counter_label), str);
g_free (str);
}
static void
count_to_five (GTask *task,
gpointer source_obj,
gpointer task_data,
GCancellable *cancellable)
{
struct TaskData *data = task_data;
/* Count to five with some sleep in the middle to make this a long
* running operation
*/
for (int i = 0; i < 5; i++)
{
/* Break if GCancellable.cancel() was called */
if (g_cancellable_is_cancelled (cancellable))
{
g_task_return_new_error (task,
G_IO_ERROR, G_IO_ERROR_CANCELLED,
"Task cancelled");
return;
}
/* Invoke update_counter_label in the default main context,
* which is the one use by GTK
*/
g_main_context_invoke (NULL, update_counter_label, data);
g_usleep (500);
}
/* The task has finished */
g_task_return_boolean (task, TRUE);
}
static void
count_to_five_async (GtkWidget *label,
GCancellable *cancellable,
GAsyncReadyCallback count_to_five_done,
gpointer data)
{
/* Allocate and initialize the task data */
struct TaskData *data = g_new (TaskData, 1);
data->counter_label = label;
data->count = 0;
/* Runs the count_to_five() function in a thread, and calls
* count_to_five_done() when the thread terminates
*/
GTask *task = g_task_new (label, cancellable, count_to_five_done, data);
g_task_set_task_data (task, data, g_free);
g_task_run_in_thread (task, count_to_five);
g_object_unref (task);
}
来源:https://stackoverflow.com/questions/36971139/gui-becomes-unresponsive-after-clicking-the-button-using-gtk-in-c