下载文件

时间:2015-10-02 10:07:03

标签: c++ libcurl gtk2

我尝试使用GTK2和curl创建一个简单的文件下载器。

正如您在下面的示例中所看到的,使用单独的线程计算和更新自己的进度,但由于某种原因它无法正常工作。即使进度没有移动,我也可以看到正在下载文件。

任何人都可以查看以下代码,让我知道那里有什么问题吗?

#include <stdio.h>
#include <gtk/gtk.h>
#include <curl/curl.h>

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);
}

GtkWidget *Bar;
int my_progress_func(GtkWidget *bar,
                     double t, /* dltotal */
                     double d, /* dlnow */
                     double ultotal,
                     double ulnow)
{
    printf("progress : %f \n", (d*100.0)/t);
    gdk_threads_enter();
    gtk_progress_set_value(GTK_PROGRESS(bar), (d*100.0)/t);
    gdk_threads_leave();
    return 0;
}

void *my_thread(void *ptr)
{
  CURL * curl;
  FILE *outfile;
  gchar *url = (gchar *) ptr;

  curl = curl_easy_init();
  if(curl)
  {
    CURLcode res;
    outfile = fopen("test.exe", "w");

    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); /// Follow redirections ////
    curl_easy_setopt(curl, CURLOPT_PROXY, "172.16.3.3:3128");
    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, 0L);
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, Bar);

    res = curl_easy_perform(curl);

    if(res != CURLE_OK)
      fprintf(stderr, "%s\n", curl_easy_strerror(res));

    fclose(outfile);
    /* always cleanup */
    curl_easy_cleanup(curl);
  }

  return NULL;
}

/// Window events ////
static void destroy_event(GtkWidget *widget, gpointer data)
{
    gtk_main_quit();
}
static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
    return FALSE; /// must return false to trigger destroy event for window
}
/// ///////////// ////

int main(int argc, char **argv)
{
    GtkWidget *Window, *Frame, *Frame2;
    GtkAdjustment *adj;

    /* Must initialize libcurl before any threads are started */
    curl_global_init(CURL_GLOBAL_ALL);
    /* Init thread */
    g_thread_init(NULL);

    gtk_init(&argc, &argv);
    /// Window ///////
    Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (Window), "Downloader");
    /// Widnow signals /////
    //g_signal_connect(G_OBJECT(Window), "delete_event", G_CALLBACK(delete_event), NULL);
    //g_signal_connect(G_OBJECT(Window), "destroy", G_CALLBACK(destroy_event), NULL);
    /// Widnow signals /////

    Frame = gtk_frame_new(NULL);
    gtk_frame_set_shadow_type(GTK_FRAME(Frame), GTK_SHADOW_OUT);
    gtk_container_add(GTK_CONTAINER(Window), Frame);

    Frame2 = gtk_frame_new(NULL);
    gtk_frame_set_shadow_type(GTK_FRAME(Frame2), GTK_SHADOW_IN);
    gtk_container_add(GTK_CONTAINER(Frame), Frame2);
    gtk_container_set_border_width(GTK_CONTAINER(Frame2), 5);

    adj = (GtkAdjustment*)gtk_adjustment_new(0, 0, 100, 0, 0, 0);
    Bar = gtk_progress_bar_new_with_adjustment(adj);
    gtk_container_add(GTK_CONTAINER(Frame2), Bar);

    gtk_widget_show_all(Window);

    if (!g_thread_create(&my_thread, argv[1], FALSE, NULL) != 0){
        g_warning("can't create the thread");
    }

    gdk_threads_enter();
    gtk_main();
    gdk_threads_leave();
    return 0;
}

1 个答案:

答案 0 :(得分:0)

必须从主线程更新GTK小部件。

我在一段时间后实现了进度条,但是在计时器的帮助下。

在调用gtk_main()之前安装一个计时器,并在计时器回调中更新小部件。

也可以使用线程完成,但需要遵循生产者 - 消费者模型,并且它更复杂。

这是一个很好的链接: [http://www.cc.gatech.edu/data_files/public/doc/gtk/tutorial/gtk_tut-10.html][1]