使用C中的GTK +单击按钮后,GUI变得无响应

#include  #include  #include  #include  #include  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; } 

单击按钮时,GUI变得无法响应。 该程序应该打印’Hurray’并且它应该接受对另一个(closeapp)按钮的响应。我尝试使用gdk_thread_enter()和gdk_thread_leave(),但它发出警告说它们已被弃用。 请帮忙!

display函数包含一个阻塞无限循环,它阻止GTK主循环运行,从而处理事件或重绘窗口内容。 GTK不会将事件处理和UI绘图运行到单独的线程中,因此您无法阻止代码中的主循环。

你要么使用一个线程,使用GTask API; 或者您将操作分解为离散块,并通过API(如g_timeout_addg_idle_add使用主循环。

让我们展示一个后者的例子,因为它更容易掌握。 如果我们有一个可以分解为多次迭代的操作,我们可以使用一个简单的空闲处理程序,如下所示:

 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; } 

空闲处理程序递增计数器并每次更新标签; 如果计数器达到目标值,我们删除处理程序。 为了设置处理程序,我们可以使用:

 ... 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); 

使用GTask有点复杂,因为虽然idle和timeout处理程序在与UI的其余部分相同的主上下文中运行,但是当使用单独的线程时,我们需要切换到正确的主上下文以便更新UI。

例如,这是一个长时间运行的操作,它使用由GTask包装的线程定期更新UI:

 /* 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); }