Re: GUI signals from a thread (GTK+-3.10)
- From: Valentin But <valentin but eesti ee>
- To: Andrew Potter <agpotter gmail com>, gtk-app-devel-list gnome org
- Subject: Re: GUI signals from a thread (GTK+-3.10)
- Date: Sun, 16 Feb 2014 06:25:07 +0200
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]