在我的C程序中,从互联网下载一些文件,我使用GTK进度条显示下载进度。
我想如果我下载一个文件,我的应用程序会显示一个进度条
如果我下载三个文件,我的应用程序可以显示三个进度条。其余的可以用同样的方式完成。
我使用glade3创建UI。 GtkTreeView 有3列
- 名称
- 进度
- 状态
醇>
我写了一些代码,它可以工作但有一些问题
如果我下载了一个文件,应用程序也不错。
但如果我下载两个文件。 应用程序无法显示twoprogress栏。
app只在一个进度中显示两个线程
我该如何解决?
和源代码:
/*
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;
}
=============================================== ==========
更新: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;
}
答案 0 :(得分:2)
以下是让您的代码正常运行的提示:
如果有一个库的pkg-config文件,比如gthread或libcurl,那么使用它而不是混合pkg-config调用和-l
开关。这不是问题,但它可能会在以后给你带来麻烦。所以编译你的文件:
gcc -Wall -g `pkg-config --cflags --libs gtk+-2.0 gmodule-export-2.0 gthread-2.0 libcurl` liststore.c -o liststore
如果始终使用线程,总是,请在gtk_main()
和gdk_threads_enter()
之间将您的来电包裹到gdk_threads_leave()
。这也不是问题,但它肯定将以后给你带来问题。
下一步,消除所有在控制台上喷出的警告。当它产生的警告在其他50个警告中没有丢失时,更容易发现错误。那些警告做意味着你做错了什么,所以不要忽视它们。
一个。我得到的第一个警告是抱怨NaNs。 NaN代表“不是数字”,它是你除以零时得到的。代码中的唯一除法是my_progress_func()
,因此CURL有时可能会将dltotal
参数作为零传递。如果你检查一下,它会消除这些警告:
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;
}
湾警告的下一个原因是thread_create()
末尾的unref语句。你正在做的是使用gtk_tree_view_get_model()
从树视图中获取模型,但这并没有提供对模型的引用。因此,当你取消它时,你实际上是在树视图仍在使用它的同时销毁模型。删除该语句,所有警告神奇地消失。您根本不需要取消模型。树视图拥有对它的唯一引用,当树视图被破坏时,它将自动取消模型。但这仍然不是问题。
现在所有这一切都已经解决了,你可以确定问题不是由一些无效的指针或线程问题引起的。毕竟事实证明这很简单:
gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store) ,
&iter,
g_strdup_printf ("%d",0));
在这里,你总是得到一个指向第0行的iter,即第一行。这就是为什么所有下载进度都出现在第一行的原因。我建议使用GPrivate
使每线程数据结构具有自己的进度分数,并在启动线程时创建一行GtkTreeRowReference
。获取CURL以将该数据结构发送到回调。 不要使用GtkTreeIter
或GtkTreePath
来存储该行。相反,让你的GtkTreeIter
像这样:
gtk_tree_model_get_iter(gtk_tree_row_reference_get_model(row_reference), &iter, gtk_tree_row_reference_get_path(row_reference));