Discussion multiples Progrès barre de contrôle avec GTK
Question
dans mon programme C, télécharger des fichiers depuis Internet, im en utilisant une barre de progression GTK pour montrer la progression du téléchargement.
je veux si je télécharger un fichier, mon application affiche une barre de progression
si je télécharger trois fichiers, mon application peut afficher trois barre de progression. le reste peut se faire de la même manière.
je crée l'interface utilisateur avec glade3. GtkTreeView ont 3 colonnes
- Nom
- Progrès
- État
et je l'ai écrit un code, son peut fonctionner, mais ont un problème
si je télécharger un fichier, l'application Lonking pas mal.
mais si je télécharge deux fichiers. app ne pouvait pas afficher la barre de twoprogress.
app montrent que deux fils dans un cours
Comment dois-je faire le résoudre?
et le code source:
/*
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;
}
=============================================== ==========
Mise à jour: 11/12/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;
}
La solution
Voici mes conseils pour obtenir votre code pour:
-
S'il y a un fichier-config pkg pour une bibliothèque, comme gthread ou libcurl, puis utilisez qu'au lieu de mélanger les appels config pkg et commutateurs
-l
. Ce ne fut pas le problème, mais il peut vous donner des problèmes plus tard. Donc compiler votre fichier comme ceci:gcc -Wall -g `pkg-config --cflags --libs gtk+-2.0 gmodule-export-2.0 gthread-2.0 libcurl` liststore.c -o liststore
-
Si vous utilisez des fils, toujours, toujours, toujours , Enveloppez votre appel à
gtk_main()
entregdk_threads_enter()
etgdk_threads_leave()
. Ce ne fut pas le problème non plus, mais il a certainement vous donner des problèmes plus tard. -
L'étape suivante, éliminer tous les avertissements crachant partout dans la console. Il est plus facile de repérer une erreur lors de l'avertissement qu'il génère ne soit pas perdu dans 50 autres avertissements. Et ces avertissements font dire que vous faites quelque chose de mal, alors ne les ignorez pas.
a. Les premiers avertissements que j'ai obtenu se plaignaient de NaN. NaN signifie « pas un nombre » et c'est ce que vous obtenez lorsque vous diviser par zéro. La seule division dans votre code est en
my_progress_func()
si CURL probablement passe parfois à zéro comme paramètredltotal
. Si vous cochez pour cela, il élimine ces avertissements: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; }
b. La prochaine cause de mises en garde est la déclaration UNREF à la fin de
thread_create()
. Ce que vous faites est à l'aidegtk_tree_view_get_model()
pour obtenir le modèle de l'arborescence, mais cela ne vous donne pas une référence au modèle. Alors, quand vous UNREF, vous êtes en train de détruire en fait le modèle alors que l'arborescence utilise encore. Supprimer cette déclaration, et tous les avertissements disparaissent comme par magie. Vous n'êtes pas obligé de UNREF le modèle du tout. L'arborescence est propriétaire de la seule référence à elle, et quand la vue de l'arbre est détruit il UNREF automatiquement le modèle. C'est toujours pas le problème cependant. -
Maintenant, tout ce qui est hors de la voie, vous pouvez être certain que le problème n'est pas causé par un pointeur non valide ou problème thread. Et il se révèle être, après tout quelque chose de très simple:
gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store) , &iter, g_strdup_printf ("%d",0));
Ici, vous obtenez toujours un pointage iter à ramer numéro 0, à savoir la première ligne. Et voilà pourquoi tous les progrès de téléchargement apparaît dans la première rangée. Je recommanderais d'utiliser
GPrivate
d'avoir un vous créez par thread structure de données avec sa propre fraction de progrès et unGtkTreeRowReference
à la ligne lors du démarrage du fil. Obtenez CURL envoyer cette structure de données à la fonction de rappel. Ne pas utiliser unGtkTreeIter
ouGtkTreePath
pour stocker la ligne. Au lieu de cela, obtenir votreGtkTreeIter
comme ceci:gtk_tree_model_get_iter(gtk_tree_row_reference_get_model(row_reference), &iter, gtk_tree_row_reference_get_path(row_reference));
Bonne chance.