Re: GUI signals from a thread (GTK+-3.10)



2014-02-15 17:02 GMT+02:00 David Marceau <uticdmarceau2007 yahoo ca>:
Here is an example that shows two threads which take a long time to
complete, but gracefully coordinate progress data to the gui.
http://www.simsoup.info/SimSoup/GUI_With_Worker_Threads.html
That didn't helped :-((((((99

2014-02-15 19:43 GMT+02:00 Andrew Potter <agpotter gmail com>:
Problem 1) Passing stack variables from a worker thread is dangerous.
Problem 2) Reading and writing variables across threads is dangerous.
I believe this 'example' would be very usefull for many and it has to
be finished.

I've modified the code around and now I have a funny construction:
    callobj->thread_data = thread_data;
    thread_data->callobj = callobj;
I'm not sure if this needs fix or not, and how to fix it approximately.

Also it appears to be that I have to use GOnce for a single event in thread?

And I started to wonder how the thread might be paused.

You can also annotate messagebox() with the
G_GNUC_PRINTF(2, 3) macro to enable compile-time format checking for
calls to that function.
Awesome!!1

// #############################################################################
// GThread with GTK+-3.10
// v2

#include <stdlib.h>
#include <gtk/gtk.h>

// #############################################################################

struct THREAD_DATA
{
    struct CallbackObject *callobj;

    GThread *pthread;
    GCancellable *cancel;

    GRWLock lock;
    guint event_source;
    GtkWidget *progress;
    gdouble x;
};

struct CallbackObject
{
    GtkWidget *win;
    GtkWidget *label;
    GtkWidget *button;
    GtkWidget *progress;

    struct THREAD_DATA *thread_data;
};

// #############################################################################

void messagebox (struct CallbackObject *callobj, gchar *format, ...)
G_GNUC_PRINTF (2, 3);

gboolean thread_exit (struct THREAD_DATA *data);
gboolean thread_progress (struct THREAD_DATA *data);
gboolean thread_cb (struct THREAD_DATA *data);
gpointer thread_func (struct THREAD_DATA *data);

void startstop_thread (GtkWidget *wid, struct CallbackObject *callobj);

// #############################################################################

const gchar *label_start = "Start thread";
const gchar *label_stop = "Stop thread";

// #############################################################################

void messagebox (struct CallbackObject *callobj, gchar *format, ...)
{
    GtkWidget *dialog = NULL;

    // Write a formatted string into a GString.
    va_list arg;
    va_start (arg, format);
    GString *msg = g_string_new (NULL);
    g_string_vprintf (msg, format, arg);

    // Create the message dialog
    dialog = gtk_message_dialog_new (GTK_WINDOW(callobj->win),
        GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
        msg->str);
    gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
    gtk_dialog_run (GTK_DIALOG(dialog));
    gtk_widget_destroy (dialog);

    g_string_free (msg, TRUE);
    va_end (arg);
}

// #############################################################################

gboolean thread_exit (struct THREAD_DATA *data)
{
  struct CallbackObject *callobj = data->callobj;

  /* Wait thread to finish */
  g_thread_join (data->pthread);
  g_thread_unref (data->pthread);
  data->pthread = NULL;

  /* Stop progress update and update once more to get "100%" */
  g_source_remove (data->event_source);
  thread_progress (data);

  /* Restore GUI state */
  gtk_label_set_label (GTK_LABEL(data->callobj->label), "");
  gtk_button_set_label (GTK_BUTTON (data->callobj->button), label_start);

  if (g_cancellable_is_cancelled (data->cancel) == TRUE)
  {
      messagebox (data->callobj, "Thread cancelled!");
  }
  else
  {
      messagebox (data->callobj, "Thread successfully completed!");
  }

  /* Cleanup */
  g_object_unref (data->cancel);
  data->cancel = NULL;
  g_rw_lock_clear (&(data->lock));
  g_free (data);
  callobj->thread_data = NULL;

  return G_SOURCE_REMOVE;
}

gboolean thread_progress (struct THREAD_DATA *data)
{
    g_rw_lock_reader_lock (&(data->lock));
    gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->progress), data->x);
    g_rw_lock_reader_unlock (&(data->lock));

    return G_SOURCE_CONTINUE;
}

gboolean thread_cb (struct THREAD_DATA *data)
{
    gtk_label_set_label (GTK_LABEL(data->callobj->label), "Reached
level 50% !");

    return G_SOURCE_REMOVE;
}

gpointer thread_func (struct THREAD_DATA *data)
{
  guint event_source_cb = 0;

  while (data->x < 1.0 &&
    g_cancellable_is_cancelled (data->cancel) == FALSE)
  {
    g_rw_lock_writer_lock (&(data->lock));
    data->x += 1.0 / 100;
    g_rw_lock_writer_unlock (&(data->lock));

    /* Process code */
    usleep (10*1000);

    if (data->x >= 0.5 && event_source_cb == 0)
    {
        event_source_cb = g_idle_add ((GSourceFunc) thread_cb, data);
    }
  }

  g_idle_add ((GSourceFunc) thread_exit, data);

  return NULL;
}

// #############################################################################

void startstop_thread (GtkWidget *wid, struct CallbackObject *callobj)
{
  if (callobj->thread_data == NULL)
  {
    /* Prepare the structure and start the progress bar update */
    struct THREAD_DATA *thread_data = g_new0 (struct THREAD_DATA, 1);
    callobj->thread_data = thread_data;
    thread_data->callobj = callobj;
    thread_data->cancel = g_cancellable_new ();
    g_rw_lock_init (&(thread_data->lock));
    thread_data->progress = callobj->progress,
    thread_data->x = 0.0;
    thread_data->event_source = g_timeout_add (20 /* ms */,
(GSourceFunc) thread_progress, thread_data);

    /* Thread starts here */
    thread_data->pthread = g_thread_new ("thread", (GThreadFunc)
thread_func, thread_data);

    /* Change GUI state */
    gtk_button_set_label (GTK_BUTTON (callobj->button), label_stop);
  }
  else
  {
    g_cancellable_cancel (callobj->thread_data->cancel);
  }
}

// #############################################################################

int main (int argc, char *argv[])
{
  GtkWidget *button = NULL;
  GtkWidget *vbox = NULL;
  struct CallbackObject *callobj = NULL;

  /* Initialize GTK+ */
  g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, (GLogFunc) gtk_false, NULL);
  gtk_init (&argc, &argv);
  g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, g_log_default_handler, NULL);

  callobj = g_malloc0 (sizeof(struct CallbackObject));

  /* Create the main window */
  callobj->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_container_set_border_width (GTK_CONTAINER (callobj->win), 8);
  gtk_window_set_title (GTK_WINDOW (callobj->win), "GTK+-3.10.7-1 Threads");
  gtk_window_set_position (GTK_WINDOW (callobj->win), GTK_WIN_POS_CENTER);
  gtk_widget_realize (callobj->win);
  g_signal_connect (callobj->win, "destroy", gtk_main_quit, NULL);

  /* Create a vertical box with buttons */
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
  gtk_container_add (GTK_CONTAINER (callobj->win), vbox);

  callobj->label = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (vbox), callobj->label, TRUE, TRUE, 0);

  callobj->progress = gtk_progress_bar_new ();
  gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR (callobj->progress), TRUE);
  gtk_box_pack_start (GTK_BOX (vbox), callobj->progress, TRUE, TRUE, 0);

  callobj->button = gtk_button_new_with_label ("Start thread");
  g_signal_connect (G_OBJECT (callobj->button), "clicked", G_CALLBACK
(startstop_thread), (gpointer) callobj);
  gtk_box_pack_start (GTK_BOX (vbox), callobj->button, TRUE, TRUE, 0);

  button = gtk_button_new_with_label ("Close");
  g_signal_connect (button, "clicked", gtk_main_quit, NULL);
  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);

  /* Enter the main loop */
  gtk_widget_show_all (callobj->win);
  gtk_main ();

  g_free (callobj);

  return 0;
}

// #############################################################################


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]