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

时间:2016-05-01 19:44:37

标签: c multithreading gtk glade

#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(),但它发出警告说它们已被弃用。 请帮忙!

1 个答案:

答案 0 :(得分:4)

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有点复杂,因为当空闲和超时处理程序在与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);
}