用gtk控制多线程进度条
在我的C程序中,从互联网下载一些文件,我使用GTK进度条来显示下载进度。
我想如果我下载一个文件,我的应用程序显示一个进度条
如果我下载三个文件,我的应用程序可以显示三个进度条。其余的也可以用同样的方法完成。
我用glade3创建UI。 Gtk树视图 有 3 列
- 姓名
- 进展
- 状态
,我写了一些代码,它可以工作,但
如果我下载一个文件,就会出现一些问题,应用程序龙宁还不错。
但如果我下载两个文件。 应用程序无法显示两个进度条。
应用程序只在一个进度中显示两个线程
我应该如何解决这个问题?
和源代码:
/*
gcc -Wall -g `pkg-config --cflags --libs gtk+-2.0 gmodule-export-2.0` -lcurl -lgthread-2.0 liststore.c -o liststore
*/
#include <stdio.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <unistd.h>
#include <pthread.h>
#include <curl/curl.h>
#include <curl/types.h> /* new for v7 */
#include <curl/easy.h> /* new for v7 */
gchar *URL = "http://soundclash-records.co.uk/mp3s/upfull_rockers_never_gonna_let_you_down.mp3";
size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fwrite(ptr, size, nmemb, stream);
}
size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fread(ptr, size, nmemb, stream);
}
typedef struct _Data Data;
struct _Data
{
GtkWidget *down; /* Down button */
GtkWidget *tree; /* Tree view */
gdouble progress;
};
enum
{
STRING_COLUMN,
INT_COLUMN,
N_COLUMNS
};
gboolean set_download_progress(gpointer data)
{
Data *treeview = (Data *)data;
GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview->tree)));
GtkTreeIter iter;
gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store) ,
&iter,
g_strdup_printf ("%d",0));
gtk_list_store_set(store, &iter,
INT_COLUMN,treeview->progress, -1);
return FALSE;
}
int my_progress_func(Data *data,
double t, /* dltotal */
double d, /* dlnow */
double ultotal,
double ulnow)
{
// printf("%d / %d (%g %%)\n", d, t, d*100.0/t);
gdk_threads_enter();
gdouble progress;
progress = d*100.0/t;
data->progress = progress;
g_idle_add(set_download_progress, data);
gdk_threads_leave();
return 0;
}
void *create_thread(void *data)
{
Data *viewtree = (Data *)data;
GtkTreeIter iter;
GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(viewtree->tree)));
gtk_list_store_append( store, &iter );
g_print("url\n");
CURL *curl;
CURLcode res;
FILE *outfile;
gchar *url = URL;
curl = curl_easy_init();
if(curl)
{
outfile = fopen("test.curl", "w");
if(outfile)
g_print("curl\n");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, data);
res = curl_easy_perform(curl);
fclose(outfile);
/* always cleanup */
curl_easy_cleanup(curl);
}
g_object_unref( G_OBJECT( store ) );
return NULL;
}
G_MODULE_EXPORT void
cb_add( GtkWidget *button,
Data *data )
{
if (!g_thread_create(&create_thread, data, FALSE, NULL) != 0)
g_warning("can't create the thread");
}
int main(int argc, char **argv)
{
GtkBuilder *builder;
GtkWidget *window;
Data *data;
curl_global_init(CURL_GLOBAL_ALL);
if( ! g_thread_supported() )
g_thread_init( NULL );
gdk_threads_init();
gtk_init(&argc, &argv);
data = g_slice_new( Data );
/* Create builder */
builder = gtk_builder_new();
gtk_builder_add_from_file( builder, "progress.glade", NULL );
window = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
data->down = GTK_WIDGET( gtk_builder_get_object( builder, "down" ) );
data->tree = GTK_WIDGET( gtk_builder_get_object( builder, "treeview" ) );
gtk_builder_connect_signals( builder, data );
g_object_unref( G_OBJECT( builder ) );
gtk_widget_show( window );
gtk_main();
g_slice_free( Data, data );
return 0;
}
== =================================================== =====
更新:2009 年 12 月 11 日
/*
gcc -Wall -g `pkg-config --cflags --libs gtk+-2.0 gmodule-export-2.0 gthread-2.0 libcurl` liststore2.c -o liststore2
*/
#include <stdio.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <unistd.h>
#include <pthread.h>
#include <curl/curl.h>
#include <curl/types.h> /* new for v7 */
#include <curl/easy.h> /* new for v7 */
gchar *URL = "http://soundclash-records.co.uk/mp3s/upfull_rockers_never_gonna_let_you_down.mp3";
static GHashTable* TreeRowReferences;
static GPrivate* current_data_key = NULL;
size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fwrite(ptr, size, nmemb, stream);
}
size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fread(ptr, size, nmemb, stream);
}
typedef struct _Data Data;
struct _Data
{
GtkWidget *down; /* Down button */
GtkWidget *tree; /* Tree view */
gdouble progress;
};
enum
{
STRING_COLUMN,
INT_COLUMN,
N_COLUMNS
};
gboolean set_download_progress(gpointer data)
{
Data *treeview = (Data *)data;
GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview->tree)));
GtkTreeIter iter;
GtkTreeRowReference* reference = g_hash_table_lookup(TreeRowReferences,data);
GtkTreePath* path = gtk_tree_row_reference_get_path(reference);
gtk_tree_model_get_iter(GTK_TREE_MODEL(store),
&iter, path);
gtk_list_store_set(store, &iter,
INT_COLUMN,treeview->progress, -1);
gtk_tree_path_free (path);
return FALSE;
}
int my_progress_func(Data *data,
double t, /* dltotal */
double d, /* dlnow */
double ultotal,
double ulnow)
{
if(t == 0)
return 0;
data->progress = d*100.0/t;
gdk_threads_enter();
g_idle_add(set_download_progress, data);
gdk_threads_leave();
return 0;
}
void *create_thread(void *data)
{
Data *current_treeview = g_private_get (current_data_key);
if (!current_treeview)
{
current_treeview = g_new (Data, 1);
current_treeview = (Data *)data;
g_private_set (current_data_key, current_treeview);
g_print("p %g\n",current_treeview->progress);
}
else{
current_treeview = (Data *)data;
g_print("c %g\n",current_treeview->progress);
}
g_print("url\n");
CURL *curl;
CURLcode res;
FILE *outfile;
gchar *url = URL;
gdk_threads_enter();
curl = curl_easy_init();
if(curl)
{
outfile = fopen("test.curl", "w");
if(outfile)
g_print("curl\n");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, current_treeview);
gdk_threads_leave();
res = curl_easy_perform(curl);
fclose(outfile);
/* always cleanup */
curl_easy_cleanup(curl);
}
return NULL;
}
G_MODULE_EXPORT void
cb_add( GtkWidget *button,
Data *data )
{
Data *current_download = (Data *)data;
GtkTreeIter iter;
GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(current_download->tree)));
gtk_list_store_append( store, &iter );
GtkTreeRowReference* reference = NULL;
GtkTreePath* path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
reference = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), path);
g_hash_table_insert(TreeRowReferences, current_download, reference);
gtk_tree_path_free(path);
if (!g_thread_create(&create_thread, current_download, FALSE, NULL) != 0)
g_warning("can't create the thread");
}
int main(int argc, char **argv)
{
GtkBuilder *builder;
GtkWidget *window;
Data *data;
curl_global_init(CURL_GLOBAL_ALL);
if( ! g_thread_supported() )
g_thread_init( NULL );
gdk_threads_init();
gtk_init(&argc, &argv);
data = g_slice_new( Data );
/* Create builder */
builder = gtk_builder_new();
gtk_builder_add_from_file( builder, "progress.glade", NULL );
window = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
data->down = GTK_WIDGET( gtk_builder_get_object( builder, "down" ) );
data->tree = GTK_WIDGET( gtk_builder_get_object( builder, "treeview" ) );
TreeRowReferences = g_hash_table_new(NULL, NULL);
gtk_builder_connect_signals( builder, data );
g_object_unref( G_OBJECT( builder ) );
gdk_threads_enter();
gtk_widget_show( window );
gdk_threads_leave();
gtk_main();
g_slice_free( Data, data );
return 0;
}
in my C program , download some files from the internet, im using a GTK progress bar to show the download progress.
i wanna if i download one file ,my app show one progress bar
if i download three files ,my app can show three progress bar. the rest can be done in the same manner.
i create UI with glade3. GtkTreeView
have 3 columns
- Name
- Progress
- Status
and i wrote some code , its can work but have some problem
if i download one file,app lonking not bad.
but if i download two files.
app could not show twoprogress bar.
app just show two thread in one progress
How should I do solve it ?
and source code :
/*
gcc -Wall -g `pkg-config --cflags --libs gtk+-2.0 gmodule-export-2.0` -lcurl -lgthread-2.0 liststore.c -o liststore
*/
#include <stdio.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <unistd.h>
#include <pthread.h>
#include <curl/curl.h>
#include <curl/types.h> /* new for v7 */
#include <curl/easy.h> /* new for v7 */
gchar *URL = "http://soundclash-records.co.uk/mp3s/upfull_rockers_never_gonna_let_you_down.mp3";
size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fwrite(ptr, size, nmemb, stream);
}
size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fread(ptr, size, nmemb, stream);
}
typedef struct _Data Data;
struct _Data
{
GtkWidget *down; /* Down button */
GtkWidget *tree; /* Tree view */
gdouble progress;
};
enum
{
STRING_COLUMN,
INT_COLUMN,
N_COLUMNS
};
gboolean set_download_progress(gpointer data)
{
Data *treeview = (Data *)data;
GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview->tree)));
GtkTreeIter iter;
gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store) ,
&iter,
g_strdup_printf ("%d",0));
gtk_list_store_set(store, &iter,
INT_COLUMN,treeview->progress, -1);
return FALSE;
}
int my_progress_func(Data *data,
double t, /* dltotal */
double d, /* dlnow */
double ultotal,
double ulnow)
{
// printf("%d / %d (%g %%)\n", d, t, d*100.0/t);
gdk_threads_enter();
gdouble progress;
progress = d*100.0/t;
data->progress = progress;
g_idle_add(set_download_progress, data);
gdk_threads_leave();
return 0;
}
void *create_thread(void *data)
{
Data *viewtree = (Data *)data;
GtkTreeIter iter;
GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(viewtree->tree)));
gtk_list_store_append( store, &iter );
g_print("url\n");
CURL *curl;
CURLcode res;
FILE *outfile;
gchar *url = URL;
curl = curl_easy_init();
if(curl)
{
outfile = fopen("test.curl", "w");
if(outfile)
g_print("curl\n");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, data);
res = curl_easy_perform(curl);
fclose(outfile);
/* always cleanup */
curl_easy_cleanup(curl);
}
g_object_unref( G_OBJECT( store ) );
return NULL;
}
G_MODULE_EXPORT void
cb_add( GtkWidget *button,
Data *data )
{
if (!g_thread_create(&create_thread, data, FALSE, NULL) != 0)
g_warning("can't create the thread");
}
int main(int argc, char **argv)
{
GtkBuilder *builder;
GtkWidget *window;
Data *data;
curl_global_init(CURL_GLOBAL_ALL);
if( ! g_thread_supported() )
g_thread_init( NULL );
gdk_threads_init();
gtk_init(&argc, &argv);
data = g_slice_new( Data );
/* Create builder */
builder = gtk_builder_new();
gtk_builder_add_from_file( builder, "progress.glade", NULL );
window = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
data->down = GTK_WIDGET( gtk_builder_get_object( builder, "down" ) );
data->tree = GTK_WIDGET( gtk_builder_get_object( builder, "treeview" ) );
gtk_builder_connect_signals( builder, data );
g_object_unref( G_OBJECT( builder ) );
gtk_widget_show( window );
gtk_main();
g_slice_free( Data, data );
return 0;
}
=========================================================
Update : 12-11-09
/*
gcc -Wall -g `pkg-config --cflags --libs gtk+-2.0 gmodule-export-2.0 gthread-2.0 libcurl` liststore2.c -o liststore2
*/
#include <stdio.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <unistd.h>
#include <pthread.h>
#include <curl/curl.h>
#include <curl/types.h> /* new for v7 */
#include <curl/easy.h> /* new for v7 */
gchar *URL = "http://soundclash-records.co.uk/mp3s/upfull_rockers_never_gonna_let_you_down.mp3";
static GHashTable* TreeRowReferences;
static GPrivate* current_data_key = NULL;
size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fwrite(ptr, size, nmemb, stream);
}
size_t my_read_func(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
return fread(ptr, size, nmemb, stream);
}
typedef struct _Data Data;
struct _Data
{
GtkWidget *down; /* Down button */
GtkWidget *tree; /* Tree view */
gdouble progress;
};
enum
{
STRING_COLUMN,
INT_COLUMN,
N_COLUMNS
};
gboolean set_download_progress(gpointer data)
{
Data *treeview = (Data *)data;
GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview->tree)));
GtkTreeIter iter;
GtkTreeRowReference* reference = g_hash_table_lookup(TreeRowReferences,data);
GtkTreePath* path = gtk_tree_row_reference_get_path(reference);
gtk_tree_model_get_iter(GTK_TREE_MODEL(store),
&iter, path);
gtk_list_store_set(store, &iter,
INT_COLUMN,treeview->progress, -1);
gtk_tree_path_free (path);
return FALSE;
}
int my_progress_func(Data *data,
double t, /* dltotal */
double d, /* dlnow */
double ultotal,
double ulnow)
{
if(t == 0)
return 0;
data->progress = d*100.0/t;
gdk_threads_enter();
g_idle_add(set_download_progress, data);
gdk_threads_leave();
return 0;
}
void *create_thread(void *data)
{
Data *current_treeview = g_private_get (current_data_key);
if (!current_treeview)
{
current_treeview = g_new (Data, 1);
current_treeview = (Data *)data;
g_private_set (current_data_key, current_treeview);
g_print("p %g\n",current_treeview->progress);
}
else{
current_treeview = (Data *)data;
g_print("c %g\n",current_treeview->progress);
}
g_print("url\n");
CURL *curl;
CURLcode res;
FILE *outfile;
gchar *url = URL;
gdk_threads_enter();
curl = curl_easy_init();
if(curl)
{
outfile = fopen("test.curl", "w");
if(outfile)
g_print("curl\n");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_func);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, current_treeview);
gdk_threads_leave();
res = curl_easy_perform(curl);
fclose(outfile);
/* always cleanup */
curl_easy_cleanup(curl);
}
return NULL;
}
G_MODULE_EXPORT void
cb_add( GtkWidget *button,
Data *data )
{
Data *current_download = (Data *)data;
GtkTreeIter iter;
GtkListStore* store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(current_download->tree)));
gtk_list_store_append( store, &iter );
GtkTreeRowReference* reference = NULL;
GtkTreePath* path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
reference = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), path);
g_hash_table_insert(TreeRowReferences, current_download, reference);
gtk_tree_path_free(path);
if (!g_thread_create(&create_thread, current_download, FALSE, NULL) != 0)
g_warning("can't create the thread");
}
int main(int argc, char **argv)
{
GtkBuilder *builder;
GtkWidget *window;
Data *data;
curl_global_init(CURL_GLOBAL_ALL);
if( ! g_thread_supported() )
g_thread_init( NULL );
gdk_threads_init();
gtk_init(&argc, &argv);
data = g_slice_new( Data );
/* Create builder */
builder = gtk_builder_new();
gtk_builder_add_from_file( builder, "progress.glade", NULL );
window = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
data->down = GTK_WIDGET( gtk_builder_get_object( builder, "down" ) );
data->tree = GTK_WIDGET( gtk_builder_get_object( builder, "treeview" ) );
TreeRowReferences = g_hash_table_new(NULL, NULL);
gtk_builder_connect_signals( builder, data );
g_object_unref( G_OBJECT( builder ) );
gdk_threads_enter();
gtk_widget_show( window );
gdk_threads_leave();
gtk_main();
g_slice_free( Data, data );
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
以下是我让代码正常工作的技巧:
如果有库的 pkg-config 文件(例如 gthread 或 libcurl),则使用该文件而不是混合 pkg-config 调用和
-l
开关。这不是问题,但以后可能会给您带来问题。因此,像这样编译你的文件:如果使用线程,总是,总是,总是,将对
gtk_main()
的调用包装在gdk_threads_enter()< 之间/code> 和
gdk_threads_leave()
。这也不是问题,但它肯定会给你带来问题。下一步,消除控制台上出现的所有警告。当错误生成的警告没有在其他 50 个警告中丢失时,就更容易发现错误。这些警告确实意味着您做错了什么,所以不要忽略它们。
a.我收到的第一个警告是抱怨 NaN。 NaN 代表“不是数字”,它是除以零时得到的结果。代码中唯一的部分位于
my_progress_func()
中,因此 CURL 有时可能会传入零作为dltotal
参数。如果您检查这一点,就会消除这些警告:b.下一个导致警告的原因是
thread_create()
末尾的 unref 语句。您正在做的是使用 gtk_tree_view_get_model() 来从树视图中获取模型,但这并没有为您提供对该模型的引用。因此,当您取消引用它时,您实际上是在破坏模型,而树视图仍在使用它。删除该语句,所有警告就会神奇地消失。您根本不需要取消引用模型。树视图拥有对其的唯一引用,当树视图被销毁时,它将自动取消引用模型。但这仍然不是问题。现在一切都已解决,您可以确定问题不是由某些无效的指针或线程问题引起的。事实证明这毕竟是一件非常简单的事情:
在这里,您总是会得到一个指向行号 0(即第一行)的迭代器。这就是为什么所有下载进度都显示在第一行的原因。我建议使用 GPrivate 拥有一个带有自己的进度分数的每线程数据结构,以及启动线程时创建的行的 GtkTreeRowReference。获取 CURL 以将该数据结构发送到回调。 不要使用
GtkTreeIter
或GtkTreePath
来存储行。相反,像这样获取你的GtkTreeIter
:祝你好运。
Here are my tips for getting your code to work:
If there's a pkg-config file for a library, like gthread or libcurl, then use that instead of mixing pkg-config calls and
-l
switches. This wasn't the problem, but it might give you problems later on. So compile your file like this:If using threads, always, always, always, wrap your call to
gtk_main()
in betweengdk_threads_enter()
andgdk_threads_leave()
. This wasn't the problem either, but it definitely will give you problems later on.Next step, eliminate all the warnings spewing all over the console. It's easier to spot a mistake when the warning it generates isn't lost in 50 other warnings. And those warnings do mean you're doing something wrong, so don't ignore them.
a. The first warnings I got were complaining about NaNs. NaN stands for "not a number" and it's what you get when you divide by zero. The only division in your code is in
my_progress_func()
so probably CURL sometimes passes in zero as thedltotal
parameter. If you check for that, it eliminates those warnings:b. The next cause of warnings is the unref statement at the end of
thread_create()
. What you are doing is usinggtk_tree_view_get_model()
to get the model from the tree view, but that doesn't give you a reference to the model. So when you unref it, you are actually destroying the model while the tree view is still using it. Delete that statement, and all the warnings magically disappear. You don't need to unref the model at all. The tree view owns the only reference to it, and when the tree view gets destroyed it will unref the model automatically. That still isn't the problem though.Now all that's out of the way, you can be certain the problem isn't being caused by some invalid pointer or thread problem. And it turns out to be something quite simple after all:
Here, you are always getting an iter pointing to row number 0, i.e. the first row. And that's why all the download progress shows up in the first row. I would recommend using
GPrivate
to have a per-thread data structure with its own progress fraction, and aGtkTreeRowReference
to the row you create when starting the thread. Get CURL to send that data structure to the callback. Don't use aGtkTreeIter
orGtkTreePath
to store the row. Instead, get yourGtkTreeIter
like this:Good luck.