#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;
}
单击按钮时,GUI变得无法响应。该程序应该打印&#39; Hurray&#39;并且它应该接受对另一个(closeapp)按钮的响应。我尝试使用gdk_thread_enter()和gdk_thread_leave(),但它发出警告说它们已被弃用。 请帮忙!
答案 0 :(得分:4)
display
函数包含一个阻塞无限循环,它阻止GTK主循环运行,从而处理事件或重绘窗口内容。 GTK不会将事件处理和UI绘图运行到单独的线程中,因此无法阻止代码中的主循环。
您使用GTask API使用线程;或者您将操作分解为离散块,并通过API g_timeout_add
或g_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有点复杂,因为当空闲和超时处理程序在与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);
}