2

私のCプログラムでは、インターネットからいくつかのファイルをダウンロードし、GTKプログレスバーを使用してダウンロードの進行状況を表示しています。

ファイルを 1 つダウンロードすると、アプリにプログレス バーが 1 つ表示されます

3 つのファイルをダウンロードすると、アプリで 3 つの進行状況バーを表示できます。残りは同じ方法で行うことができます。

私はglade3でUIを作成しています。GtkTreeView には 3 つの列があります

  1. 名前
  2. 進捗
  3. 状態

そして私はいくつかのコードを書きました、それは動作しますが、いくつかの問題があります

1つのファイルをダウンロードすると、アプリのロンキングは悪くありません。

しかし、2つのファイルをダウンロードした場合。アプリは 2 つの進行状況バーを表示できませんでした。

アプリは 1 つの進行状況で 2 つのスレッドを表示するだけです

どうすれば解決できますか?

進捗状況

およびソースコード:

/*
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. gthread や libcurl などのライブラリ用の pkg-config ファイルがある場合は、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. スレッドを使用する場合は、常に、常に、常にgtk_main()、呼び出しをと の間gdk_threads_enter()にラップしますgdk_threads_leave()。これも問題ではありませんでしたが、後で問題が発生することは間違いありません

  3. 次のステップでは、コンソール全体に吐き出されるすべての警告を取り除きます。生成された警告が他の 50 個の警告で失われていない場合、間違いを見つけやすくなります。これらの警告、何か間違ったことをしていることを意味するので、無視しないでください。

    を。私が受け取った最初の警告は、NaN に関する不満でした。NaN は「not a number」の略で、ゼロで割ったときに得られるものです。コードの唯一の区分は in で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;
    }
    

    b. 警告の次の原因は、 の最後の unref ステートメントです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、つまり最初の行を指す iter を取得しています。そのため、すべてのダウンロードの進行状況が最初の行に表示されます。GPrivateスレッドごとのデータ構造に独自の進捗率を設定しGtkTreeRowReference、スレッドの開始時に作成する行に を使用することをお勧めします。CURL を取得して、そのデータ構造をコールバックに送信します。行の保存にorを使用しないでください。代わりに、次のように取得します。GtkTreeIterGtkTreePathGtkTreeIter

    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 に答える