在gtk中使用进度条

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;
}
updatedupdated2022-02-222022-02-22