2

在我的 C 程序中,从 Internet 下载一些文件,我使用 GTK 进度条显示下载进度。

我想如果我下载一个文件,我的应用程序会显示一个进度条

如果我下载三个文件,我的应用程序可以显示三个进度条。其余的可以以相同的方式完成。

我用 glade3 创建 UI。GtkTreeView 有 3 列

  1. 姓名
  2. 进步
  3. 地位

我写了一些代码,它可以工作但有一些问题

如果我下载一个文件,app lonking 还不错。

但如果我下载两个文件。应用程序无法显示两个进度条。

应用程序只在一个进程中显示两个线程

我该怎么解决呢?

进步.glade

和源代码:

/*
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;
}
4

1 回答 1

2

以下是让您的代码正常工作的提示:

  1. 如果有库的 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
    
  2. 如果使用线程,always,always,always,将调用包装gtk_main()gdk_threads_enter()and之间gdk_threads_leave()。这也不是问题,但以后肯定会给您带来问题。

  3. 下一步,消除所有在控制台上喷出的警告。当它生成的警告没有在其他 50 个警告中丢失时,更容易发现错误。这些警告确实意味着你做错了什么,所以不要忽视它们。

    一种。我收到的第一个警告是抱怨 NaN。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(). 您正在做的是使用gtk_tree_view_get_model()从树视图中获取模型,但这并没有为您提供对模型的引用。因此,当您取消引用它时,实际上是在树视图仍在使用它时破坏了模型。删除该语句,所有警告都会神奇地消失。您根本不需要取消引用模型。树视图拥有对它的唯一引用,当树视图被销毁时,它会自动取消引用模型。但这仍然不是问题。

  4. 现在所有这些都已经解决了,您可以确定问题不是由一些无效的指针或线程问题引起的。事实证明这是一件非常简单的事情:

    gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store) ,
                                         &iter,
                                         g_strdup_printf ("%d",0));
    

    在这里,您总是得到一个指向第 0 行的迭代器,即第一行。这就是为什么所有下载进度都显示在第一行的原因。我建议使用GPrivate具有自己的进度分数的每线程数据结构,以及GtkTreeRowReference在启动线程时创建的行。获取 CURL 以将该数据结构发送到回调。不要使用GtkTreeIterorGtkTreePath来存储行。相反,请GtkTreeIter像这样:

    gtk_tree_model_get_iter(gtk_tree_row_reference_get_model(row_reference), &iter, gtk_tree_row_reference_get_path(row_reference));
    

祝你好运。

于 2009-12-10T22:22:51.473 回答