gui编程,在后台做一件事,每到一个阶段就把进度条增加一点。
大概一年前,我就有开始研究这个问题,当时刚接触gui编程,不得其法,按照着当时写的思路写,结果是处理“后台”的时候整个gui都冻结了。
自然而然的,使用线程,开始学习和使用pthread,用在gui上,似乎有那么一点效果,然而似乎是偶尔才像我想象中那样刷新进度条;在循环中加入gdk_fflush()刷新整个gui,功能是对了,这效率无法接受。
最后,参照gtk的例子,使用超时函数,凑合着用吧。
过了一年多,昨天晚上,应该是今天凌晨0:00以后开始开始研究gtk线程,在抓狂了3个多小时以后,终于有了点头绪,然后倒下睡了。今天稍微弄了一下,大概达到我当初的要求了。
最关键的是:在gtk_init()以前加入g_thread_init()。
最简单的方法就是只加入这一句,其余不变,这已经能达到要求了。
然而有一个问题,gtk不是线程安全的,解决办法就是加锁。我依然不太明白其中细节,然后看起来似乎是这样的:gdk_threads_enter ()/gdk_threads_leave ()分别对应加锁和解锁,不过在使用这两个函数之前需要在gtk_init()以前,g_thread_init()以后加入gdk_threads_init ()函数。然而在加入gdk_threads_init ()以后,必须把gtk_main()包含在gdk_threads_enter ()/gdk_threads_leave ()这一对函数中,否则整个gui都被阻塞了,可以编写一个gtk程序看看什么效果。
接下来要做的是,在响应信号的回调函数中使用g_thread_create()新建一个线程,在线程中,把需要对gui widget操作的函数包裹在gdk_threads_enter ()/gdk_threads_leave ()中。
一个进度条的例子:
#include <gtk.h> gboolean stat; GtkWidget *label; GtkWidget *progressbar; void t1(gpointer data) { int i; char msg[10]; msg[9] = ''; stat = TRUE; for (i = 1; i <= 100; i++) { if (!stat) { break; } g_message ("log%d", i); sprintf (msg, "log%d", i); gdk_threads_enter (); gtk_label_set_text (GTK_LABEL (label), msg); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progressbar), (i / 100.0)); gdk_threads_leave (); g_usleep (100000); } } void start(gpointer widget, gpointer data) { g_thread_create (t1, NULL, FALSE, NULL); } void stop (gpointer widget, gpointer data) { stat = FALSE; } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *vbox; GtkWidget *button1; GtkWidget *button2; g_thread_init (NULL); gdk_threads_init (); gdk_threads_enter (); gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); vbox = gtk_vbox_new (TRUE, 2); gtk_container_add (GTK_CONTAINER (window), vbox); button1 = gtk_button_new_with_label ("starrrrrrrrrrrrrrrrrrt"); gtk_box_pack_start_defaults (GTK_BOX (vbox), button1); button2 = gtk_button_new_with_label ("stop"); gtk_box_pack_start_defaults (GTK_BOX (vbox), button2); label = gtk_label_new("abc"); //gtk_label_set_text (GTK_LABEL (label), "nop"); gtk_box_pack_start_defaults (GTK_BOX (vbox), label); progressbar = gtk_progress_bar_new (); gtk_box_pack_start_defaults (GTK_BOX (vbox), progressbar); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL); g_signal_connect (GTK_BUTTON (button1), "clicked", G_CALLBACK (start), NULL); g_signal_connect (GTK_BUTTON (button2), "clicked", G_CALLBACK (stop), NULL); gtk_widget_show_all (window); gtk_main (); gdk_threads_leave (); return 0; }